mirror of
https://github.com/alexgo-io/stacks-blockchain-api.git
synced 2026-06-12 07:45:21 +08:00
feat: rosetta data api and construction validation with rosetta-cli
* test: added test for rosetta cli check:construction and check:data * ci: added github workflow to test roseta-cli:data * ci: add github action for rosetta-cli: construction * refactor: refactor cli tests * fix: use localhoust for github actions in rosetta config file * fix: github actions * fix: rebase issue * refactor: update rosetta-cli env files path * refactor: remove hard coded cli container and use rosetta-cli ouput * perf: remove unnecessary wait of 20 seconds
This commit is contained in:
120
.github/workflows/stacks-blockchain-api.yml
vendored
120
.github/workflows/stacks-blockchain-api.yml
vendored
@@ -266,6 +266,126 @@ jobs:
|
||||
uses: codecov/codecov-action@v1
|
||||
if: always()
|
||||
|
||||
test-rosetta-cli-data:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14'
|
||||
check-latest: true
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: |
|
||||
~/.npm
|
||||
node_modules
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Install deps
|
||||
run: npm install --audit=false
|
||||
|
||||
- name: Setup env vars
|
||||
run: echo "STACKS_CORE_EVENT_HOST=http://0.0.0.0">> $GITHUB_ENV
|
||||
|
||||
- name: Setup cli enviroment variable
|
||||
run: |
|
||||
echo STACKS_BLOCKCHAIN_API_HOST=0.0.0.0>> .env
|
||||
echo STACKS_CORE_PROXY_HOST=0.0.0.0 >> .env
|
||||
echo STACKS_CORE_RPC_HOST=0.0.0.0 >> .env
|
||||
echo STACKS_CORE_EVENT_HOST=0.0.0.0 >> .env
|
||||
echo BTC_RPC_HOST=http://0.0.0.0 >> .env
|
||||
|
||||
- name: Setup integration environment
|
||||
run: |
|
||||
sudo ufw disable
|
||||
npm run devenv:deploy -- -d
|
||||
npm run devenv:logs -- --no-color &> docker-compose-logs.txt &
|
||||
|
||||
- name: Run tests
|
||||
run: sudo npm run test:rosetta-cli:data
|
||||
|
||||
- name: Print integration environment logs
|
||||
run: cat docker-compose-logs.txt
|
||||
if: failure()
|
||||
|
||||
- name: Teardown integration environment
|
||||
run: npm run devenv:stop
|
||||
if: always()
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v1
|
||||
if: always()
|
||||
|
||||
test-rosetta-cli-construction:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14'
|
||||
check-latest: true
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
path: |
|
||||
~/.npm
|
||||
node_modules
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Install deps
|
||||
run: npm install --audit=false
|
||||
|
||||
- name: Setup env vars
|
||||
run: echo "STACKS_CORE_EVENT_HOST=http://0.0.0.0">> $GITHUB_ENV
|
||||
|
||||
- name: Setup cli enviroment variable
|
||||
run: |
|
||||
echo STACKS_BLOCKCHAIN_API_HOST=0.0.0.0>> .env
|
||||
echo STACKS_CORE_PROXY_HOST=0.0.0.0 >> .env
|
||||
echo STACKS_CORE_RPC_HOST=0.0.0.0 >> .env
|
||||
echo STACKS_CORE_EVENT_HOST=0.0.0.0 >> .env
|
||||
echo BTC_RPC_HOST=http://0.0.0.0 >> .env
|
||||
|
||||
- name: Setup integration environment
|
||||
run: |
|
||||
sudo ufw disable
|
||||
npm run devenv:deploy -- -d
|
||||
npm run devenv:logs -- --no-color &> docker-compose-logs.txt &
|
||||
|
||||
- name: Run tests
|
||||
run: sudo npm run test:rosetta-cli:construction
|
||||
|
||||
- name: Print integration environment logs
|
||||
run: cat docker-compose-logs.txt
|
||||
if: failure()
|
||||
|
||||
- name: Teardown integration environment
|
||||
run: npm run devenv:stop
|
||||
if: always()
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v1
|
||||
if: always()
|
||||
|
||||
test-tokens:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -126,3 +126,7 @@ yarn.lock
|
||||
|
||||
# Git info output
|
||||
.git-info
|
||||
|
||||
#rosetta-cli results
|
||||
rosetta-output-construction/rosetta-cli-output-const.json
|
||||
rosetta-output/rosetta-cli-output.json
|
||||
|
||||
@@ -4,4 +4,7 @@ services:
|
||||
build:
|
||||
context: ./rosetta-cli-config
|
||||
dockerfile: docker/Dockerfile
|
||||
command: /bin/rosetta-cli --configuration-file /app/rosetta-config-docker.json check:construction
|
||||
command: ${CMD}
|
||||
volumes:
|
||||
- ${OUTPUT}
|
||||
|
||||
16
jest.config.rosetta-cli-construction.js
Normal file
16
jest.config.rosetta-cli-construction.js
Normal file
@@ -0,0 +1,16 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
rootDir: 'src',
|
||||
testMatch: ['<rootDir>/tests-rosetta-cli-construction/**/*.ts'],
|
||||
testPathIgnorePatterns: ['<rootDir>/tests-rosetta-cli-construction/setup.ts', '<rootDir>/tests-rosetta-cli-construction/teardown.ts'],
|
||||
collectCoverageFrom: ['<rootDir>/**/*.ts'],
|
||||
coveragePathIgnorePatterns: ['<rootDir>/tests'],
|
||||
coverageDirectory: '../coverage',
|
||||
globalSetup: '<rootDir>/tests-rosetta-cli-construction/setup.ts',
|
||||
globalTeardown: '<rootDir>/tests-rosetta-cli-construction/teardown.ts',
|
||||
testTimeout: 180000,
|
||||
transformIgnorePatterns: [
|
||||
"node_modules/(?!(@stacks/stacks-transactions)/)"
|
||||
]
|
||||
}
|
||||
16
jest.config.rosetta-cli-data.js
Normal file
16
jest.config.rosetta-cli-data.js
Normal file
@@ -0,0 +1,16 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
rootDir: 'src',
|
||||
testMatch: ['<rootDir>/tests-rosetta-cli-data/**/*.ts'],
|
||||
testPathIgnorePatterns: ['<rootDir>/tests-rosetta-cli-data/setup.ts', '<rootDir>/tests-rosetta-cli-data/teardown.ts'],
|
||||
collectCoverageFrom: ['<rootDir>/**/*.ts'],
|
||||
coveragePathIgnorePatterns: ['<rootDir>/tests'],
|
||||
coverageDirectory: '../coverage',
|
||||
globalSetup: '<rootDir>/tests-rosetta-cli-data/setup.ts',
|
||||
globalTeardown: '<rootDir>/tests-rosetta-cli-data/teardown.ts',
|
||||
testTimeout: 240000,
|
||||
transformIgnorePatterns: [
|
||||
"node_modules/(?!(@stacks/stacks-transactions)/)"
|
||||
]
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
rootDir: 'src',
|
||||
testMatch: ['<rootDir>/tests-rosetta-cli/validate-rosetta-construction.ts'],
|
||||
testPathIgnorePatterns: [
|
||||
'<rootDir>/tests-rosetta-cli/setup.ts',
|
||||
'<rootDir>/tests-rosetta-cli/teardown.ts',
|
||||
],
|
||||
collectCoverageFrom: ['<rootDir>/**/*.ts'],
|
||||
coveragePathIgnorePatterns: ['<rootDir>/tests'],
|
||||
coverageDirectory: '../coverage',
|
||||
globalSetup: '<rootDir>/tests-rosetta-cli/setup.ts',
|
||||
globalTeardown: '<rootDir>/tests-rosetta-cli/teardown.ts',
|
||||
testTimeout: 180000,
|
||||
};
|
||||
122
package-lock.json
generated
122
package-lock.json
generated
@@ -3832,6 +3832,16 @@
|
||||
"wif": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"bl": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
||||
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
|
||||
"requires": {
|
||||
"buffer": "^5.5.0",
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.4.0"
|
||||
}
|
||||
},
|
||||
"bluebird": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||
@@ -4317,6 +4327,11 @@
|
||||
"readdirp": "~3.5.0"
|
||||
}
|
||||
},
|
||||
"chownr": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
||||
},
|
||||
"ci-info": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
|
||||
@@ -5080,6 +5095,15 @@
|
||||
"yaml": "^1.7.2"
|
||||
}
|
||||
},
|
||||
"cpu-features": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.2.tgz",
|
||||
"integrity": "sha512-/2yieBqvMcRj8McNzkycjW2v3OIUOibBfd2dLEJ0nWts8NobAxwiyw9phVNS6oDL8x8tz9F7uNVFEVpJncQpeA==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"nan": "^2.14.1"
|
||||
}
|
||||
},
|
||||
"create-ecdh": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz",
|
||||
@@ -5513,6 +5537,41 @@
|
||||
"yaml": "^1.10.2"
|
||||
}
|
||||
},
|
||||
"docker-modem": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-3.0.3.tgz",
|
||||
"integrity": "sha512-Tgkn2a+yiNP9FoZgMa/D9Wk+D2Db///0KOyKSYZRJa8w4+DzKyzQMkczKSdR/adQ0x46BOpeNkoyEOKjPhCzjw==",
|
||||
"requires": {
|
||||
"debug": "^4.1.1",
|
||||
"readable-stream": "^3.5.0",
|
||||
"split-ca": "^1.0.1",
|
||||
"ssh2": "^1.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
|
||||
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"dockerode": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-3.3.1.tgz",
|
||||
"integrity": "sha512-AS2mr8Lp122aa5n6d99HkuTNdRV1wkkhHwBdcnY6V0+28D3DSYwhxAk85/mM9XwD3RMliTxyr63iuvn5ZblFYQ==",
|
||||
"requires": {
|
||||
"docker-modem": "^3.0.0",
|
||||
"tar-fs": "~2.0.1"
|
||||
}
|
||||
},
|
||||
"doctrine": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
|
||||
@@ -5714,7 +5773,6 @@
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
@@ -7277,6 +7335,11 @@
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
|
||||
},
|
||||
"fs-constants": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
|
||||
@@ -12896,6 +12959,11 @@
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
|
||||
},
|
||||
"mkdirp-classic": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
||||
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
|
||||
},
|
||||
"mobx": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/mobx/-/mobx-6.3.0.tgz",
|
||||
@@ -13685,7 +13753,6 @@
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@@ -14525,7 +14592,6 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"end-of-stream": "^1.1.0",
|
||||
"once": "^1.3.1"
|
||||
@@ -16135,6 +16201,11 @@
|
||||
"integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==",
|
||||
"dev": true
|
||||
},
|
||||
"split-ca": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz",
|
||||
"integrity": "sha1-bIOv82kvphJW4M0ZfgXp3hV2kaY="
|
||||
},
|
||||
"split-string": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
|
||||
@@ -16157,6 +16228,25 @@
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
||||
},
|
||||
"ssh2": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.4.0.tgz",
|
||||
"integrity": "sha512-XvXwcXKvS452DyQvCa6Ct+chpucwc/UyxgliYz+rWXJ3jDHdtBb9xgmxJdMmnIn5bpgGAEV3KaEsH98ZGPHqwg==",
|
||||
"requires": {
|
||||
"asn1": "^0.2.4",
|
||||
"bcrypt-pbkdf": "^1.0.2",
|
||||
"cpu-features": "0.0.2",
|
||||
"nan": "^2.15.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"nan": {
|
||||
"version": "2.15.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
|
||||
"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==",
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
|
||||
@@ -16623,6 +16713,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"tar-fs": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz",
|
||||
"integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==",
|
||||
"requires": {
|
||||
"chownr": "^1.1.1",
|
||||
"mkdirp-classic": "^0.5.2",
|
||||
"pump": "^3.0.0",
|
||||
"tar-stream": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"tar-stream": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
|
||||
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
|
||||
"requires": {
|
||||
"bl": "^4.0.3",
|
||||
"end-of-stream": "^1.4.1",
|
||||
"fs-constants": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"tdigest": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz",
|
||||
@@ -17977,8 +18090,7 @@
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"dev": true
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
},
|
||||
"write-file-atomic": {
|
||||
"version": "3.0.3",
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
"dev:follower": "npm run devenv:build && concurrently npm:dev npm:devenv:follower",
|
||||
"test": "cross-env NODE_ENV=development jest --config ./jest.config.js --coverage --runInBand",
|
||||
"test:rosetta": "cross-env NODE_ENV=development jest --config ./jest.config.rosetta.js --coverage --runInBand",
|
||||
"test:rosetta-cli:data": "cross-env NODE_ENV=development STACKS_CHAIN_ID=0x80000000 jest --config ./jest.config.rosetta-cli-data.js --runInBand",
|
||||
"test:rosetta-cli:construction": "cross-env NODE_ENV=development STACKS_CHAIN_ID=0x80000000 jest --config ./jest.config.rosetta-cli-construction.js --runInBand",
|
||||
"test:bns": "cross-env NODE_ENV=development jest --config ./jest.config.bns.js --coverage --runInBand",
|
||||
"test:microblocks": "cross-env NODE_ENV=development jest --config ./jest.config.microblocks.js --coverage --runInBand",
|
||||
"test:tokens": "cross-env NODE_ENV=development jest --config ./jest.config.tokens.js --coverage --runInBand",
|
||||
@@ -28,7 +30,7 @@
|
||||
"lint:prettier": "prettier --check src/**/*.{ts,json}",
|
||||
"lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx -f codeframe --fix && prettier --write --check src/**/*.{ts,json}",
|
||||
"migrate": "node-pg-migrate -m src/migrations",
|
||||
"devenv:build": "docker-compose -f docker-compose.dev.postgres.yml -f docker-compose.dev.stacks-blockchain.yml -f docker-compose.dev.bitcoind.yml build --no-cache",
|
||||
"devenv:build": "docker-compose -f docker-compose.dev.postgres.yml -f docker-compose.dev.stacks-blockchain.yml -f docker-compose.dev.bitcoind.yml -f docker-compose.dev.rosetta-cli.yml build --no-cache",
|
||||
"devenv:deploy": "docker-compose -f docker-compose.dev.postgres.yml -f docker-compose.dev.stacks-blockchain.yml -f docker-compose.dev.bitcoind.yml up",
|
||||
"devenv:deploy:pg": "docker-compose -f docker-compose.dev.postgres.yml up",
|
||||
"devenv:follower": "docker-compose -f docker-compose.dev.postgres.yml -f docker-compose.dev.stacks-blockchain-follower.yml up",
|
||||
@@ -121,6 +123,7 @@
|
||||
"compression": "^1.7.4",
|
||||
"cors": "^2.8.5",
|
||||
"cross-env": "^7.0.2",
|
||||
"dockerode": "^3.3.1",
|
||||
"dotenv": "^8.2.0",
|
||||
"dotenv-flow": "^3.2.0",
|
||||
"escape-goat": "^3.0.0",
|
||||
|
||||
28
rosetta-cli-config/docker/Dockerfile
Normal file
28
rosetta-cli-config/docker/Dockerfile
Normal file
@@ -0,0 +1,28 @@
|
||||
FROM everpeace/curl-jq as build
|
||||
|
||||
RUN mkdir -p /bin
|
||||
|
||||
ENV ROSETTA_CLI_VERSION "0.5.10"
|
||||
|
||||
RUN curl -L --output rosetta-cli-${ROSETTA_CLI_VERSION}-linux-amd64.tar.gz \
|
||||
https://github.com/coinbase/rosetta-cli/releases/download/v${ROSETTA_CLI_VERSION}/rosetta-cli-${ROSETTA_CLI_VERSION}-linux-amd64.tar.gz \
|
||||
&& tar xzf rosetta-cli-${ROSETTA_CLI_VERSION}-linux-amd64.tar.gz \
|
||||
&& mv rosetta-cli-${ROSETTA_CLI_VERSION}-linux-amd64 /bin/rosetta-cli \
|
||||
&& chmod +x /bin/rosetta-cli
|
||||
|
||||
FROM debian:stretch
|
||||
|
||||
RUN mkdir -p /bin /app
|
||||
|
||||
COPY --from=build /bin/rosetta-cli /bin/
|
||||
|
||||
COPY docker/docker-entrypoint.sh /bin
|
||||
RUN chmod +x /bin/docker-entrypoint.sh
|
||||
|
||||
COPY . /app
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ENTRYPOINT ["/bin/docker-entrypoint.sh"]
|
||||
|
||||
# CMD ["/bin/rosetta-cli", "--configuration-file /app/rosetta-config-docker.json", "view:networks"]
|
||||
15
rosetta-cli-config/docker/docker-entrypoint.sh
Normal file
15
rosetta-cli-config/docker/docker-entrypoint.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
# Use this script to enable host.docker.internal on Docker for linux.
|
||||
# See https://github.com/bufferings/docker-access-host
|
||||
|
||||
HOST_DOMAIN="host.docker.internal"
|
||||
ping -q -c1 $HOST_DOMAIN >/dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
HOST_IP=$(ip route | awk 'NR==1 {print $3}')
|
||||
echo -e "$HOST_IP\t$HOST_DOMAIN" >>/etc/hosts
|
||||
fi
|
||||
|
||||
cat /etc/hosts
|
||||
echo "patched host.docker.internal"
|
||||
|
||||
exec "$@"
|
||||
@@ -34,5 +34,32 @@
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "STF9B75ADQAVXQHNEQ6KGHXTG7JP305J2GRWF3A2"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "ST18MDW2PDTBSCR1ACXYRJP2JX70FWNM6YY2VX4SS"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "ST000000000000000000002AMW42H"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
191
rosetta-cli-config/interesting_accounts.json
Normal file
191
rosetta-cli-config/interesting_accounts.json
Normal file
@@ -0,0 +1,191 @@
|
||||
[
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "ST1DTAEAKM02GKCT4NGKTVER8MTJJHYQ9NT27E677"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "ST3KTZ45AQES4PNNHB2YGGJ4JXQQMRACRNZPQ19SP"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "ST17ZNMSQMDARSCZ85Z7BVJX6T20ZWDF3VX0ZP33K"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "ST3DR3THSKSWRH10SEDFFP3D90KZYG57J5N8M9KR9"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "ST3WHD5WEKHENA38T9BWZK9M0SDXA46T9DQT0ZDTY"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "ST2WZKZVZRJJMT2RJPWWM9FVC20S9R30CNVMJEX0H"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "STFHWET10QDAQ8B88WNAVEV15XVNWNNCTMQGD5KN"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "ST3YFNTA33A14NYTE0P85790E2X7Y890SD24K8WAK"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "ST367HN911AFQ666829M2C6XP0HPMSTT517B5FMNB"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "STN5W5SAFHVWS9P6BDMVJQZWQGCAHQBPXQYKKCCW"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "ST5WDT0C4995DWA3H7YDKYM0NK4QNADR4T93HYD1"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "ST1JBBKAPX6XDGR9TEGQTNVV2H4NJX456FKRJ5QWA"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "ST3T6ERPJ2TXPQ668747Z52P4EJBYY966SVFM7W8X"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "ST2Y60MJ5CPGEN1CP405H7X8CPCEYV2FJ0PHZ742P"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "STAXJZ26VDBNA5TVJRJ63SV77C1AWW786X84V634"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "ST3Q362NAVV3W6T1GV0XH78JJJ0BKZREHR6C168TV"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "ST16EDNWEW80AX2737KA58SJZQ8QNW7DXKTDBBVCV"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "ST3VHZ1ETEB0D7V27TCB5BB9YVQVWPQJHJG5Y6ARH"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "ST2NC3GSVB511TPCBDKB4PRA6VJHYNSGH93756H1M"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "ST11QXGG0SRPC5QYESX3WA55BKQWWMPV7CSWKG6SN"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"account_identifier": {
|
||||
"address": "STJ5PW80NKC6NJ0J87YWXYNGE474RN38K2B4YW1R"
|
||||
},
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -6,9 +6,7 @@
|
||||
"online_url": "http://host.docker.internal:3999/rosetta/v1",
|
||||
"data_directory": "",
|
||||
"http_timeout": 10,
|
||||
"sync_concurrency": 8,
|
||||
"transaction_concurrency": 16,
|
||||
"tip_delay": 5,
|
||||
"tip_delay": 500,
|
||||
"data": {
|
||||
"active_reconciliation_concurrency": 16,
|
||||
"inactive_reconciliation_concurrency": 4,
|
||||
@@ -18,80 +16,70 @@
|
||||
"log_balance_changes": true,
|
||||
"log_reconciliations": false,
|
||||
"ignore_reconciliation_error": false,
|
||||
"exempt_accounts": "/app/exempt_accounts.json",
|
||||
"bootstrap_balances": "/app/bootstrap_balances.json",
|
||||
"interesting_accounts": "/app/interesting_accounts.json",
|
||||
"historical_balance_disabled": true,
|
||||
"exempt_accounts": "exempt_accounts.json",
|
||||
"bootstrap_balances": "bootstrap_balances.json",
|
||||
"historical_balance_enabled": true,
|
||||
"reconciliation_disabled": false,
|
||||
"inactive_discrepency_search_disabled": false,
|
||||
"balance_tracking_disabled": false,
|
||||
"end_conditions": {
|
||||
"reconciliation_coverage": 0.95,
|
||||
"index": 100
|
||||
"reconciliation_coverage": {
|
||||
"coverage": 0.80,
|
||||
"index": 100
|
||||
}
|
||||
},
|
||||
"results_output_file": "./rosetta-output/rosetta-cli-output.json"
|
||||
},
|
||||
"construction": {
|
||||
"offline_url": "http://host.docker.internal:3999/rosetta/v1",
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 18
|
||||
},
|
||||
"minimum_balance": "0",
|
||||
"maximum_fee": "10000000000000000",
|
||||
"curve_type": "secp256k1",
|
||||
"accounting_model": "account",
|
||||
"scenario": [
|
||||
"max_offline_connections": 0,
|
||||
"stale_depth": 0,
|
||||
"broadcast_limit": 0,
|
||||
"ignore_broadcast_failures": false,
|
||||
"clear_broadcasts": false,
|
||||
"broadcast_behind_tip": false,
|
||||
"block_broadcast_limit": 0,
|
||||
"rebroadcast_all": false,
|
||||
"constructor_dsl_file": "stacks.ros",
|
||||
"prefunded_accounts": [
|
||||
{
|
||||
"operation_identifier": {
|
||||
"index": 0
|
||||
"privkey": "21d43d2ae0da1d9d04cfcaac7d397a33733881081f0b2cd038062cf0ccbb7526",
|
||||
"account_identifier": {
|
||||
"address": "ST11NJTTKGVT6D1HY4NJRVQWMQM7TVAR091EJ8P2Y"
|
||||
},
|
||||
"type": "NATIVE_TRANSFER",
|
||||
"status": "",
|
||||
"account": {
|
||||
"address": "{{ SENDER }}"
|
||||
},
|
||||
"amount": {
|
||||
"value": "{{ SENDER_VALUE }}",
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
"curve_type": "secp256k1",
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"operation_identifier": {
|
||||
"index": 1
|
||||
"privkey": "c71700b07d520a8c9731e4d0f095aa6efb91e16e25fb27ce2b72e7b698f8127a",
|
||||
"account_identifier": {
|
||||
"address": "ST1HB1T8WRNBYB0Y3T7WXZS38NKKPTBR3EG9EPJKR"
|
||||
},
|
||||
"related_operations": [
|
||||
{
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
"type": "NATIVE_TRANSFER",
|
||||
"status": "",
|
||||
"account": {
|
||||
"address": "{{ RECIPIENT }}"
|
||||
"curve_type": "secp256k1",
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"privkey": "e75dcb66f84287eaf347955e94fa04337298dbd95aa0dbb985771104ef1913db",
|
||||
"account_identifier": {
|
||||
"address": "STRYYQQ9M8KAF4NS7WNZQYY59X93XEKR31JP64CP"
|
||||
},
|
||||
"amount": {
|
||||
"value": "{{ RECIPIENT_VALUE }}",
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
"curve_type": "secp256k1",
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
}
|
||||
],
|
||||
"confirmation_depth": 5,
|
||||
"stale_depth": 30,
|
||||
"broadcast_limit": 3,
|
||||
"ignore_broadcast_failures": false,
|
||||
"change_scenario": null,
|
||||
"clear_broadcasts": false,
|
||||
"broadcast_behind_tip": false,
|
||||
"block_broadcast_limit": 5,
|
||||
"rebroadcast_all": false,
|
||||
"new_account_probability": 0.5,
|
||||
"max_addresses": 200
|
||||
"end_conditions": {
|
||||
"create_account": 10,
|
||||
"transfer": 10
|
||||
},
|
||||
"results_output_file": "./rosetta-output/rosetta-cli-output-const.json"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,13 +5,32 @@
|
||||
},
|
||||
"online_url": "http://localhost:3999/rosetta/v1",
|
||||
"data_directory": "",
|
||||
"http_timeout": 300,
|
||||
"max_retries": 5,
|
||||
"retry_elapsed_time": 0,
|
||||
"max_online_connections": 0,
|
||||
"max_sync_concurrency": 0,
|
||||
"tip_delay": 60,
|
||||
"log_configuration": false,
|
||||
"http_timeout": 10,
|
||||
"tip_delay": 300,
|
||||
"data": {
|
||||
"active_reconciliation_concurrency": 16,
|
||||
"inactive_reconciliation_concurrency": 4,
|
||||
"inactive_reconciliation_frequency": 250,
|
||||
"log_blocks": true,
|
||||
"log_transactions": true,
|
||||
"log_balance_changes": true,
|
||||
"log_reconciliations": false,
|
||||
"ignore_reconciliation_error": false,
|
||||
"exempt_accounts": "rosetta-cli-config/exempt_accounts.json",
|
||||
"interesting_accounts": "interesting_accounts.json",
|
||||
"historical_balance_enabled": true,
|
||||
"reconciliation_disabled": false,
|
||||
"inactive_discrepency_search_disabled": false,
|
||||
"balance_tracking_disabled": false,
|
||||
"end_conditions": {
|
||||
"reconciliation_coverage": {
|
||||
"coverage": 0.70,
|
||||
"from_tip": true,
|
||||
"index": 100
|
||||
}
|
||||
},
|
||||
"results_output_file": "./rosetta-output/rosetta-cli-output.json"
|
||||
},
|
||||
"construction": {
|
||||
"offline_url": "http://localhost:3999/rosetta/v1",
|
||||
"max_offline_connections": 0,
|
||||
@@ -23,8 +42,8 @@
|
||||
"block_broadcast_limit": 0,
|
||||
"rebroadcast_all": false,
|
||||
"constructor_dsl_file": "stacks.ros",
|
||||
"prefunded_accounts": [
|
||||
{"privkey": "21d43d2ae0da1d9d04cfcaac7d397a33733881081f0b2cd038062cf0ccbb7526",
|
||||
"prefunded_accounts": [
|
||||
{"privkey": "21d43d2ae0da1d9d04cfcaac7d397a33733881081f0b2cd038062cf0ccbb7526",
|
||||
"account_identifier": {"address": "ST11NJTTKGVT6D1HY4NJRVQWMQM7TVAR091EJ8P2Y"},
|
||||
"curve_type": "secp256k1",
|
||||
"currency": {"symbol": "STX", "decimals": 6}},
|
||||
@@ -39,6 +58,7 @@
|
||||
"end_conditions": {
|
||||
"create_account": 10,
|
||||
"transfer": 10
|
||||
}
|
||||
},
|
||||
"results_output_file": "./rosetta-output/rosetta-cli-output-const.json"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
"online_url": "http://localhost:3999/rosetta/v1",
|
||||
"data_directory": "",
|
||||
"http_timeout": 10,
|
||||
"max_sync_concurrency": 8,
|
||||
"sync_concurrency": 8,
|
||||
"transaction_concurrency": 16,
|
||||
"tip_delay": 300,
|
||||
"data": {
|
||||
"active_reconciliation_concurrency": 16,
|
||||
@@ -18,13 +19,77 @@
|
||||
"log_reconciliations": false,
|
||||
"ignore_reconciliation_error": false,
|
||||
"exempt_accounts": "rosetta-cli-config/exempt_accounts.json",
|
||||
"bootstrap_balances": "bootstrap_balances.json",
|
||||
"bootstrap_balances": "rosetta-cli-config/bootstrap_balances.json",
|
||||
"historical_balance_disabled": true,
|
||||
"interesting_accounts": "",
|
||||
"reconciliation_disabled": true,
|
||||
"reconciliation_disabled": false,
|
||||
"inactive_discrepency_search_disabled": false,
|
||||
"balance_tracking_disabled": false,
|
||||
"end_conditions": {
|
||||
"tip": true
|
||||
"reconciliation_coverage": 0.4
|
||||
}
|
||||
},
|
||||
"construction": {
|
||||
"offline_url": "http://localhost:3999/rosetta/v1",
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
},
|
||||
"minimum_balance": "0",
|
||||
"maximum_fee": "10000000000000000",
|
||||
"curve_type": "secp256k1",
|
||||
"accounting_model": "account",
|
||||
"scenario": [
|
||||
{
|
||||
"operation_identifier": {
|
||||
"index": 0
|
||||
},
|
||||
"type": "NATIVE_TRANSFER",
|
||||
"status": "",
|
||||
"account": {
|
||||
"address": "{{ SENDER }}"
|
||||
},
|
||||
"amount": {
|
||||
"value": "{{ SENDER_VALUE }}",
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"operation_identifier": {
|
||||
"index": 1
|
||||
},
|
||||
"related_operations": [
|
||||
{
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
"type": "NATIVE_TRANSFER",
|
||||
"status": "",
|
||||
"account": {
|
||||
"address": "{{ RECIPIENT }}"
|
||||
},
|
||||
"amount": {
|
||||
"value": "{{ RECIPIENT_VALUE }}",
|
||||
"currency": {
|
||||
"symbol": "STX",
|
||||
"decimals": 6
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"confirmation_depth": 5,
|
||||
"stale_depth": 30,
|
||||
"broadcast_limit": 3,
|
||||
"ignore_broadcast_failures": false,
|
||||
"change_scenario": null,
|
||||
"clear_broadcasts": false,
|
||||
"broadcast_behind_tip": false,
|
||||
"block_broadcast_limit": 5,
|
||||
"rebroadcast_all": false,
|
||||
"new_account_probability": 0.5,
|
||||
"max_addresses": 200
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ function makeFeeOperation(tx: BaseTx): RosettaOperation {
|
||||
const fee: RosettaOperation = {
|
||||
operation_identifier: { index: 0 },
|
||||
type: RosettaOperationType.Fee,
|
||||
status: getTxStatus(DbTxStatus.Success),
|
||||
status: getTxStatus(tx.status),
|
||||
account: { address: tx.sender_address },
|
||||
amount: {
|
||||
value: (0n - unwrapOptional(tx.fee_rate, () => 'Unexpected nullish amount')).toString(10),
|
||||
@@ -591,9 +591,9 @@ function parseStackingContractCall(
|
||||
function parseGenericContractCall(operation: RosettaOperation, tx: BaseTx) {
|
||||
operation.metadata = {
|
||||
contract_call_function_name: tx.contract_call_function_name,
|
||||
contract_call_function_args: bufferToHexPrefixString(
|
||||
tx.contract_call_function_args ? tx.contract_call_function_args : Buffer.from('')
|
||||
),
|
||||
contract_call_function_args: tx.contract_call_function_args
|
||||
? bufferToHexPrefixString(unwrapOptional(tx.contract_call_function_args, () => ''))
|
||||
: '',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
2
src/tests-rosetta-cli-construction/envs/env.construction
Normal file
2
src/tests-rosetta-cli-construction/envs/env.construction
Normal file
@@ -0,0 +1,2 @@
|
||||
CMD=/bin/rosetta-cli --configuration-file /app/rosetta-config-docker.json check:construction
|
||||
OUTPUT=./rosetta-output-construction:/app/rosetta-output
|
||||
23
src/tests-rosetta-cli-construction/setup.ts
Normal file
23
src/tests-rosetta-cli-construction/setup.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { loadDotEnv } from '../helpers';
|
||||
import { StacksCoreRpcClient } from '../core-rpc/client';
|
||||
import { PgDataStore } from '../datastore/postgres-store';
|
||||
|
||||
export interface GlobalServices {
|
||||
db: PgDataStore;
|
||||
}
|
||||
|
||||
export default async (): Promise<void> => {
|
||||
console.log('Jest - setup..');
|
||||
if (!process.env.NODE_ENV) {
|
||||
process.env.NODE_ENV = 'test';
|
||||
}
|
||||
loadDotEnv();
|
||||
const db = await PgDataStore.connect(true);
|
||||
console.log('Waiting for RPC connection to core node..');
|
||||
await new StacksCoreRpcClient().waitForConnection(60000);
|
||||
const globalServices: GlobalServices = {
|
||||
db: db,
|
||||
};
|
||||
Object.assign(global, globalServices);
|
||||
console.log('Jest - setup done');
|
||||
};
|
||||
8
src/tests-rosetta-cli-construction/teardown.ts
Normal file
8
src/tests-rosetta-cli-construction/teardown.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import type { GlobalServices } from './setup';
|
||||
|
||||
export default async (): Promise<void> => {
|
||||
console.log('Jest - teardown..');
|
||||
const globalServices = (global as unknown) as GlobalServices;
|
||||
await globalServices.db.close();
|
||||
console.log('Jest - teardown done');
|
||||
};
|
||||
185
src/tests-rosetta-cli-construction/validate-construction.ts
Normal file
185
src/tests-rosetta-cli-construction/validate-construction.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
import { PgDataStore, cycleMigrations, runMigrations } from '../datastore/postgres-store';
|
||||
import { PoolClient } from 'pg';
|
||||
import { ApiServer, startApiServer } from '../api/init';
|
||||
import { startEventServer } from '../event-stream/event-server';
|
||||
import { Server } from 'net';
|
||||
import { DbBlock, DbMempoolTx, DbTx, DbTxStatus } from '../datastore/common';
|
||||
import { AnchorMode, ChainID, makeSTXTokenTransfer } from '@stacks/transactions';
|
||||
import { StacksTestnet } from '@stacks/network';
|
||||
import * as BN from 'bn.js';
|
||||
import * as fs from 'fs';
|
||||
import { StacksCoreRpcClient, getCoreNodeEndpoint } from '../core-rpc/client';
|
||||
import * as compose from 'docker-compose';
|
||||
import * as path from 'path';
|
||||
import Docker = require('dockerode');
|
||||
|
||||
const docker = new Docker();
|
||||
|
||||
const sender1 = {
|
||||
address: 'STB44HYPYAT2BB2QE513NSP81HTMYWBJP02HPGK6',
|
||||
privateKey: 'cb3df38053d132895220b9ce471f6b676db5b9bf0b4adefb55f2118ece2478df01',
|
||||
};
|
||||
|
||||
const recipientAdd1 = 'ST11NJTTKGVT6D1HY4NJRVQWMQM7TVAR091EJ8P2Y';
|
||||
|
||||
const HOST = 'localhost';
|
||||
const PORT = 20443;
|
||||
const stacksNetwork = GetStacksTestnetNetwork();
|
||||
|
||||
const isContainerRunning = async (name: string): Promise<boolean> =>
|
||||
new Promise((resolve, reject): void => {
|
||||
docker.listContainers((err: any, containers: any): void => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
const running = (containers || []).filter((container: any): boolean =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
container.Names.includes(name)
|
||||
);
|
||||
|
||||
resolve(running.length > 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Rosetta API', () => {
|
||||
let db: PgDataStore;
|
||||
let client: PoolClient;
|
||||
let eventServer: Server;
|
||||
let api: ApiServer;
|
||||
let rosettaOutput: any;
|
||||
|
||||
beforeAll(async () => {
|
||||
process.env.PG_DATABASE = 'postgres';
|
||||
await cycleMigrations();
|
||||
db = await PgDataStore.connect();
|
||||
client = await db.pool.connect();
|
||||
eventServer = await startEventServer({ datastore: db, chainId: ChainID.Testnet });
|
||||
api = await startApiServer({ datastore: db, chainId: ChainID.Testnet });
|
||||
|
||||
// remove previous outputs if any
|
||||
fs.rmdirSync('rosetta-output-construction', { recursive: true });
|
||||
|
||||
// build rosetta-cli container
|
||||
await compose.buildOne('rosetta-cli', {
|
||||
cwd: path.join(__dirname, '../../'),
|
||||
log: true,
|
||||
composeOptions: [
|
||||
'-f',
|
||||
'docker-compose.dev.rosetta-cli.yml',
|
||||
'--env-file',
|
||||
'src/tests-rosetta-cli-construction/envs/env.construction',
|
||||
],
|
||||
});
|
||||
// start cli container
|
||||
void compose.upOne('rosetta-cli', {
|
||||
cwd: path.join(__dirname, '../../'),
|
||||
log: true,
|
||||
composeOptions: [
|
||||
'-f',
|
||||
'docker-compose.dev.rosetta-cli.yml',
|
||||
'--env-file',
|
||||
'src/tests-rosetta-cli-construction/envs/env.construction',
|
||||
],
|
||||
commandOptions: ['--abort-on-container-exit'],
|
||||
});
|
||||
|
||||
await waitForBlock(api);
|
||||
// await sleep(10000);
|
||||
await transferStx(recipientAdd1, 1000000000, sender1.privateKey, api);
|
||||
await transferStx(recipientAdd1, 1000000000, sender1.privateKey, api);
|
||||
await transferStx(recipientAdd1, 1000000000, sender1.privateKey, api);
|
||||
await transferStx(recipientAdd1, 1000000000, sender1.privateKey, api);
|
||||
await transferStx(recipientAdd1, 1000000000, sender1.privateKey, api);
|
||||
|
||||
//wait on rosetta-cli to finish output
|
||||
while (!rosettaOutput) {
|
||||
if (fs.existsSync('rosetta-output-construction/rosetta-cli-output-const.json'))
|
||||
rosettaOutput = require('../../rosetta-output-construction/rosetta-cli-output-const.json');
|
||||
await sleep(1000);
|
||||
}
|
||||
});
|
||||
|
||||
it('check transaction confirmed', () => {
|
||||
expect(rosettaOutput.stats.transactions_confirmed).toBeGreaterThan(1);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await new Promise(resolve => eventServer.close(() => resolve(true)));
|
||||
await api.terminate();
|
||||
client.release();
|
||||
await db?.close();
|
||||
await runMigrations(undefined, 'down');
|
||||
});
|
||||
});
|
||||
|
||||
async function transferStx(
|
||||
recipientAddr: string,
|
||||
amount: number,
|
||||
senderPk: string,
|
||||
api: ApiServer
|
||||
) {
|
||||
await waitForBlock(api);
|
||||
const transferTx = await makeSTXTokenTransfer({
|
||||
recipient: recipientAddr,
|
||||
amount: new BN(amount),
|
||||
senderKey: senderPk,
|
||||
network: stacksNetwork,
|
||||
memo: 'test-transaction',
|
||||
sponsored: false,
|
||||
anchorMode: AnchorMode.Any,
|
||||
});
|
||||
const serialized: Buffer = transferTx.serialize();
|
||||
|
||||
const { txId } = await sendCoreTx(serialized, api, 'transfer-stx');
|
||||
await standByForTx(txId, api);
|
||||
|
||||
return txId;
|
||||
}
|
||||
|
||||
function standByForTx(expectedTxId: string, api: ApiServer): Promise<string> {
|
||||
const broadcastTx = new Promise<string>(resolve => {
|
||||
const listener: (info: string) => void = info => {
|
||||
api.datastore.removeListener('txUpdate', listener);
|
||||
resolve(info);
|
||||
};
|
||||
api.datastore.addListener('txUpdate', listener);
|
||||
});
|
||||
|
||||
return broadcastTx;
|
||||
}
|
||||
|
||||
async function sendCoreTx(
|
||||
serializedTx: Buffer,
|
||||
api: ApiServer,
|
||||
type: string
|
||||
): Promise<{ txId: string }> {
|
||||
try {
|
||||
const submitResult = await new StacksCoreRpcClient({
|
||||
host: HOST,
|
||||
port: PORT,
|
||||
}).sendTransaction(serializedTx);
|
||||
return submitResult;
|
||||
} catch (error) {
|
||||
console.log(type);
|
||||
console.error(error);
|
||||
}
|
||||
return Promise.resolve({ txId: '' });
|
||||
}
|
||||
|
||||
export function GetStacksTestnetNetwork() {
|
||||
const stacksNetwork = new StacksTestnet();
|
||||
stacksNetwork.coreApiUrl = getCoreNodeEndpoint({
|
||||
host: `http://${HOST}`,
|
||||
port: PORT,
|
||||
});
|
||||
return stacksNetwork;
|
||||
}
|
||||
|
||||
async function waitForBlock(api: ApiServer) {
|
||||
await new Promise<string>(resolve => api.datastore.once('blockUpdate', block => resolve(block)));
|
||||
}
|
||||
|
||||
function sleep(ms: number) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
5
src/tests-rosetta-cli-data/contracts/hello-world.clar
Normal file
5
src/tests-rosetta-cli-data/contracts/hello-world.clar
Normal file
@@ -0,0 +1,5 @@
|
||||
(define-public (say-hi)
|
||||
(ok "hello world"))
|
||||
|
||||
(define-public (echo-number (val int))
|
||||
(ok val))
|
||||
2
src/tests-rosetta-cli-data/envs/env.data
Normal file
2
src/tests-rosetta-cli-data/envs/env.data
Normal file
@@ -0,0 +1,2 @@
|
||||
CMD=/bin/rosetta-cli --configuration-file /app/rosetta-config-docker.json check:data
|
||||
OUTPUT=./rosetta-output:/app/rosetta-output
|
||||
23
src/tests-rosetta-cli-data/setup.ts
Normal file
23
src/tests-rosetta-cli-data/setup.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { loadDotEnv } from '../helpers';
|
||||
import { StacksCoreRpcClient } from '../core-rpc/client';
|
||||
import { PgDataStore } from '../datastore/postgres-store';
|
||||
|
||||
export interface GlobalServices {
|
||||
db: PgDataStore;
|
||||
}
|
||||
|
||||
export default async (): Promise<void> => {
|
||||
console.log('Jest - setup..');
|
||||
if (!process.env.NODE_ENV) {
|
||||
process.env.NODE_ENV = 'test';
|
||||
}
|
||||
loadDotEnv();
|
||||
const db = await PgDataStore.connect(true);
|
||||
console.log('Waiting for RPC connection to core node..');
|
||||
await new StacksCoreRpcClient().waitForConnection(60000);
|
||||
const globalServices: GlobalServices = {
|
||||
db: db,
|
||||
};
|
||||
Object.assign(global, globalServices);
|
||||
console.log('Jest - setup done');
|
||||
};
|
||||
8
src/tests-rosetta-cli-data/teardown.ts
Normal file
8
src/tests-rosetta-cli-data/teardown.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import type { GlobalServices } from './setup';
|
||||
|
||||
export default async (): Promise<void> => {
|
||||
console.log('Jest - teardown..');
|
||||
const globalServices = (global as unknown) as GlobalServices;
|
||||
await globalServices.db.close();
|
||||
console.log('Jest - teardown done');
|
||||
};
|
||||
@@ -17,12 +17,13 @@ import {
|
||||
ClarityAbi,
|
||||
encodeClarityValue,
|
||||
ChainID,
|
||||
AnchorMode,
|
||||
} from '@stacks/transactions';
|
||||
import { StacksTestnet } from '@stacks/network';
|
||||
import * as BN from 'bn.js';
|
||||
import * as fs from 'fs';
|
||||
import { StacksCoreRpcClient, getCoreNodeEndpoint } from '../core-rpc/client';
|
||||
import { unwrapOptional } from '../helpers';
|
||||
import { timeout, unwrapOptional } from '../helpers';
|
||||
import * as compose from 'docker-compose';
|
||||
import * as path from 'path';
|
||||
import Docker = require('dockerode');
|
||||
@@ -94,8 +95,6 @@ const contracts: string[] = [];
|
||||
|
||||
const HOST = 'localhost';
|
||||
const PORT = 20443;
|
||||
const URL = `http://${HOST}:${PORT}`;
|
||||
|
||||
const stacksNetwork = getStacksTestnetNetwork();
|
||||
|
||||
const isContainerRunning = async (name: string): Promise<boolean> =>
|
||||
@@ -106,6 +105,7 @@ const isContainerRunning = async (name: string): Promise<boolean> =>
|
||||
}
|
||||
|
||||
const running = (containers || []).filter((container: any): boolean =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
container.Names.includes(name)
|
||||
);
|
||||
|
||||
@@ -135,13 +135,23 @@ describe('Rosetta API', () => {
|
||||
await compose.buildOne('rosetta-cli', {
|
||||
cwd: path.join(__dirname, '../../'),
|
||||
log: true,
|
||||
composeOptions: ['-f', 'docker-compose.dev.rosetta-cli.yml'],
|
||||
composeOptions: [
|
||||
'-f',
|
||||
'docker-compose.dev.rosetta-cli.yml',
|
||||
'--env-file',
|
||||
'src/tests-rosetta-cli-data/envs/env.data',
|
||||
],
|
||||
});
|
||||
// start cli container
|
||||
void compose.upOne('rosetta-cli', {
|
||||
cwd: path.join(__dirname, '../../'),
|
||||
log: true,
|
||||
composeOptions: ['-f', 'docker-compose.dev.rosetta-cli.yml'],
|
||||
composeOptions: [
|
||||
'-f',
|
||||
'docker-compose.dev.rosetta-cli.yml',
|
||||
'--env-file',
|
||||
'src/tests-rosetta-cli-data/envs/env.data',
|
||||
],
|
||||
commandOptions: ['--abort-on-container-exit'],
|
||||
});
|
||||
|
||||
@@ -156,7 +166,7 @@ describe('Rosetta API', () => {
|
||||
for (const sender of senders) {
|
||||
const response = await deployContract(
|
||||
sender.privateKey,
|
||||
'src/tests-rosetta-cli/contracts/hello-world.clar',
|
||||
'src/tests-rosetta-cli-data/contracts/hello-world.clar',
|
||||
api
|
||||
);
|
||||
contracts.push(response.contractId);
|
||||
@@ -167,15 +177,12 @@ describe('Rosetta API', () => {
|
||||
await callContractFunction(api, sender2.privateKey, contract, 'say-hi');
|
||||
}
|
||||
|
||||
// wait for rosetta-cli to exit
|
||||
let check = true;
|
||||
while (check) {
|
||||
// todo: remove hardcoded container name with dynamic
|
||||
check = await isContainerRunning('/stacks-blockchain-api_rosetta-cli_1');
|
||||
await sleep(2000);
|
||||
//wait on rosetta-cli to finish output
|
||||
while (!rosettaOutput) {
|
||||
if (fs.existsSync('rosetta-output'))
|
||||
rosettaOutput = require('../../rosetta-output/rosetta-cli-output.json');
|
||||
await timeout(1000);
|
||||
}
|
||||
|
||||
rosettaOutput = require('../../rosetta-output/rosetta-cli-output.json');
|
||||
});
|
||||
|
||||
it('check request/response', () => {
|
||||
@@ -239,6 +246,7 @@ async function callContractFunction(
|
||||
network: stacksNetwork,
|
||||
postConditionMode: PostConditionMode.Allow,
|
||||
sponsored: false,
|
||||
anchorMode: AnchorMode.Any,
|
||||
});
|
||||
const fee = await estimateContractFunctionCall(contractCallTx, stacksNetwork);
|
||||
contractCallTx.setFee(fee);
|
||||
@@ -262,6 +270,7 @@ async function deployContract(senderPk: string, sourceFile: string, api: ApiServ
|
||||
network: stacksNetwork,
|
||||
postConditionMode: PostConditionMode.Allow,
|
||||
sponsored: false,
|
||||
anchorMode: AnchorMode.Any,
|
||||
});
|
||||
|
||||
const contractId = senderAddress + '.' + contractName;
|
||||
@@ -290,6 +299,7 @@ async function transferStx(
|
||||
network: stacksNetwork,
|
||||
memo: 'test-transaction',
|
||||
sponsored: false,
|
||||
anchorMode: AnchorMode.Any,
|
||||
});
|
||||
const serialized: Buffer = transferTx.serialize();
|
||||
|
||||
@@ -330,9 +340,7 @@ function uniqueId() {
|
||||
}
|
||||
|
||||
async function waitForBlock(api: ApiServer) {
|
||||
await new Promise<string>(resolve => api.datastore.once('blockUpdate', blockHash => resolve(blockHash)));
|
||||
}
|
||||
|
||||
function sleep(ms: number) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
await new Promise<string>(resolve =>
|
||||
api.datastore.once('blockUpdate', blockHash => resolve(blockHash))
|
||||
);
|
||||
}
|
||||
@@ -1,285 +0,0 @@
|
||||
import { PgDataStore, cycleMigrations, runMigrations } from '../datastore/postgres-store';
|
||||
import { PoolClient } from 'pg';
|
||||
import { ApiServer, startApiServer } from '../api/init';
|
||||
import { startEventServer } from '../event-stream/event-server';
|
||||
import { Server } from 'net';
|
||||
import fetch from 'node-fetch';
|
||||
import { DbBlock } from '../datastore/common';
|
||||
import {
|
||||
makeSTXTokenTransfer,
|
||||
makeContractDeploy,
|
||||
PostConditionMode,
|
||||
makeContractCall,
|
||||
ClarityValue,
|
||||
getAddressFromPrivateKey,
|
||||
getAbi,
|
||||
estimateContractFunctionCall,
|
||||
ClarityAbi,
|
||||
encodeClarityValue,
|
||||
ChainID,
|
||||
} from '@stacks/transactions';
|
||||
import { StacksTestnet } from '@stacks/network';
|
||||
import * as BN from 'bn.js';
|
||||
import * as fs from 'fs';
|
||||
import { StacksCoreRpcClient, getCoreNodeEndpoint } from '../core-rpc/client';
|
||||
import { unwrapOptional } from '../helpers';
|
||||
import * as compose from 'docker-compose';
|
||||
import * as path from 'path';
|
||||
import Docker = require('dockerode');
|
||||
|
||||
const docker = new Docker();
|
||||
|
||||
const sender1 = {
|
||||
address: 'STF9B75ADQAVXQHNEQ6KGHXTG7JP305J2GRWF3A2',
|
||||
privateKey: 'ce109fee08860bb16337c76647dcbc02df0c06b455dd69bcf30af74d4eedd19301',
|
||||
};
|
||||
const sender2 = {
|
||||
address: 'ST18MDW2PDTBSCR1ACXYRJP2JX70FWNM6YY2VX4SS',
|
||||
privateKey: '08c14a1eada0dd42b667b40f59f7c8dedb12113613448dc04980aea20b268ddb01',
|
||||
};
|
||||
|
||||
const sender3 = {
|
||||
address: 'ST1DTAEAKM02GKCT4NGKTVER8MTJJHYQ9NT27E677',
|
||||
privateKey: 'fdab825d3a12ca73d24c0b446eda8639605025450a4bf6716a2627121c594d0a01',
|
||||
};
|
||||
|
||||
const senders = [sender1, sender2, sender3];
|
||||
|
||||
const recipientAdd1 = 'ST3KTZ45AQES4PNNHB2YGGJ4JXQQMRACRNZPQ19SP';
|
||||
const recipientAdd2 = 'ST17ZNMSQMDARSCZ85Z7BVJX6T20ZWDF3VX0ZP33K';
|
||||
const recipientAdd3 = 'ST3DR3THSKSWRH10SEDFFP3D90KZYG57J5N8M9KR9';
|
||||
const recipientPk3 = '3af9b0b442389252c61db56233a2267cd242cc9f8a3284ae64808784c684c4ef01';
|
||||
const recipientAdd4 = 'ST3WHD5WEKHENA38T9BWZK9M0SDXA46T9DQT0ZDTY';
|
||||
const recipientAdd5 = 'ST2WZKZVZRJJMT2RJPWWM9FVC20S9R30CNVMJEX0H';
|
||||
const recipientAdd6 = 'STFHWET10QDAQ8B88WNAVEV15XVNWNNCTMQGD5KN';
|
||||
const recipientAdd7 = 'ST3YFNTA33A14NYTE0P85790E2X7Y890SD24K8WAK';
|
||||
const recipientAdd8 = 'ST367HN911AFQ666829M2C6XP0HPMSTT517B5FMNB';
|
||||
const recipientAdd9 = 'STN5W5SAFHVWS9P6BDMVJQZWQGCAHQBPXQYKKCCW';
|
||||
const recipientAdd10 = 'ST5WDT0C4995DWA3H7YDKYM0NK4QNADR4T93HYD1';
|
||||
const recipientAdd11 = 'ST1JBBKAPX6XDGR9TEGQTNVV2H4NJX456FKRJ5QWA';
|
||||
const recipientAdd12 = 'ST3T6ERPJ2TXPQ668747Z52P4EJBYY966SVFM7W8X';
|
||||
const recipientAdd13 = 'ST2Y60MJ5CPGEN1CP405H7X8CPCEYV2FJ0PHZ742P';
|
||||
const recipientAdd14 = 'STAXJZ26VDBNA5TVJRJ63SV77C1AWW786X84V634';
|
||||
const recipientAdd15 = 'ST3Q362NAVV3W6T1GV0XH78JJJ0BKZREHR6C168TV';
|
||||
const recipientAdd16 = 'ST16EDNWEW80AX2737KA58SJZQ8QNW7DXKTDBBVCV';
|
||||
const recipientAdd17 = 'ST3VHZ1ETEB0D7V27TCB5BB9YVQVWPQJHJG5Y6ARH';
|
||||
const recipientAdd18 = 'ST2NC3GSVB511TPCBDKB4PRA6VJHYNSGH93756H1M';
|
||||
const recipientAdd19 = 'ST11QXGG0SRPC5QYESX3WA55BKQWWMPV7CSWKG6SN';
|
||||
const recipientAdd20 = 'STJ5PW80NKC6NJ0J87YWXYNGE474RN38K2B4YW1R';
|
||||
|
||||
const recipients = [
|
||||
recipientAdd1,
|
||||
recipientAdd2,
|
||||
recipientAdd3,
|
||||
recipientAdd4,
|
||||
recipientAdd5,
|
||||
recipientAdd6,
|
||||
recipientAdd7,
|
||||
recipientAdd8,
|
||||
recipientAdd9,
|
||||
recipientAdd10,
|
||||
recipientAdd11,
|
||||
recipientAdd12,
|
||||
recipientAdd13,
|
||||
recipientAdd14,
|
||||
recipientAdd15,
|
||||
recipientAdd16,
|
||||
recipientAdd17,
|
||||
recipientAdd18,
|
||||
recipientAdd19,
|
||||
recipientAdd20,
|
||||
];
|
||||
|
||||
const contracts: string[] = [];
|
||||
|
||||
const HOST = 'localhost';
|
||||
const PORT = 20443;
|
||||
const URL = `http://${HOST}:${PORT}`;
|
||||
|
||||
const stacksNetwork = getStacksTestnetNetwork();
|
||||
|
||||
const isContainerRunning = async (name: string): Promise<boolean> =>
|
||||
new Promise((resolve, reject): void => {
|
||||
docker.listContainers((err: any, containers: any): void => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
const running = (containers || []).filter((container: any): boolean =>
|
||||
container.Names.includes(name)
|
||||
);
|
||||
|
||||
resolve(running.length > 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Rosetta API', () => {
|
||||
let db: PgDataStore;
|
||||
let client: PoolClient;
|
||||
let eventServer: Server;
|
||||
let api: ApiServer;
|
||||
let rosettaOutput: any;
|
||||
|
||||
beforeAll(async () => {
|
||||
process.env.PG_DATABASE = 'postgres';
|
||||
await cycleMigrations();
|
||||
db = await PgDataStore.connect();
|
||||
client = await db.pool.connect();
|
||||
eventServer = await startEventServer({ datastore: db, chainId: ChainID.Testnet });
|
||||
api = await startApiServer({ datastore: db, chainId: ChainID.Testnet });
|
||||
|
||||
// build rosetta-cli container
|
||||
await compose.buildOne('rosetta-cli', {
|
||||
cwd: path.join(__dirname, '../../'),
|
||||
log: true,
|
||||
composeOptions: ['-f', 'docker-compose.dev.rosetta-construction.yml'],
|
||||
});
|
||||
// start cli container
|
||||
void compose.upOne('rosetta-cli', {
|
||||
cwd: path.join(__dirname, '../../'),
|
||||
log: true,
|
||||
composeOptions: ['-f', 'docker-compose.dev.rosetta-construction.yml'],
|
||||
commandOptions: ['--abort-on-container-exit'],
|
||||
});
|
||||
|
||||
await waitForBlock(api);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await new Promise<void>(resolve => eventServer.close(() => resolve()));
|
||||
await api.terminate();
|
||||
client.release();
|
||||
await db?.close();
|
||||
await runMigrations(undefined, 'down');
|
||||
});
|
||||
});
|
||||
|
||||
async function callContractFunction(
|
||||
api: ApiServer,
|
||||
senderPk: string,
|
||||
contractId: string,
|
||||
functionName: string,
|
||||
...functionArgs: string[]
|
||||
) {
|
||||
await waitForBlock(api);
|
||||
const [contractAddr, contractName] = contractId.split('.');
|
||||
|
||||
const contractAbi: ClarityAbi = await getAbi(contractAddr, contractName, stacksNetwork);
|
||||
const abiFunction = contractAbi.functions.find(fn => fn.name === functionName);
|
||||
if (abiFunction === undefined) {
|
||||
throw new Error(`Contract ${contractId} ABI does not have function "${functionName}"`);
|
||||
}
|
||||
const clarityValueArgs: ClarityValue[] = new Array(abiFunction.args.length);
|
||||
for (let i = 0; i < clarityValueArgs.length; i++) {
|
||||
const abiArg = abiFunction.args[i];
|
||||
const stringArg = unwrapOptional(functionArgs[i]);
|
||||
const clarityVal = encodeClarityValue(abiArg.type, stringArg);
|
||||
clarityValueArgs[i] = clarityVal;
|
||||
}
|
||||
|
||||
const contractCallTx = await makeContractCall({
|
||||
contractAddress: contractAddr,
|
||||
contractName: contractName,
|
||||
functionName: functionName,
|
||||
functionArgs: clarityValueArgs,
|
||||
senderKey: senderPk,
|
||||
network: stacksNetwork,
|
||||
postConditionMode: PostConditionMode.Allow,
|
||||
sponsored: false,
|
||||
});
|
||||
const fee = await estimateContractFunctionCall(contractCallTx, stacksNetwork);
|
||||
contractCallTx.setFee(fee);
|
||||
|
||||
const serialized: Buffer = contractCallTx.serialize();
|
||||
|
||||
const { txId } = await sendCoreTx(serialized, api, 'call-contract-func');
|
||||
}
|
||||
|
||||
async function deployContract(senderPk: string, sourceFile: string, api: ApiServer) {
|
||||
await waitForBlock(api);
|
||||
const contractName = `test-contract-${uniqueId()}`;
|
||||
const senderAddress = getAddressFromPrivateKey(senderPk, stacksNetwork.version);
|
||||
const source = fs.readFileSync(sourceFile).toString();
|
||||
const normalized_contract_source = source.replace(/\r/g, '').replace(/\t/g, ' ');
|
||||
|
||||
const contractDeployTx = await makeContractDeploy({
|
||||
contractName: contractName,
|
||||
codeBody: normalized_contract_source,
|
||||
senderKey: senderPk,
|
||||
network: stacksNetwork,
|
||||
postConditionMode: PostConditionMode.Allow,
|
||||
sponsored: false,
|
||||
});
|
||||
|
||||
const contractId = senderAddress + '.' + contractName;
|
||||
|
||||
const feeRateReq = await fetch(stacksNetwork.getTransferFeeEstimateApiUrl());
|
||||
const feeRateResult = await feeRateReq.text();
|
||||
const txBytes = new BN(contractDeployTx.serialize().byteLength);
|
||||
const feeRate = new BN(feeRateResult);
|
||||
const fee = feeRate.mul(txBytes);
|
||||
contractDeployTx.setFee(fee);
|
||||
const { txId } = await sendCoreTx(contractDeployTx.serialize(), api, 'deploy-contract');
|
||||
|
||||
return { txId, contractId };
|
||||
}
|
||||
async function transferStx(
|
||||
recipientAddr: string,
|
||||
amount: number,
|
||||
senderPk: string,
|
||||
api: ApiServer
|
||||
) {
|
||||
await waitForBlock(api);
|
||||
const transferTx = await makeSTXTokenTransfer({
|
||||
recipient: recipientAddr,
|
||||
amount: new BN(amount),
|
||||
senderKey: senderPk,
|
||||
network: stacksNetwork,
|
||||
memo: 'test-transaction',
|
||||
sponsored: false,
|
||||
});
|
||||
const serialized: Buffer = transferTx.serialize();
|
||||
|
||||
const { txId } = await sendCoreTx(serialized, api, 'transfer-stx');
|
||||
|
||||
return txId;
|
||||
}
|
||||
|
||||
async function sendCoreTx(
|
||||
serializedTx: Buffer,
|
||||
api: ApiServer,
|
||||
type: string
|
||||
): Promise<{ txId: string }> {
|
||||
try {
|
||||
const submitResult = await new StacksCoreRpcClient({
|
||||
host: HOST,
|
||||
port: PORT,
|
||||
}).sendTransaction(serializedTx);
|
||||
return submitResult;
|
||||
} catch (error) {
|
||||
console.log(type);
|
||||
console.error(error);
|
||||
}
|
||||
return Promise.resolve({ txId: '' });
|
||||
}
|
||||
|
||||
export function getStacksTestnetNetwork() {
|
||||
const stacksNetwork = new StacksTestnet();
|
||||
stacksNetwork.coreApiUrl = getCoreNodeEndpoint({
|
||||
host: `http://${HOST}`,
|
||||
port: PORT,
|
||||
});
|
||||
return stacksNetwork;
|
||||
}
|
||||
|
||||
function uniqueId() {
|
||||
return Math.random().toString(16).slice(-4);
|
||||
}
|
||||
|
||||
async function waitForBlock(api: ApiServer) {
|
||||
await new Promise<string>(resolve => api.datastore.once('blockUpdate', blockHash => resolve(blockHash)));
|
||||
}
|
||||
|
||||
function sleep(ms: number) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
Reference in New Issue
Block a user