├── .eslintignore ├── CODEOWNERS ├── .gitattributes ├── orchestrator ├── scripts │ ├── setApiVersions.ts │ ├── supply.ts │ └── deploy.ts ├── builder │ ├── blockchain │ │ ├── password │ │ ├── Dockerfile │ │ ├── init.sh │ │ ├── create-account.sh │ │ ├── .ethereum │ │ │ └── keystore │ │ │ │ └── UTC--2023-10-10T10-59-11.037135818Z--ceee442a149784faa65c35e328ccd64d874f9a02 │ │ ├── docker-build.sh │ │ ├── start.sh │ │ └── genesis.json │ ├── bee-data-dirs │ │ ├── .gitignore │ │ ├── fdp-play-queen │ │ │ ├── .gitignore │ │ │ └── keys │ │ │ │ ├── pss.key │ │ │ │ ├── libp2p.key │ │ │ │ ├── swarm.key │ │ │ │ └── libp2p_v2.key │ │ ├── fdp-play-worker-1 │ │ │ ├── .gitignore │ │ │ └── keys │ │ │ │ ├── pss.key │ │ │ │ ├── swarm.key │ │ │ │ ├── libp2p.key │ │ │ │ └── libp2p_v2.key │ │ ├── fdp-play-worker-2 │ │ │ ├── .gitignore │ │ │ └── keys │ │ │ │ ├── pss.key │ │ │ │ ├── swarm.key │ │ │ │ ├── libp2p.key │ │ │ │ └── libp2p_v2.key │ │ ├── fdp-play-worker-3 │ │ │ ├── .gitignore │ │ │ └── keys │ │ │ │ ├── pss.key │ │ │ │ ├── swarm.key │ │ │ │ ├── libp2p.key │ │ │ │ └── libp2p_v2.key │ │ └── fdp-play-worker-4 │ │ │ ├── .gitignore │ │ │ └── keys │ │ │ ├── pss.key │ │ │ ├── swarm.key │ │ │ ├── libp2p.key │ │ │ └── libp2p_v2.key │ ├── utils │ │ ├── .gitignore │ │ ├── env-variable-value.sh │ │ └── build-image-tag.sh │ ├── network.sh │ ├── .env │ ├── bee-cleanup.sh │ ├── publish-environment.sh │ ├── bee-docker-build.sh │ ├── build-environment.sh │ ├── environment.sh │ ├── gen-traffic.js │ └── bee.sh ├── .gitignore ├── contracts │ ├── ERC20PresetMinterPauser.sol │ ├── SwapPriceOracle.bytecode │ ├── PostagePriceOracle.bytecode │ ├── StakeRegistry.bytecode │ └── SimpleSwapFactory.bytecode ├── bee-eth-addresses.json ├── tsconfig.json ├── contract-addresses.json ├── hardhat.config.ts ├── package.json └── README.md ├── src ├── utils │ ├── blockchain │ │ ├── index.d.ts │ │ ├── account.ts │ │ ├── api.ts │ │ └── jsonrpc.ts │ ├── index.ts │ ├── types.ts │ ├── config-sources.ts │ ├── error.ts │ └── wait.ts ├── command │ ├── eth │ │ ├── index.ts │ │ ├── eth-command.ts │ │ ├── balance.ts │ │ └── send.ts │ ├── root-command │ │ ├── index.ts │ │ ├── printer.ts │ │ └── logging.ts │ ├── stop.ts │ ├── logs.ts │ └── start.ts ├── application.ts ├── index.ts ├── printer.ts ├── constants.ts ├── shim │ └── crypto.ts └── config.ts ├── .editorconfig ├── tsconfig.test.json ├── commitlint.config.js ├── .prettierrc ├── .github ├── dependabot.yml └── workflows │ ├── release-github.yaml │ ├── publish-npmjs.yaml │ ├── test.yaml │ ├── check.yaml │ └── publish-docker.yaml ├── test ├── utils │ ├── run.ts │ ├── docker.ts │ └── console-log.ts └── integration │ ├── eth.spec.ts │ ├── stop.spec.ts │ └── start.spec.ts ├── tsconfig.json ├── .gitignore ├── .babelrc.js ├── jest.config.ts ├── LICENSE ├── DISCLAIMER ├── package.json ├── .eslintrc.js ├── webpack.config.ts ├── README.md └── CHANGELOG.md /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/** 2 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @nugaon 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /orchestrator/scripts/setApiVersions.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /orchestrator/builder/blockchain/password: -------------------------------------------------------------------------------- 1 | toTheSun -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/.gitignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | -------------------------------------------------------------------------------- /src/utils/blockchain/index.d.ts: -------------------------------------------------------------------------------- 1 | // Wallet address of the sender 2 | type Signer = string 3 | -------------------------------------------------------------------------------- /orchestrator/builder/utils/.gitignore: -------------------------------------------------------------------------------- 1 | # ignore tmp file for custom image tag 2 | .commit-version-tag 3 | -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-queen/.gitignore: -------------------------------------------------------------------------------- 1 | statestore 2 | localstore 3 | stamperstore 4 | kademlia-metrics 5 | -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-worker-1/.gitignore: -------------------------------------------------------------------------------- 1 | statestore 2 | localstore 3 | stamperstore 4 | kademlia-metrics 5 | -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-worker-2/.gitignore: -------------------------------------------------------------------------------- 1 | statestore 2 | localstore 3 | stamperstore 4 | kademlia-metrics 5 | -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-worker-3/.gitignore: -------------------------------------------------------------------------------- 1 | statestore 2 | localstore 3 | stamperstore 4 | kademlia-metrics 5 | -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-worker-4/.gitignore: -------------------------------------------------------------------------------- 1 | statestore 2 | localstore 3 | stamperstore 4 | kademlia-metrics 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | end_of_line = lf 7 | -------------------------------------------------------------------------------- /orchestrator/builder/blockchain/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ethereum/client-go:release-1.13 2 | COPY . /root 3 | EXPOSE 9545 9546 30303 30303/udp 4 | ENTRYPOINT ["geth"] 5 | -------------------------------------------------------------------------------- /orchestrator/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | coverage.json 4 | typechain 5 | typechain-types 6 | 7 | # Hardhat files 8 | cache 9 | artifacts 10 | 11 | builder/blockchain/.ethereum/geth 12 | -------------------------------------------------------------------------------- /orchestrator/contracts/ERC20PresetMinterPauser.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | pragma solidity ^0.8.19; 3 | 4 | import '@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol'; 5 | -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [ 4 | "src", 5 | "test", 6 | "jest.config.ts" 7 | ], 8 | "compilerOptions": { 9 | "noEmit": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | rules: { 4 | 'body-max-line-length': [0, 'always', Infinity], // disable commit body length restriction 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Sleep for N miliseconds 3 | * 4 | * @param ms Number of miliseconds to sleep 5 | */ 6 | export async function sleep(ms: number): Promise { 7 | return new Promise(resolve => setTimeout(() => resolve(), ms)) 8 | } 9 | -------------------------------------------------------------------------------- /src/utils/types.ts: -------------------------------------------------------------------------------- 1 | type FlavoredType = Type & { 2 | __tag__?: Name 3 | } 4 | type NumberString = FlavoredType 5 | 6 | export function isNumberString(value: string): value is NumberString { 7 | return /^\d+(\.\d+)?$/.test(value) 8 | } 9 | -------------------------------------------------------------------------------- /src/utils/config-sources.ts: -------------------------------------------------------------------------------- 1 | export function stripCommit(version: string): string { 2 | if (version === 'latest') { 3 | return version 4 | } 5 | 6 | // If the version contains commit ==> hash remove it 7 | return version.replace('-stateful', '').replace(/-[0-9a-fA-F]{8}$/, '') 8 | } 9 | -------------------------------------------------------------------------------- /orchestrator/bee-eth-addresses.json: -------------------------------------------------------------------------------- 1 | [ 2 | "0x26234a2ad3ba8b398a762f279b792cfacd536a3f", 3 | "0x8e3cb0148c5f39577fb815dc8c37795e30f5dcfa", 4 | "0xed52b8ac9b1bc1e7f3fe46ea3a094fbaa8f6ccb4", 5 | "0x119331b8074bd779fc5b96fe4d50947d31addfe4", 6 | "0x102aaa556337d86e270010588d9fbd5ecaeebff8" 7 | ] 8 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "bracketSpacing": true, 6 | "semi": false, 7 | "singleQuote": true, 8 | "quoteProps": "as-needed", 9 | "trailingComma": "all", 10 | "endOfLine": "lf", 11 | "arrowParens": "avoid", 12 | "proseWrap": "always" 13 | } 14 | -------------------------------------------------------------------------------- /src/command/eth/index.ts: -------------------------------------------------------------------------------- 1 | import { GroupCommand } from 'furious-commander' 2 | import { Balance } from './balance' 3 | import { Send } from './send' 4 | 5 | export class Eth implements GroupCommand { 6 | public readonly name = 'eth' 7 | 8 | public readonly description = 'Blockchain related operations.' 9 | 10 | public subCommandClasses = [Send, Balance] 11 | } 12 | -------------------------------------------------------------------------------- /orchestrator/builder/blockchain/init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # doc: https://geth.ethereum.org/docs/fundamentals/private-network 3 | MY_PATH=$(dirname "$0") 4 | MY_PATH=$( cd "$MY_PATH" && pwd ) 5 | 6 | docker run --rm -v $MY_PATH:/root ethereum/client-go:release-1.13 init /root/genesis.json 7 | echo "Build the docker image of the blockchain with docker-build.sh after migrating and supplying" 8 | -------------------------------------------------------------------------------- /orchestrator/builder/blockchain/create-account.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # doc: https://geth.ethereum.org/docs/fundamentals/private-network 3 | MY_PATH=$(dirname "$0") 4 | MY_PATH=$( cd "$MY_PATH" && pwd ) 5 | 6 | docker run --rm -v $MY_PATH:/root ethereum/client-go:release-1.13 account new --password /root/password 7 | echo "Update genesis.json and start.sh with the generated address before moving on" continue -------------------------------------------------------------------------------- /orchestrator/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true 10 | }, 11 | "include": ["./test", "./src", "./scripts"], 12 | "files": ["./hardhat.config.ts"] 13 | } 14 | -------------------------------------------------------------------------------- /src/application.ts: -------------------------------------------------------------------------------- 1 | import { Application } from 'furious-commander/dist/application' 2 | import PackageJson from '../package.json' 3 | 4 | export const application: Application = { 5 | name: 'FDP Play', 6 | command: 'fdp-play', 7 | description: 'Orchestration CLI for spinning up local development Bee cluster and FDP environment with Docker', 8 | version: PackageJson.version, 9 | autocompletion: 'fromOption', 10 | } 11 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | 8 | - package-ecosystem: "npm" 9 | directory: "/" 10 | schedule: 11 | interval: "weekly" 12 | # Always increase the version in package.json as well (for patch versions by default only package-lock.json i updated) 13 | versioning-strategy: increase 14 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import './shim/crypto' 3 | import { cli } from 'furious-commander' 4 | import { application } from './application' 5 | import { optionParameters, rootCommandClasses } from './config' 6 | import { printer } from './printer' 7 | import { errorHandler } from './utils/error' 8 | 9 | cli({ 10 | rootCommandClasses, 11 | optionParameters, 12 | printer, 13 | application, 14 | errorHandler, 15 | }) 16 | -------------------------------------------------------------------------------- /orchestrator/builder/network.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | 6 | MY_PATH=$(dirname "$0") 7 | MY_PATH=$( cd "$MY_PATH" && pwd ) 8 | # Check used system variable set 9 | BEE_ENV_PREFIX=$("$MY_PATH/utils/env-variable-value.sh" BEE_ENV_PREFIX) 10 | 11 | NETWORK="$BEE_ENV_PREFIX-network" 12 | if ! eval "docker network inspect $NETWORK > /dev/null" ; then 13 | echo "Creating $NETWORK..." 14 | docker network create $NETWORK 15 | fi 16 | -------------------------------------------------------------------------------- /test/utils/run.ts: -------------------------------------------------------------------------------- 1 | import { cli } from 'furious-commander' 2 | import { optionParameters, rootCommandClasses } from '../../src/config' 3 | import { errorHandler } from '../../src/utils/error' 4 | 5 | export async function run(argv: string[]): ReturnType { 6 | const commandBuilder = await cli({ 7 | rootCommandClasses, 8 | optionParameters, 9 | testArguments: argv, 10 | errorHandler, 11 | }) 12 | 13 | return commandBuilder 14 | } 15 | -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-queen/keys/pss.key: -------------------------------------------------------------------------------- 1 | {"address":"4f86d2a325b5484114c746f4f62567c3307c4609","crypto":{"cipher":"aes-128-ctr","ciphertext":"a965f1be20058860f1c463de2f7fd14b168f0eacf015dcfa009c5e8fff6f943f","cipherparams":{"iv":"51379174047cfdf82a4267e4f33de608"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"18779e1a157feb172aee97c53f8c2302b74127289b10466372cd3971a66ab28e"},"mac":"6d052ca5750d5f35b7d3798484c414bcc8ad8f8b3ed5309e78a80974a55d1a9e"},"version":3} -------------------------------------------------------------------------------- /orchestrator/builder/utils/env-variable-value.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo_env_variable_value() { 4 | REF=$(printenv $1) 5 | 6 | # There were no global set 7 | if [ -z "$REF" ] ; then 8 | MY_PATH=$(dirname "$0") 9 | ENV_PATH=$( cd "$MY_PATH/.." && pwd ) 10 | VALUE=$(grep "^$1=" "$ENV_PATH/.env" | cut -d= -f2-) 11 | VALUE=${VALUE//\"/} 12 | VALUE=${VALUE//\'/} 13 | echo "$VALUE" 14 | else 15 | echo "$REF" 16 | fi 17 | } 18 | 19 | echo_env_variable_value $1 -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-queen/keys/libp2p.key: -------------------------------------------------------------------------------- 1 | {"address":"33d0fff43ab18b6066d5a7c1a74852f0e5f1d549","crypto":{"cipher":"aes-128-ctr","ciphertext":"d0c9f5ba4d26f4c3a5ff75054a452ae06c4e7137456964286fb71ee4c39001c9","cipherparams":{"iv":"d53f8d37effe8eaf9215b614194c6336"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"676530f1559492f872a030a68044434c62b4e4ccc45834cfdd93ac7ae5bf3eaf"},"mac":"b4f0c223f23a028d0070a0657ac2f394c17d7501b23d17ffc9da0e0eb4a468f7"},"version":3} -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-queen/keys/swarm.key: -------------------------------------------------------------------------------- 1 | {"address":"26234a2ad3ba8b398a762f279b792cfacd536a3f","crypto":{"cipher":"aes-128-ctr","ciphertext":"fffb7140c654126abb77a8c2f876d6be0e15213a823b1bb69b061bc015b4dbb1","cipherparams":{"iv":"ee1fd715ab558a569ca47707c5cc0dda"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"4d959d634ced780cd51217d654b2b4c776118a4bdfa91c6899ab08348ce2d6b0"},"mac":"b3ebd4561b62ced24a4b059fcbc4106690fb25bb3774bff323dcdfdf61661c6c"},"version":3} -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-worker-1/keys/pss.key: -------------------------------------------------------------------------------- 1 | {"address":"172c9699e88d18b9c4bb91bd76cfba83c021d341","crypto":{"cipher":"aes-128-ctr","ciphertext":"81de20a914b615f102e660283402f8481fb37db55916351fa7ec4d73be167f20","cipherparams":{"iv":"054a833aced209f8435cb7ed6153832d"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"2395d56b6397fbe4c76bd74f5b312b4b1e7e4cddc5b86986eee411cb98b1789c"},"mac":"0342ec20f4d1ac86734527fc28015cd84a1bcfbf621c0e4fcefecf8f98ccc206"},"version":3} -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-worker-1/keys/swarm.key: -------------------------------------------------------------------------------- 1 | {"address":"8e3cb0148c5f39577fb815dc8c37795e30f5dcfa","crypto":{"cipher":"aes-128-ctr","ciphertext":"c3c64c5b4371a59d87a4883b499a5622774511d4421298c0e8849acb64d1111e","cipherparams":{"iv":"e31330130d3045f1b2614e819a4c06f6"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"25be64863839d78f078ff7c874412c682dc2fc290138ce749ac3bdc7ed267b5a"},"mac":"c8ecf497e8afec977de05b2c514194aa329eca701ca559520a0d4545591e3d2f"},"version":3} -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-worker-2/keys/pss.key: -------------------------------------------------------------------------------- 1 | {"address":"7618b0084b6b274d5140049df8b3ae423626fcac","crypto":{"cipher":"aes-128-ctr","ciphertext":"19a117a9c17ef72f2b2fb3642f6a07bafda0487d58428fab05dc175976dbd0b3","cipherparams":{"iv":"10d2d1bcf0281b0cac32b2e2a84ea701"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"e504f0e661803766a6e0841345987ec112edbda80569d9f912a3618d8c9fc904"},"mac":"682392951017ce45ea64a2e225dcd5cae129b78d6f34602123f3746bd0db5168"},"version":3} -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-worker-2/keys/swarm.key: -------------------------------------------------------------------------------- 1 | {"address":"ed52b8ac9b1bc1e7f3fe46ea3a094fbaa8f6ccb4","crypto":{"cipher":"aes-128-ctr","ciphertext":"439cd5a2ebe1b37417708dff4f494eccef49ca8bf90caf28fb941fe964e3826d","cipherparams":{"iv":"0ea3e06704e1842ab7a9756a18ac4c27"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"54774845914adc48df887864a1935e97de333045021c27eb0561cacfd65eed78"},"mac":"40de101683baa8acd76a3577211a6827e330a81d7a7e28af9c5872e2ea855b6a"},"version":3} -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-worker-3/keys/pss.key: -------------------------------------------------------------------------------- 1 | {"address":"b531efea3a09881d97e6526ff5ac2647fb8d8fb7","crypto":{"cipher":"aes-128-ctr","ciphertext":"a6e3791bf08c4c49531ebe24156201b2654c1b27c4fdf022c8281197736e3b4d","cipherparams":{"iv":"e6a73c1b91f81dff9031f64edce1b611"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"31acaa07698ca075e9da7fc039087d58991cf719d69d547e9b87a8ae9f8da193"},"mac":"9d04640df0f569275f99c70e20d1221b6dda50f387824409ff40c68efc98289c"},"version":3} -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-worker-3/keys/swarm.key: -------------------------------------------------------------------------------- 1 | {"address":"119331b8074bd779fc5b96fe4d50947d31addfe4","crypto":{"cipher":"aes-128-ctr","ciphertext":"8ad055c629e5ec8731a879a72c994a4813c1fcc512ed2630c287dfd13e00aa1e","cipherparams":{"iv":"602457307b87eb967542f89d90ab6116"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"fcb50fe029550bce3c8d33db67bd8a48bd51d74b32b13e8e771bb158a9083bf3"},"mac":"e3e590561691f13c12d78c5eadb7adb106cb32e32af6f07f44a8d0188bda0043"},"version":3} -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-worker-4/keys/pss.key: -------------------------------------------------------------------------------- 1 | {"address":"e629ba730a0ac0d6efc6a90957af96ca12be257d","crypto":{"cipher":"aes-128-ctr","ciphertext":"5878029af23f16cb63d4a4d9d90a40fb53921aa8a7639c0e35f4ad835ae7299c","cipherparams":{"iv":"b3d8e4e9d0366ee572e8ead3b5c7d05b"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"78b7beef4f18d6cc4cfb751e7a34f4bdd837e25f0fa2ad0c0f0f437368eb0566"},"mac":"4ea8a7787dc1a055af8ecfaded2d2d3fd45cd45d626fbcb3f5dadbda3bd5588e"},"version":3} -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-worker-4/keys/swarm.key: -------------------------------------------------------------------------------- 1 | {"address":"102aaa556337d86e270010588d9fbd5ecaeebff8","crypto":{"cipher":"aes-128-ctr","ciphertext":"0794b7bfb7f68df0abb0a729c1774fa5821fb1811cd04c48e8644d6743b20307","cipherparams":{"iv":"533434567172fb0c3cf6c1c5a94621cd"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"70fa8ec6fce2fc2adcb14ecef2945dd49592c27605baa45f7e051845b2f04813"},"mac":"4f5ef03dcf39e7b6e2c3d3ffaca260a576968f64a2026b1b8749f430b25217a0"},"version":3} -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-worker-1/keys/libp2p.key: -------------------------------------------------------------------------------- 1 | {"address":"a073d821229ab0ec33a4ca823a48328171f30dfc","crypto":{"cipher":"aes-128-ctr","ciphertext":"e73ed3fabee776bd9151f7f7594479f969b58941a2bbdeb7534c4897211b0e51","cipherparams":{"iv":"2d376deaad1c949dc7eb49af4521a6e1"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"bbd2a1f20411edd89c7ca4eabad6fc4deb23edadfdbba94067ec6b30f68bd313"},"mac":"20c5d9d0ec69877d8087f91056cce750a624eae094d05a18428b36ce36549603"},"version":3} -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-worker-2/keys/libp2p.key: -------------------------------------------------------------------------------- 1 | {"address":"d31ef99de0a34356f3bd356617f332fddc948232","crypto":{"cipher":"aes-128-ctr","ciphertext":"022c6805e9b8e7e499046d26cba90e90fd37c46cf9ee21c1aff15f50a2a8d3c6","cipherparams":{"iv":"d4d96fb0ee5a1df7534fdf702c7733ca"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"a0f1e03f9f73c5e7607c4b8c68242e0a7c705e40e9193952a209d4ed9b204a7f"},"mac":"ecea407aa1c76be3fb0254aecf92cba256dfd8f9f1eca904415f7c8877be8452"},"version":3} -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-worker-3/keys/libp2p.key: -------------------------------------------------------------------------------- 1 | {"address":"906ab9aede7032f8e1049d057355904f8b9e15bc","crypto":{"cipher":"aes-128-ctr","ciphertext":"32bf4178f95c3554ff1b99c0f8a00458e0b6e1b017cd18dbac8e08d620687c33","cipherparams":{"iv":"66e35d850dfc6ff0bbfb67e8ce7d11c7"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"dd0a67dd5feb9f4c538fcfdcdfa04b48d229a9d0222513a14ddd6b92083d07f1"},"mac":"7bc40b0ea294d77bdcff48dab0d83f02302ae1d73154be132f017cf84c30ac6f"},"version":3} -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-worker-4/keys/libp2p.key: -------------------------------------------------------------------------------- 1 | {"address":"554b4e35700ba8118b74b0c3d006b46b4322d37c","crypto":{"cipher":"aes-128-ctr","ciphertext":"b8b2431700b19fffaab5b5a3afae2f26610617c30db4d85a10eeebc6c2c1e2ed","cipherparams":{"iv":"2e5228d8b2cebf36989f3e8738034ce4"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"cde5530d1e339e03087f6bcc17e4380c685df529307caeb66e2437bc455d7f09"},"mac":"80ae4166ded63aa4c8f77227e6c200c11621ee6ee2a0f2370e17fefd61776160"},"version":3} -------------------------------------------------------------------------------- /orchestrator/contract-addresses.json: -------------------------------------------------------------------------------- 1 | { 2 | "bzzToken": "0xe78A0F7E598Cc8b0Bb87894B0F60dD2a88d6a8Ab", 3 | "swapPriceOrcale": "0x5b1869D9A4C187F2EAa108f3062412ecf0526b24", 4 | "swapFactory": "0xCfEB869F69431e42cdB54A4F4f105C19C080A601", 5 | "postage": "0x254dffcd3277C0b1660F6d42EFbB754edaBAbC2B", 6 | "postagePriceOracle": "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550", 7 | "stakeRegistry": "0xD833215cBcc3f914bD1C9ece3EE7BF8B14f841bb", 8 | "redistribution": "0x9561C133DD8580860B6b7E504bC5Aa500f0f06a7" 9 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "exclude": ["node_modules", "dist", "generator"], 3 | "compilerOptions": { 4 | "alwaysStrict": true, 5 | "esModuleInterop": true, 6 | "allowSyntheticDefaultImports": true, 7 | "moduleResolution": "node", 8 | "strict": true, 9 | "experimentalDecorators": true, 10 | "emitDecoratorMetadata": true, 11 | "target": "ES6", 12 | "module": "commonjs", 13 | "outDir": "dist", 14 | "resolveJsonModule": true, 15 | "useDefineForClassFields": true 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # OS files 6 | .DS_Store 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | .nyc_output 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | build 24 | dist 25 | 26 | # Dependency directory 27 | node_modules 28 | 29 | # Generated files 30 | docs 31 | 32 | .vscode 33 | 34 | generator/build 35 | yarn.lock 36 | -------------------------------------------------------------------------------- /src/printer.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk' 2 | import { Printer } from 'furious-commander/dist/printer' 3 | import { Printer as SwarmPrinter } from './command/root-command/printer' 4 | 5 | export const printer: Printer = { 6 | print: SwarmPrinter.log, 7 | printError: SwarmPrinter.error, 8 | printHeading: (text: string) => SwarmPrinter.log(chalk.bold('█ ' + text)), 9 | formatDim: (text: string) => chalk.dim(text), 10 | formatImportant: (text: string) => chalk.bold(text), 11 | getGenericErrorMessage: () => 'Failed to run command! Please check if docker daemon is running!', 12 | } 13 | -------------------------------------------------------------------------------- /orchestrator/builder/blockchain/.ethereum/keystore/UTC--2023-10-10T10-59-11.037135818Z--ceee442a149784faa65c35e328ccd64d874f9a02: -------------------------------------------------------------------------------- 1 | {"address":"ceee442a149784faa65c35e328ccd64d874f9a02","crypto":{"cipher":"aes-128-ctr","ciphertext":"cad423a82da2630084869398d51846788b30bb8b72947a42e2481442f287a04e","cipherparams":{"iv":"5ee8b119e92fe0a60a7815f6c76223ae"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"09d8cb82dac4a8f151cd702c19b33916a0cae6ca24d3e9ce2bcfe89ccfa25c07"},"mac":"53f1eef48fd59686a1c11cd7808844a6a8edd5b929713e1c3074f33a835dd421"},"id":"12cf36d5-3894-44d5-9488-fc7470f0c577","version":3} -------------------------------------------------------------------------------- /.github/workflows/release-github.yaml: -------------------------------------------------------------------------------- 1 | # On each new commit to master, create/update a PR with release 2 | # automatically bumps version and creates changelog as per conventional commits 3 | name: Release Github 4 | 5 | on: 6 | push: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | release-please: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: GoogleCloudPlatform/release-please-action@v3 15 | id: release 16 | with: 17 | token: ${{ secrets.REPO_GHA_PAT }} 18 | release-type: node 19 | package-name: fdp-play 20 | bump-minor-pre-major: true 21 | -------------------------------------------------------------------------------- /orchestrator/builder/.env: -------------------------------------------------------------------------------- 1 | BEE_VERSION="2.6.0" 2 | BLOCKCHAIN_VERSION="2.3.0" 3 | BEE_ENV_PREFIX="fdp-play" 4 | BEE_IMAGE_PREFIX="fairdatasociety" 5 | COMMIT_VERSION_TAG="false" 6 | STATE_COMMIT="true" 7 | BLOCKCHAIN_RUN_ARGS='--allow-insecure-unlock --unlock=0xCEeE442a149784faa65C35e328CCd64d874F9a02 --password /root/password --mine --miner.etherbase=0xCEeE442a149784faa65C35e328CCd64d874F9a02 --http --http.api="debug,web3,eth,txpool,net,personal" --http.corsdomain=* --http.port=9545 --http.addr=0.0.0.0 --http.vhosts=* --ws --ws.api="debug,web3,eth,txpool,net,personal" --ws.origins=* --ws.port=9546 --maxpeers=0 --networkid=4020 --authrpc.vhosts=* --authrpc.addr=0.0.0.0' -------------------------------------------------------------------------------- /orchestrator/builder/blockchain/docker-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -o errexit 3 | set -o pipefail 4 | 5 | MY_PATH=$(dirname "$0") 6 | MY_PATH=$( cd "$MY_PATH" && pwd ) 7 | # Check used system variable set 8 | BEE_ENV_PREFIX=$("$MY_PATH/../utils/env-variable-value.sh" BEE_ENV_PREFIX) 9 | BEE_IMAGE_PREFIX=$("$MY_PATH/../utils/env-variable-value.sh" BEE_IMAGE_PREFIX) 10 | BLOCKCHAIN_VERSION=$("$MY_PATH/../utils/env-variable-value.sh" BLOCKCHAIN_VERSION) 11 | 12 | echo "Blockchain will have image version: $BLOCKCHAIN_VERSION" 13 | 14 | NAME="$BEE_ENV_PREFIX-blockchain" 15 | 16 | echo "Make blockchain docker image" 17 | sudo docker build $MY_PATH -t $BEE_IMAGE_PREFIX/$NAME:$BLOCKCHAIN_VERSION 18 | -------------------------------------------------------------------------------- /src/command/eth/eth-command.ts: -------------------------------------------------------------------------------- 1 | import { RootCommand } from '../root-command' 2 | import { BLOCKCHAIN_NETWORK_ID, BLOCKCHAIN_RPC_EP } from '../../constants' 3 | import { CommandLineError } from '../../utils/error' 4 | import { BlockchainApi } from '../../utils/blockchain/api' 5 | 6 | export class EthCommand extends RootCommand { 7 | protected provider = new BlockchainApi(BLOCKCHAIN_RPC_EP) 8 | 9 | protected async init(): Promise { 10 | await super.init() 11 | const chainId = await this.provider.chainId() 12 | 13 | if (chainId !== BLOCKCHAIN_NETWORK_ID) { 14 | throw new CommandLineError( 15 | `The RPC connection operates on different network ID than the expected 4020. Got: ${chainId}`, 16 | ) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /orchestrator/builder/blockchain/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -o errexit 3 | set -o pipefail 4 | 5 | MY_PATH=$(dirname "$0") 6 | MY_PATH=$( cd "$MY_PATH" && pwd ) 7 | # Check used system variable set 8 | BEE_ENV_PREFIX=$("$MY_PATH/../utils/env-variable-value.sh" BEE_ENV_PREFIX) 9 | BLOCKCHAIN_RUN_ARGS=$("$MY_PATH/../utils/env-variable-value.sh" BLOCKCHAIN_RUN_ARGS) 10 | NETWORK="$BEE_ENV_PREFIX-network" 11 | NAME="$BEE_ENV_PREFIX-blockchain" 12 | CONTAINER_IN_DOCKER=$(docker container ls -qaf name=$NAME) 13 | 14 | if [ -z "$CONTAINER_IN_DOCKER" ]; then 15 | exec docker run -p 127.0.0.1:9545:9545 --network $NETWORK --name $NAME -v "$MY_PATH:/root" -d \ 16 | ethereum/client-go:release-1.13 $BLOCKCHAIN_RUN_ARGS 17 | else 18 | docker start $NAME 19 | fi 20 | -------------------------------------------------------------------------------- /src/command/root-command/index.ts: -------------------------------------------------------------------------------- 1 | import { ExternalOption } from 'furious-commander' 2 | import { Logging, VerbosityLevel } from './logging' 3 | 4 | export class RootCommand { 5 | @ExternalOption('quiet') 6 | public quiet!: boolean 7 | 8 | @ExternalOption('verbose') 9 | public verbose!: boolean 10 | 11 | public verbosity!: VerbosityLevel 12 | 13 | public console!: Logging 14 | public readonly appName = 'fdp-play' 15 | 16 | protected async init(): Promise { 17 | this.verbosity = VerbosityLevel.Normal 18 | 19 | if (this.quiet) { 20 | this.verbosity = VerbosityLevel.Quiet 21 | } 22 | 23 | if (this.verbose) { 24 | this.verbosity = VerbosityLevel.Verbose 25 | } 26 | 27 | this.console = new Logging(this.verbosity) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-queen/keys/libp2p_v2.key: -------------------------------------------------------------------------------- 1 | {"address":"04e2d326bf5253d7fccefdfab26af5fe8f7835e16bf4cfe1e19fd56382dc57b3f5f6adb0df6ec77046a2f9dd35abdc5d8f94f8f51f2640476c90d68d621d0f311a","crypto":{"cipher":"aes-128-ctr","ciphertext":"ad666cfd7841108984720d2f1dadbba51d0606b8a25aae63fdd8985d12126980d46fcba2c3e7e97601b0208cf4e0cce67b730a8bc96a8a45f401d1f5590e684c67ac1d019e67cf31107d0fb6b3e63736fb773aa992dbc9beed26b2479eb42ef30aa40dff2b67b6001627341615513c7b7256f641c748007a05","cipherparams":{"iv":"c4d95b4b218593b8c2c8ff359d328fd4"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"1bca95465b61eaa367c6841b6ebcee1172d01cc4abba341d577450294cf425f9"},"mac":"43c632557a1d463b5e68cfc78a9296dab7d4a3be3a70f15d39054fe152de9123"},"version":3,"id":"fa45aa8e-d6c3-440b-82d3-69f3ea387f67"} -------------------------------------------------------------------------------- /.github/workflows/publish-npmjs.yaml: -------------------------------------------------------------------------------- 1 | # After new release is published on github, publish it to npmjs 2 | name: Publish on npmjs 3 | 4 | on: 5 | release: 6 | types: [published] 7 | 8 | jobs: 9 | publish: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - uses: actions/setup-node@v3 14 | with: 15 | node-version: 22 16 | registry-url: 'https://registry.npmjs.org' 17 | - run: npm ci 18 | - run: npm publish --access public 19 | env: 20 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 21 | - uses: apexskier/github-release-commenter@v1 22 | with: 23 | GITHUB_TOKEN: ${{ secrets.REPO_GHA_PAT }} 24 | comment-template: | 25 | Release {release_link} addresses this. 26 | -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-worker-1/keys/libp2p_v2.key: -------------------------------------------------------------------------------- 1 | {"address":"04bff8a223ba80cbc384bf558afdf45b77bd655003a7a30e00407795faac38f7a3b784872bad3c9842999e5bcfa86fba4cf1f6ce725396cf824878b86335ff02c9","crypto":{"cipher":"aes-128-ctr","ciphertext":"759fd4447f7afbae2d17ad2916fe7ebc5e2fa7a29b28693998c5e5189535e91b89d9eca4213796b80a2f294ef4c421591582d4be8eee5944db90df754be05f1b3f7fa05662ed0d603392d6b86b50f6e94f368e63e643e3648eea2e1672b59fdc44e1a36b496da54f7e099536488cba60846e07979dbc525cc1","cipherparams":{"iv":"b7c51b1e7d8d9af8a0e72183bbb70b1b"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"ac832a48c715f7e9b6e8797b2bbda6504bc005b4b37686f806b7b9e2d9327efd"},"mac":"e2b6a5de1a9ddd0159a59ac7299683309c263410b5ddcac13cb43bec734f46c0"},"version":3,"id":"33dd2d55-4c29-4004-82d5-f02aa1f8abf5"} -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-worker-2/keys/libp2p_v2.key: -------------------------------------------------------------------------------- 1 | {"address":"0475cf9bfdbee7d9ee53a0014592d8d1bd5aae48d42493bd9aa7b0818d76773bd1598566b3274b7237c43f306f672946f65dcf3fe92af6d0f8d845279f26d8faa1","crypto":{"cipher":"aes-128-ctr","ciphertext":"d1f139eabb2555dce6020c884bcf575c9f4379b3df9a61979a6d422dc46f0344c8576f965d39edb06c8c0436135669e6e4b573473cfb2628cd8643c6deec5e9ebf0435415cb22eb5101f50bdd2757a6dc01a8c9aa3a345a116bd08808240de03e0f2668c7274e247d0f13491ddc9496b83a53a36a4e68ba489","cipherparams":{"iv":"9e70d8851af9e2bed922bf62d556f38e"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"3ce5acf729f04b9199bce8a916305899781162704c95537be1ffe43af2e46e77"},"mac":"412785444f2cb395573f696ce4c245960887cc430873a12c3628aba856bc0454"},"version":3,"id":"df6f3765-bd8e-4bf6-8cff-723b054e0c5e"} -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-worker-3/keys/libp2p_v2.key: -------------------------------------------------------------------------------- 1 | {"address":"0499ba538e4cb17c7131f84d994cff201466af61ff3d4e5a6ac6fe5691596a929c59a12e6ca4efb07675897f9205e89fc3aa1c70d57e4a102beb6374bd6600f5f2","crypto":{"cipher":"aes-128-ctr","ciphertext":"ba2b4cdd57cfea2eb104decbfbd1afe36dd5ca317f12c8940254c7aa6f0d0a05c122ebbe77cd302a84432f58499fe25951daccf54d4e4e2821ed3e539c4465420963186cfc5f02e22f1c58006c759531f366c27d065c66f40d70efa8ef4fa0109ac1578c3c9aabac8639a80a99f92dd1ddcbc6864ce0446b3e","cipherparams":{"iv":"c55c31f769e99d610c829a35a8b854c2"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"6f14e64a15c451e9153b2efc8500f346648a12d85d3b284308bc0c02edcb3e93"},"mac":"709c749b788949699caa371434befadd704351f463bc57346b193be152ed5e98"},"version":3,"id":"e49c750e-437e-4197-9917-affa3cdf0af3"} -------------------------------------------------------------------------------- /orchestrator/builder/bee-data-dirs/fdp-play-worker-4/keys/libp2p_v2.key: -------------------------------------------------------------------------------- 1 | {"address":"040751bfa32f1303ee72fc4721f04bb918c966fe880b62faff7197fea2469eab3ed484f2dbe36238e2c4ae844c084186a7f2ae15ccd0d5f1b3c10e43b9be99f79d","crypto":{"cipher":"aes-128-ctr","ciphertext":"f3c6b766e65ae2d8e3b5d11ef861073ba02c7126d36746d85c293ee56d9a44eb9d851cca75958f8978bf755ac8f23f6f98f2f4678de0cb0686aa5d9a76d5baefe435057e9e659ac09c6eb0265582bc9b571a1382325bc4e904b3f219c411ba6529dffb38fb19cb9122db8f068d1f6792bf0528872f41985209","cipherparams":{"iv":"716fc5142ae9cdbbfce29a120bdd0857"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"7b6b923007495ec76b8a26d3b246f81a9065678a8e5893b4faeb7bd2b06b70bd"},"mac":"2530a320f45cd8edf061ccfe32659a8446b19188df2028004899a4be1e37c868"},"version":3,"id":"0fd6dc48-29f3-4854-ab02-35797f5650f0"} -------------------------------------------------------------------------------- /src/command/eth/balance.ts: -------------------------------------------------------------------------------- 1 | import { Argument, LeafCommand } from 'furious-commander' 2 | import { EthCommand } from './eth-command' 3 | import { utils } from 'ethers' 4 | 5 | export class Balance extends EthCommand implements LeafCommand { 6 | public readonly name = 'balance' 7 | 8 | public readonly description = 'Shows balance under the given ethereum address.' 9 | 10 | @Argument({ 11 | key: 'address', 12 | type: 'string', 13 | description: 'The ethereum address of which balance will be printed.', 14 | required: true, 15 | }) 16 | public address!: string 17 | 18 | public async run(): Promise { 19 | await super.init() 20 | 21 | const amount = await this.provider.getBalance(this.address) 22 | this.console.log(`${utils.formatEther(amount)} ETH`) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * for base image that this project build use "fdp-play-blockchain" 3 | * it will use the latest tag. on image upgrade, either delete the latest locally or force pull 4 | */ 5 | export const DEFAULT_BLOCKCHAIN_IMAGE = 'fairdatasociety/fdp-play-blockchain' 6 | export const FDP_BLOCKCHAIN_IMAGE = 'fairdatasociety/fdp-contracts-blockchain' 7 | export const DEFAULT_BLOCKCHAIN_CONTAINER = 'fdp-play-blockchain' 8 | export const DEFAULT_FAIROS_IMAGE = 'fairdatasociety/fairos-dfs' 9 | export const ENV_ENV_PREFIX_KEY = 'FDP_PLAY_ENV_PREFIX' 10 | export const BLOCKCHAIN_RPC_EP = 'http://localhost:9545' 11 | export const BLOCKCHAIN_NETWORK_ID = 4020 12 | // wallet address of the wallet that holds all ether. 13 | export const BLOCKCHAIN_WALLET_ADDR = '0xCEeE442a149784faa65C35e328CCd64d874F9a02' 14 | -------------------------------------------------------------------------------- /src/command/root-command/printer.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import chalk from 'chalk' 3 | 4 | export const FORMATTED_ERROR = chalk.red.bold('ERROR') 5 | 6 | export const Printer = { 7 | emptyFunction: (): void => { 8 | return 9 | }, 10 | divider: (char = '-'): void => { 11 | console.log(char.repeat(process.stdout.columns)) 12 | }, 13 | error: (message: string, ...args: unknown[]): void => { 14 | console.error(message, ...args) 15 | }, 16 | log: (message: string, ...args: unknown[]): void => { 17 | console.log(message, ...args) 18 | }, 19 | info: (message: string, ...args: unknown[]): void => { 20 | console.log(chalk.dim(message), ...args) 21 | }, 22 | dimFunction: (message: string, ...args: unknown[]): void => { 23 | console.log(chalk.dim(message), ...args) 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /.babelrc.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (api) { 4 | const targets = 'node >=12' 5 | api.cache(true) 6 | api.cacheDirectory = true 7 | 8 | return { 9 | presets: [ 10 | '@babel/preset-typescript', 11 | [ 12 | '@babel/preset-env', 13 | { 14 | corejs: 3, 15 | useBuiltIns: 'entry', 16 | modules: 'commonjs', 17 | bugfixes: true, 18 | targets 19 | } 20 | ] 21 | ], 22 | plugins: [ 23 | ['@babel/plugin-proposal-decorators', { legacy: true }], 24 | '@babel/plugin-proposal-class-properties', 25 | '@babel/plugin-transform-strict-mode', 26 | [ 27 | '@babel/plugin-transform-runtime', 28 | { 29 | helpers: false, 30 | regenerator: true 31 | } 32 | ] 33 | ] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/utils/blockchain/account.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from 'ethers' 2 | import { JsonRpc } from './jsonrpc' 3 | 4 | type ReqSendTransaction = { 5 | // optional when creating new contract 6 | to: string 7 | // in wei 8 | value: string | BigNumber 9 | } 10 | 11 | export class Account { 12 | // FIXME: create GitHub issue about this prettier problem 13 | // prettier-ignore 14 | // eslint-disable-next-line 15 | constructor(private signer: Signer, private rpcClient: JsonRpc) {} 16 | 17 | // returns back the hash of the transaction 18 | async sendTransaction(params: ReqSendTransaction): Promise { 19 | const tx = await this.rpcClient.post({ 20 | method: 'eth_sendTransaction', 21 | params: [ 22 | { 23 | from: this.signer, 24 | to: params.to, 25 | value: params.value, 26 | }, 27 | ], 28 | id: 2, 29 | }) 30 | 31 | return tx.result as string 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /orchestrator/builder/utils/build-image-tag.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Store/get the tag of the Docker image that will be built 3 | 4 | store_custom_tag() { 5 | echo "$1" > "$MY_PATH/$COMMIT_VERSION_TAG_FILENAME" 6 | } 7 | 8 | # Echos the image tag which is defined by the environment or has been extracted from the Bee version command 9 | get_tag() { 10 | COMMIT_VERSION_TAG=$("$MY_PATH/env-variable-value.sh" COMMIT_VERSION_TAG) 11 | if [ "$COMMIT_VERSION_TAG" == 'true' ] ; then 12 | # retrieve from the output of previous store action 13 | cat "$MY_PATH/$COMMIT_VERSION_TAG_FILENAME" 14 | else 15 | BEE_VERSION=$("$MY_PATH/env-variable-value.sh" BEE_VERSION) 16 | echo "$BEE_VERSION" 17 | fi 18 | } 19 | 20 | MY_PATH=$(dirname "$0") 21 | MY_PATH=$( cd "$MY_PATH" && pwd ) 22 | COMMIT_VERSION_TAG_FILENAME=".commit-version-tag" 23 | 24 | if [ "$1" == "set" ] ; then 25 | store_custom_tag "$2" 26 | elif [ "$1" == "get" ] ; then 27 | get_tag 28 | fi 29 | -------------------------------------------------------------------------------- /src/utils/blockchain/api.ts: -------------------------------------------------------------------------------- 1 | import { Account } from './account' 2 | import { JsonRpc } from './jsonrpc' 3 | 4 | export class BlockchainApi { 5 | private rpcClient: JsonRpc 6 | constructor(rpcAddress: string) { 7 | this.rpcClient = new JsonRpc(rpcAddress) 8 | } 9 | 10 | public async chainId(): Promise { 11 | const balance = await this.rpcClient.post({ 12 | method: 'eth_chainId', 13 | id: 0, 14 | }) 15 | 16 | return parseInt(balance.result as string, 16) 17 | } 18 | 19 | /** 20 | * @param address wallet address 21 | * @returns balance in wei 22 | */ 23 | public async getBalance(address: string): Promise { 24 | const balance = await this.rpcClient.post({ 25 | method: 'eth_getBalance', 26 | params: [address, 'latest'], 27 | id: 1, 28 | }) 29 | 30 | return balance.result as string 31 | } 32 | 33 | public getSigner(signer: Signer): Account { 34 | return new Account(signer, this.rpcClient) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/shim/crypto.ts: -------------------------------------------------------------------------------- 1 | import crypto from 'crypto' 2 | 3 | const isNode = typeof process !== 'undefined' && process.versions && process.versions.node !== null 4 | 5 | const getRandomValuesNode = (array: T): T => { 6 | if (!(array instanceof Uint8Array || array instanceof Uint32Array)) { 7 | throw new TypeError('Expected Uint8Array or Uint32Array') 8 | } 9 | 10 | if (array.length > 65536) { 11 | const e = new Error() 12 | e.message = `Failed to execute 'getRandomValues' on 'Crypto': The ArrayBufferView's byte length (${array.length}) exceeds the number of bytes of entropy available via this API (65536).` 13 | e.name = 'QuotaExceededError' 14 | throw e 15 | } 16 | 17 | const bytes = crypto.randomBytes(array.length) 18 | array.set(bytes) 19 | 20 | return array 21 | } 22 | 23 | if (isNode && globalThis) { 24 | if (!globalThis.crypto) { 25 | globalThis.crypto = {} as Crypto 26 | } 27 | 28 | if (!globalThis.crypto.getRandomValues) { 29 | globalThis.crypto.getRandomValues = getRandomValuesNode 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /orchestrator/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import { HardhatUserConfig } from 'hardhat/config' 2 | import '@nomicfoundation/hardhat-toolbox' 3 | import '@nomicfoundation/hardhat-ethers' 4 | 5 | const FDP_PLAY_URL = process.env.FDP_PLAY_URL || 'http://localhost:9545' 6 | 7 | const config: HardhatUserConfig = { 8 | solidity: { 9 | version: '0.8.19', 10 | settings: { 11 | optimizer: { 12 | enabled: true, 13 | runs: 1000, 14 | }, 15 | }, 16 | }, 17 | networks: { 18 | fdpPlay: { 19 | url: 'http://127.0.0.1:9545', 20 | chainId: 4020, 21 | gasPrice: 10000000000, //10 gwei 22 | accounts: ['0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d'], 23 | }, 24 | }, 25 | etherscan: { 26 | apiKey: { 27 | fdpPlay: 'hello_there!', 28 | }, 29 | customChains: [ 30 | { 31 | network: 'fdpPlay', 32 | chainId: 4020, 33 | urls: { 34 | apiURL: `${FDP_PLAY_URL}/api`, 35 | browserURL: `${FDP_PLAY_URL}`, 36 | }, 37 | }, 38 | ], 39 | }, 40 | } 41 | 42 | export default config 43 | -------------------------------------------------------------------------------- /orchestrator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "orchestrator", 3 | "version": "1.0.0", 4 | "description": "deploying smart contracts and manage environment", 5 | "main": "index.js", 6 | "scripts": { 7 | "blockchain:init": "./blockchain/init.sh", 8 | "blockchain:start": "./blockchain/start.sh", 9 | "migrate:contracts": "hardhat run scripts/deploy.ts --network fdpPlay", 10 | "supply": "hardhat run scripts/supply.ts --network fdpPlay", 11 | "build:env": "./builder/build-environment.sh", 12 | "publish:env": "./builder/publish-environment.sh", 13 | "run:env": "./builder/environment.sh" 14 | }, 15 | "keywords": [ 16 | "swarm", 17 | "orchestrator", 18 | "fdp-play" 19 | ], 20 | "author": "Viktor Levente Tóth @nugaon", 21 | "license": "ISC", 22 | "devDependencies": { 23 | "@nomicfoundation/hardhat-ethers": "^3.0.4", 24 | "@nomicfoundation/hardhat-toolbox": "^3.0.0", 25 | "ethers": "^6.7.1", 26 | "hardhat": "^2.18.0", 27 | "ts-node": "^10.9.1", 28 | "typescript": "^5.2.2" 29 | }, 30 | "dependencies": { 31 | "@openzeppelin/contracts": "^4.9.3" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/utils/docker.ts: -------------------------------------------------------------------------------- 1 | import Dockerode from 'dockerode' 2 | import { ENV_ENV_PREFIX_KEY } from '../../src/constants' 3 | import { BatchId, Bee } from '@ethersphere/bee-js' 4 | 5 | export async function findContainer(docker: Dockerode, name: string): Promise { 6 | const containerName = `${process.env[ENV_ENV_PREFIX_KEY]}-${name}` 7 | 8 | return docker.getContainer(containerName).inspect() 9 | } 10 | 11 | export async function sleep(ms: number): Promise { 12 | return new Promise(resolve => setTimeout(() => resolve(), ms)) 13 | } 14 | 15 | export async function waitForUsablePostageStamp(beeDebug: Bee, id: BatchId, timeout = 120_000): Promise { 16 | const TIME_STEP = 1500 17 | for (let time = 0; time < timeout; time += TIME_STEP) { 18 | // it is in a try...catch because after postage creation Bee (1.8.2) does not find stamp immediately somehow 19 | try { 20 | const stamp = await beeDebug.getPostageBatch(id) 21 | 22 | if (stamp.usable) { 23 | return 24 | } 25 | 26 | await sleep(TIME_STEP) 27 | } catch (e) { 28 | await sleep(TIME_STEP) 29 | } 30 | } 31 | 32 | throw new Error('Timeout on waiting for postage stamp to become usable') 33 | } 34 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'master' 7 | pull_request: 8 | branches: 9 | - '**' 10 | jobs: 11 | node-tests: 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | node-version: [16.x, 18.x] 17 | 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v3 21 | with: 22 | fetch-depth: 1 23 | 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v3 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | 29 | ## Try getting the node modules from cache, if failed npm ci 30 | - uses: actions/cache@v3 31 | id: cache-npm 32 | with: 33 | path: node_modules 34 | key: ${{ runner.os }}-node-${{ matrix.node }}-${{ hashFiles('**/package-lock.json') }} 35 | restore-keys: | 36 | ${{ runner.OS }}-node-${{ matrix.node }}-${{ env.cache-name }}- 37 | ${{ runner.OS }}-node-${{ matrix.node }}- 38 | - name: Install npm deps 39 | if: steps.cache-npm.outputs.cache-hit != 'true' 40 | run: npm ci 41 | - name: Run node tests 42 | run: npm test -- --bail true 43 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import { IOption } from 'furious-commander' 2 | import PackageJson from '../package.json' 3 | import { Start } from './command/start' 4 | import { Stop } from './command/stop' 5 | import { Logs } from './command/logs' 6 | import { Eth } from './command/eth' 7 | 8 | export const quiet: IOption = { 9 | key: 'quiet', 10 | alias: 'q', 11 | description: 'Does not print anything', 12 | type: 'boolean', 13 | default: false, 14 | conflicts: 'verbose', 15 | } 16 | 17 | export const verbose: IOption = { 18 | key: 'verbose', 19 | alias: 'v', 20 | description: 'Display logs', 21 | type: 'boolean', 22 | default: false, 23 | conflicts: 'quiet', 24 | } 25 | 26 | export const help: IOption = { 27 | key: 'help', 28 | alias: 'h', 29 | description: 'Print context specific help and exit', 30 | type: 'boolean', 31 | default: false, 32 | } 33 | 34 | export const version: IOption = { 35 | key: 'version', 36 | alias: 'V', 37 | description: 'Print version and exit', 38 | type: 'boolean', 39 | default: false, 40 | handler: () => { 41 | process.stdout.write(PackageJson.version + '\n') 42 | }, 43 | } 44 | 45 | export const optionParameters: IOption[] = [quiet, verbose, help, version] 46 | 47 | export const rootCommandClasses = [Start, Stop, Logs, Eth] 48 | -------------------------------------------------------------------------------- /.github/workflows/check.yaml: -------------------------------------------------------------------------------- 1 | name: Check 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'master' 7 | pull_request: 8 | branches: 9 | - '**' 10 | 11 | jobs: 12 | check: 13 | runs-on: ubuntu-latest 14 | 15 | strategy: 16 | matrix: 17 | node-version: [18.x] 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | with: 22 | fetch-depth: 0 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v3 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | 28 | ## Try getting the node modules from cache, if failed npm ci 29 | - uses: actions/cache@v3 30 | id: cache-npm 31 | with: 32 | path: node_modules 33 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 34 | restore-keys: | 35 | ${{ runner.OS }}-node-${{ env.cache-name }}- 36 | ${{ runner.OS }}-node- 37 | ${{ runner.OS }}- 38 | - name: Install npm deps 39 | if: steps.cache-npm.outputs.cache-hit != 'true' 40 | run: npm ci 41 | 42 | - name: Commit linting 43 | uses: wagoid/commitlint-github-action@v5 44 | 45 | - name: Code linting 46 | run: npm run lint:check 47 | env: 48 | CI: true 49 | 50 | - name: Types check 51 | run: npm run types:check 52 | 53 | - name: Build nodejs code 54 | run: npm run build 55 | -------------------------------------------------------------------------------- /orchestrator/builder/bee-cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -o errexit 3 | set -o pipefail 4 | 5 | echo "Removing 'localstore' and 'statestore' folders from Bee datadirs..." 6 | echo "You may need to pass your password for sudo permission to remove the bee-data folders" 7 | 8 | MY_PATH=$(dirname "$0") 9 | MY_PATH=$( cd "$MY_PATH" && pwd ) 10 | BEE_DIRS=$(ls -d $MY_PATH/bee-data-dirs/*/) 11 | for BEE_DIR in $BEE_DIRS 12 | do 13 | echo "$BEE_DIR" 14 | rm -rf "$BEE_DIR/localstore" 15 | rm -rf "$BEE_DIR/statestore" 16 | rm -rf "$BEE_DIR/kademlia-metrics" 17 | rm -rf "$BEE_DIR/stamperstore" 18 | done 19 | 20 | echo "Removing stopped Bee Docker containers..." 21 | docker container prune -f 22 | 23 | echo "Removing built Bee Docker images..." 24 | 25 | BEE_VERSION=$("$MY_PATH/utils/build-image-tag.sh" get) 26 | BEE_ENV_PREFIX=$("$MY_PATH/utils/env-variable-value.sh" BEE_ENV_PREFIX) 27 | BEE_IMAGE_PREFIX=$("$MY_PATH/utils/env-variable-value.sh" BEE_IMAGE_PREFIX) 28 | DOCKER_IMAGES=$(docker image ls -qaf reference="$BEE_IMAGE_PREFIX/$BEE_ENV_PREFIX*:$BEE_VERSION") 29 | for DOCKER_IMAGE in $DOCKER_IMAGES 30 | do 31 | echo "$DOCKER_IMAGE" 32 | docker image rm "$DOCKER_IMAGE" 33 | done 34 | 35 | echo "Removing built Blockchain Docker image..." 36 | BLOCKCHAIN_DOCKER_IMAGE=$(docker image ls -qaf reference="$BEE_IMAGE_PREFIX/$BEE_ENV_PREFIX-blockchain:$BLOCKCHAIN_VERSION") 37 | 38 | if [ -n "$BLOCKCHAIN_DOCKER_IMAGE" ] ; then 39 | docker image rm "$BLOCKCHAIN_DOCKER_IMAGE" 40 | fi 41 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * For a detailed explanation regarding each configuration property and type check, visit: 3 | * https://jestjs.io/docs/en/configuration.html 4 | */ 5 | import type { Config } from '@jest/types' 6 | 7 | export default async (): Promise => { 8 | return { 9 | // Indicates whether the coverage information should be collected while executing the test 10 | // collectCoverage: false, 11 | 12 | // The directory where Jest should output its coverage files 13 | coverageDirectory: 'coverage', 14 | 15 | // An array of regexp pattern strings used to skip coverage collection 16 | coveragePathIgnorePatterns: ['/node_modules/'], 17 | 18 | // An array of directory names to be searched recursively up from the requiring module's location 19 | moduleDirectories: ['node_modules'], 20 | 21 | // Run tests from one or more projects 22 | projects: [ 23 | { 24 | displayName: 'node', 25 | testEnvironment: 'node', 26 | testRegex: 'test/.*\\.spec\\.ts', 27 | }, 28 | ] as unknown[] as string[], // bad types 29 | 30 | // The root directory that Jest should scan for tests and modules within 31 | rootDir: 'test', 32 | // Increase timeout since we are spinning Bee containers 33 | testTimeout: 7 * 60 * 1000, 34 | 35 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 36 | testPathIgnorePatterns: ['/node_modules/'], 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/command/root-command/logging.ts: -------------------------------------------------------------------------------- 1 | import { Printer } from './printer' 2 | 3 | export enum VerbosityLevel { 4 | /** No output message, only at errors or result strings (e.g. hash of uploaded file) */ 5 | Quiet, 6 | /** Formatted informal messages at end of operations, output row number is equal at same operations */ 7 | Normal, 8 | /** dim messages, gives info about state of the operation frequently. Default */ 9 | Verbose, 10 | } 11 | 12 | type PrinterFnc = (message: string, ...args: unknown[]) => void 13 | 14 | export class Logging { 15 | public readonly verbosityLevel: VerbosityLevel 16 | // Callable logging functions (instead of console.log) 17 | 18 | /** Error messages */ 19 | public error: PrinterFnc 20 | /** Identical with console.log */ 21 | public log: PrinterFnc 22 | /** Informal messages (e.g. Tips) */ 23 | public info: PrinterFnc 24 | 25 | constructor(verbosityLevel: VerbosityLevel) { 26 | this.verbosityLevel = verbosityLevel 27 | switch (verbosityLevel) { 28 | case VerbosityLevel.Verbose: 29 | this.error = Printer.error 30 | this.log = Printer.log 31 | this.info = Printer.info 32 | break 33 | case VerbosityLevel.Normal: 34 | this.error = Printer.error 35 | this.log = Printer.log 36 | this.info = Printer.emptyFunction 37 | break 38 | default: 39 | // quiet 40 | this.error = Printer.error 41 | this.log = Printer.emptyFunction 42 | this.info = Printer.emptyFunction 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 The Fair Data Protocol Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Swarm nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /src/utils/error.ts: -------------------------------------------------------------------------------- 1 | import { printer } from '../printer' 2 | import chalk from 'chalk' 3 | 4 | const FORMATTED_ERROR = chalk.red.bold('ERROR') 5 | 6 | export class CommandLineError extends Error { 7 | public readonly type = 'CommandLineError' 8 | } 9 | 10 | /** 11 | * Thrown when the error is not related to Bee/network 12 | */ 13 | export class TimeoutError extends CommandLineError {} 14 | 15 | export class ContainerImageConflictError extends CommandLineError { 16 | existingContainersImage: string 17 | newContainersImage: string 18 | 19 | constructor(message: string, existingContainersImage: string, newContainersImage: string) { 20 | super(message) 21 | this.existingContainersImage = existingContainersImage 22 | this.newContainersImage = newContainersImage 23 | } 24 | } 25 | 26 | function getFieldOrNull(some: unknown, key: string): T | null { 27 | return typeof some === 'object' && some !== null ? Reflect.get(some, key) : null 28 | } 29 | 30 | export function errorHandler(error: unknown): void { 31 | if (!process.exitCode) { 32 | process.exitCode = 1 33 | } 34 | // grab error.message, or error if it is a string 35 | const message: string | null = typeof error === 'string' ? error : getFieldOrNull(error, 'message') 36 | 37 | if (message) { 38 | printer.printError(FORMATTED_ERROR + ' ' + message) 39 | } else { 40 | printer.printError(FORMATTED_ERROR + ' The command failed, but there is no error message available.') 41 | printer.printError('') 42 | printer.printError('There may be additional information in the Blockhain, Bee or FairOS logs.') 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/utils/console-log.ts: -------------------------------------------------------------------------------- 1 | type describeFunctionArgs = { 2 | consoleMessages: string[] 3 | getNthLastMessage: (n: number) => string 4 | getLastMessage: () => string 5 | hasMessageContaining: (substring: string) => boolean 6 | } 7 | 8 | export function describeCommand(description: string, func: (clauseFields: describeFunctionArgs) => void): void { 9 | describe(description, () => { 10 | const consoleMessages: string[] = [] 11 | const getNthLastMessage = (n: number) => consoleMessages[consoleMessages.length - n] 12 | const getLastMessage = () => consoleMessages[consoleMessages.length - 1] 13 | const hasMessageContaining = (substring: string) => 14 | Boolean(consoleMessages.find(consoleMessage => consoleMessage.includes(substring))) 15 | 16 | global.console.log = jest.fn(message => { 17 | consoleMessages.push(message) 18 | }) 19 | global.console.error = jest.fn(message => { 20 | consoleMessages.push(message) 21 | }) 22 | 23 | global.process.stdout.write = jest.fn(message => { 24 | if (typeof message === 'string') { 25 | consoleMessages.push(message) 26 | } else { 27 | consoleMessages.push(new TextDecoder().decode(message)) 28 | } 29 | 30 | return true 31 | }) 32 | 33 | jest.spyOn(process, 'exit').mockImplementation(() => { 34 | throw new Error('process.exit() was called') 35 | }) 36 | 37 | jest.spyOn(global.console, 'warn') 38 | 39 | beforeEach(() => { 40 | consoleMessages.length = 0 41 | }) 42 | 43 | func({ consoleMessages, getNthLastMessage, getLastMessage, hasMessageContaining }) 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /DISCLAIMER: -------------------------------------------------------------------------------- 1 | # DISCLAIMER 2 | Account integrity persistence and security are not assured. Expect that funds 3 | used for account might be lost, as well as any data the account uses. 4 | 5 | Use of this service is limited only to **non-sensitive and publicly available 6 | data**. Users must not use, share, or store any kind of sensitive data like 7 | health status, provision or payment of healthcare, Personally Identifiable 8 | Information (PII) and/or Protected Health Information (PHI), etc. under **ANY** 9 | circumstance. 10 | 11 | Administrators for this service can not moderate any information 12 | used, shared, or stored with this service at any time. Any user that cannot 13 | abide by this disclaimer and Code of Conduct must refrain from using this service. 14 | 15 | The material embodied in this software is provided to you "as-is" and without 16 | warranty of any kind, express, implied or otherwise, including without 17 | limitation, any warranty of fitness for a particular purpose. In no event shall 18 | the any government be liable to you or anyone else for any direct, special, incidental, 19 | indirect or consequential damages of any kind, or any damages whatsoever, 20 | including without limitation, loss of profit, loss of use, savings or revenue, 21 | or the claims of third parties, whether or not any government has 22 | been advised of the possibility of such loss, however caused and on any theory 23 | of liability, arising out of or in connection with the possession, use or 24 | performance of this software. 25 | 26 | 27 | # Disclaimer of Endorsement 28 | Reference herein to any specific commercial product, process, or service by trade 29 | name, trademark, manufacturer, or otherwise, in this Work does not constitute an 30 | endorsement, recommendation, or favoring by the any Government and 31 | shall not be used for advertising or product endorsement purposes. 32 | -------------------------------------------------------------------------------- /orchestrator/builder/blockchain/genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | "chainId": 4020, 4 | "homesteadBlock": 0, 5 | "eip150Block": 0, 6 | "eip155Block": 0, 7 | "eip158Block": 0, 8 | "byzantiumBlock": 0, 9 | "constantinopleBlock": 0, 10 | "petersburgBlock": 0, 11 | "istanbulBlock": 0, 12 | "berlinBlock": 0, 13 | "londonBlock": 0, 14 | "clique": { 15 | "period": 5, 16 | "epoch": 30000 17 | } 18 | }, 19 | "alloc": { 20 | "0xCEeE442a149784faa65C35e328CCd64d874F9a02": { 21 | "balance": "10000000000000000000000" 22 | }, 23 | "90F8bf6A479f320ead074411a4B0e7944Ea8c9C1": { 24 | "balance": "10000000000000000000000" 25 | }, 26 | "FFcf8FDEE72ac11b5c542428B35EEF5769C409f0": { 27 | "balance": "10000000000000000000000" 28 | }, 29 | "22d491Bde2303f2f43325b2108D26f1eAbA1e32b": { 30 | "balance": "10000000000000000000000" 31 | }, 32 | "E11BA2b4D45Eaed5996Cd0823791E0C93114882d": { 33 | "balance": "10000000000000000000000" 34 | }, 35 | "d03ea8624C8C5987235048901fB614fDcA89b117": { 36 | "balance": "10000000000000000000000" 37 | }, 38 | "95cED938F7991cd0dFcb48F0a06a40FA1aF46EBC": { 39 | "balance": "10000000000000000000000" 40 | }, 41 | "3E5e9111Ae8eB78Fe1CC3bb8915d5D461F3Ef9A9": { 42 | "balance": "10000000000000000000000" 43 | }, 44 | "28a8746e75304c0780E011BEd21C72cD78cd535E": { 45 | "balance": "10000000000000000000000" 46 | }, 47 | "ACa94ef8bD5ffEE41947b4585a84BdA5a3d3DA6E": { 48 | "balance": "10000000000000000000000" 49 | }, 50 | "1dF62f291b2E969fB0849d99D9Ce41e2F137006e": { 51 | "balance": "10000000000000000000000" 52 | } 53 | }, 54 | "difficulty": "1", 55 | "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000CEeE442a149784faa65C35e328CCd64d874F9a020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 56 | "gasLimit": "0x1C9C380" 57 | } 58 | -------------------------------------------------------------------------------- /src/command/eth/send.ts: -------------------------------------------------------------------------------- 1 | import { LeafCommand, Option } from 'furious-commander' 2 | import * as ethers from 'ethers' 3 | import { EthCommand } from './eth-command' 4 | import { isNumberString } from '../../utils/types' 5 | import { CommandLineError } from '../../utils/error' 6 | import { BLOCKCHAIN_WALLET_ADDR } from '../../constants' 7 | 8 | export class Send extends EthCommand implements LeafCommand { 9 | public readonly name = 'send' 10 | 11 | public readonly description = 'Send ethers from one unlocked account.' 12 | 13 | @Option({ 14 | key: 'amount', 15 | alias: 'a', 16 | type: 'string', 17 | description: 'How many ethers should be sent to the recipient.', 18 | required: true, 19 | }) 20 | public amount!: string 21 | 22 | @Option({ 23 | key: 'to', 24 | type: 'string', 25 | description: 'The Ethereum address of the recipient.', 26 | required: true, 27 | }) 28 | public to!: string 29 | 30 | @Option({ 31 | key: 'from', 32 | type: 'string', 33 | description: 'The wallet address from which the coins will be sent.', 34 | default: BLOCKCHAIN_WALLET_ADDR, 35 | }) 36 | public from!: string 37 | 38 | public async run(): Promise { 39 | await super.init() 40 | 41 | const account = this.provider.getSigner(this.from) 42 | 43 | if (!account) { 44 | throw new CommandLineError(`There is no account on index ${this.from}.`) 45 | } 46 | 47 | if (!isNumberString(this.amount)) { 48 | throw new CommandLineError(`Given value ${this.amount} is not a valid`) 49 | } 50 | 51 | const tx = { 52 | to: this.to, 53 | value: '0x' + BigInt(ethers.utils.parseEther(this.amount).toString()).toString(16), 54 | } 55 | 56 | try { 57 | const response = await account.sendTransaction(tx) 58 | this.console.info(`${this.amount} ETH has been sent to ${this.to}.`) 59 | this.console.log(`TxId: ${response}`) 60 | } catch (e) { 61 | const errMessage = (e as Error).message || e 62 | throw new CommandLineError(`Cannot execute transaction: ${errMessage}`) 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /orchestrator/builder/publish-environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | 6 | MY_PATH=$(dirname "$0") 7 | MY_PATH=$( cd "$MY_PATH" && pwd ) 8 | 9 | STATE_COMMIT=$("$MY_PATH/utils/env-variable-value.sh" STATE_COMMIT) 10 | BUILD_IMAGE=$("$MY_PATH/utils/env-variable-value.sh" BUILD_IMAGE) 11 | BLOCKCHAIN_VERSION=$("$MY_PATH/utils/env-variable-value.sh" BLOCKCHAIN_VERSION) 12 | 13 | if [ $BUILD_IMAGE == 'true' ] ; then 14 | # Necessary for fetch BEE_VERSION from .commit-version-tag 15 | export COMMIT_VERSION_TAG='true' 16 | fi 17 | 18 | BEE_VERSION=$("$MY_PATH/utils/build-image-tag.sh" get) 19 | 20 | if [ $STATE_COMMIT == 'true' ] ; then 21 | BLOCKCHAIN_VERSION+="-for-$BEE_VERSION" 22 | fi 23 | 24 | BEE_ENV_PREFIX=$("$MY_PATH/utils/env-variable-value.sh" BEE_ENV_PREFIX) 25 | BEE_IMAGE_PREFIX=$("$MY_PATH/utils/env-variable-value.sh" BEE_IMAGE_PREFIX) 26 | BLOCKCHAIN_NAME="$BEE_ENV_PREFIX-blockchain" 27 | BLOCKCHAIN_IMAGE_NAME="$BEE_IMAGE_PREFIX/$BLOCKCHAIN_NAME:$BLOCKCHAIN_VERSION" 28 | 29 | LATEST=false 30 | # handle passed options 31 | while [ $# -gt 0 ] 32 | do 33 | case "$1" in 34 | --latest) 35 | LATEST=true 36 | shift 1 37 | ;; 38 | *) 39 | echoerr "Unknown argument: $1" 40 | usage 41 | ;; 42 | esac 43 | done 44 | 45 | echo "Search Docker built images with the following parameters: $BEE_IMAGE_PREFIX/$BEE_ENV_PREFIX*:$BEE_VERSION" 46 | DOCKER_IMAGES=$(docker image ls --format "{{.Repository}}:{{.Tag}}" -af reference="$BEE_IMAGE_PREFIX/$BEE_ENV_PREFIX*:$BEE_VERSION") 47 | echo "Push Bee docker images: $DOCKER_IMAGES" 48 | for DOCKER_IMAGE in $DOCKER_IMAGES 49 | do 50 | echo "$DOCKER_IMAGE" 51 | docker push "$DOCKER_IMAGE" 52 | 53 | if $LATEST ; then 54 | docker tag "$DOCKER_IMAGE" "${DOCKER_IMAGE%:*}:latest" 55 | docker push "${DOCKER_IMAGE%:*}:latest" 56 | fi 57 | done 58 | 59 | echo "Push Blockchain docker image: $BLOCKCHAIN_IMAGE_NAME" 60 | docker push "$BLOCKCHAIN_IMAGE_NAME" 61 | 62 | if $LATEST ; then 63 | docker tag "$BLOCKCHAIN_IMAGE_NAME" "${BLOCKCHAIN_IMAGE_NAME%:*}:latest" 64 | docker push "${BLOCKCHAIN_IMAGE_NAME%:*}:latest" 65 | fi 66 | -------------------------------------------------------------------------------- /src/command/stop.ts: -------------------------------------------------------------------------------- 1 | import { LeafCommand, Option } from 'furious-commander' 2 | import { RootCommand } from './root-command' 3 | import { DEFAULT_ENV_PREFIX, DEFAULT_BEE_IMAGE_PREFIX, Docker } from '../utils/docker' 4 | import ora from 'ora' 5 | import { VerbosityLevel } from './root-command/logging' 6 | import { DEFAULT_BLOCKCHAIN_IMAGE, ENV_ENV_PREFIX_KEY } from '../constants' 7 | 8 | export class Stop extends RootCommand implements LeafCommand { 9 | public readonly name = 'stop' 10 | 11 | public readonly description = 'Stops the Bee Factory cluster' 12 | 13 | @Option({ 14 | key: 'env-prefix', 15 | type: 'string', 16 | description: "Docker container's names prefix", 17 | envKey: ENV_ENV_PREFIX_KEY, 18 | default: DEFAULT_ENV_PREFIX, 19 | }) 20 | public envPrefix!: string 21 | 22 | @Option({ 23 | key: 'blockchain-image', 24 | type: 'string', 25 | description: 'Docker image name of the used blockchain', 26 | envKey: 'FDP_PLAY_BLOCKCHAIN_IMAGE', 27 | default: DEFAULT_BLOCKCHAIN_IMAGE, 28 | }) 29 | public blockchainImageName!: string 30 | 31 | @Option({ 32 | key: 'bee-image-prefix', 33 | type: 'string', 34 | description: 'Docker bee image name prefix', 35 | envKey: 'FACTORY_DOCKER_PREFIX', 36 | default: DEFAULT_BEE_IMAGE_PREFIX, 37 | }) 38 | public beeImagePrefix!: string 39 | 40 | @Option({ 41 | key: 'rm', 42 | type: 'boolean', 43 | description: 'Remove the containers', 44 | }) 45 | public deleteContainers!: boolean 46 | 47 | public async run(): Promise { 48 | await super.init() 49 | 50 | const docker = new Docker({ 51 | console: this.console, 52 | envPrefix: this.envPrefix, 53 | beeImagePrefix: this.beeImagePrefix, 54 | blockchainImageName: this.blockchainImageName, 55 | }) 56 | 57 | const dockerSpinner = ora({ 58 | text: 'Stopping all containers...', 59 | spinner: 'point', 60 | color: 'yellow', 61 | isSilent: this.verbosity === VerbosityLevel.Quiet, 62 | }).start() 63 | 64 | await docker.stopAll(true, this.deleteContainers) 65 | 66 | dockerSpinner.succeed('Containers stopped') 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /test/integration/eth.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import crypto from 'crypto' 3 | import * as ethers from 'ethers' 4 | 5 | import { run } from '../utils/run' 6 | import { ENV_ENV_PREFIX_KEY } from '../../src/constants' 7 | import { describeCommand } from '../utils/console-log' 8 | import { sleep } from '../../src/utils' 9 | 10 | describeCommand('stop command', ({ getLastMessage }) => { 11 | const envPrefix = `bee-play-test-${crypto.randomBytes(4).toString('hex')}` 12 | 13 | beforeAll(async () => { 14 | // This will force Bee Factory to create 15 | process.env[ENV_ENV_PREFIX_KEY] = envPrefix 16 | 17 | // As spinning the cluster with --detach the command will exit once the cluster is up and running 18 | await run(['start', '--detach', '--without-bees']) 19 | }) 20 | 21 | afterAll(async () => { 22 | await run(['stop', '--rm']) // Cleanup the testing containers 23 | }) 24 | 25 | it('should send ether and update balance', async () => { 26 | const wallet = ethers.Wallet.createRandom() 27 | const blockTime = 6000 // more than one block time in fdp-play 28 | 29 | await run(['eth', 'balance', wallet.address]) 30 | expect(getLastMessage()).toBe('0.0 ETH') 31 | 32 | await run(['eth', 'send', '--to', wallet.address, '--amount', '0.1']) 33 | expect(getLastMessage()).toContain('TxId') 34 | 35 | await sleep(blockTime) 36 | 37 | await run(['eth', 'balance', wallet.address]) 38 | expect(getLastMessage()).toBe('0.1 ETH') 39 | 40 | await run(['eth', 'send', '--to', wallet.address, '--amount', '0.9']) 41 | expect(getLastMessage()).toContain('TxId') 42 | 43 | await sleep(blockTime) 44 | 45 | await run(['eth', 'balance', wallet.address]) 46 | expect(getLastMessage()).toBe('1.0 ETH') 47 | 48 | await run(['eth', 'send', '--to', wallet.address, '--amount', '100000000000000']) 49 | expect(getLastMessage()).toContain('jsonrpc: -32000 - insufficient funds for transfer') 50 | 51 | await run(['eth', 'send', '--to', '0xD293493418172', '--amount', '100000000000000']) 52 | expect(getLastMessage()).toContain('jsonrpc: -32602') 53 | 54 | await run(['eth', 'balance', '0xD293493418172']) 55 | expect(getLastMessage()).toContain('jsonrpc: -32602') 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /orchestrator/contracts/SwapPriceOracle.bytecode: -------------------------------------------------------------------------------- 1 | 0x608060405234801561001057600080fd5b506040516104b83803806104b883398101604081905261002f9161007e565b600080546001600160a01b031916339081178255604051909182917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3506001919091556002556100a1565b60008060408385031215610090578182fd5b505080516020909101519092909150565b610408806100b06000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806398d5fdca1161005b57806398d5fdca146100dd578063a035b1fe146100f8578063dd4899321461010f578063f2fde38b1461011857600080fd5b8063710f2dd11461008d578063715018a6146100a25780638d6cc56d146100aa5780638da5cb5b146100bd575b600080fd5b6100a061009b366004610385565b61012b565b005b6100a061019a565b6100a06100b8366004610385565b61020e565b6000546040516001600160a01b0390911681526020015b60405180910390f35b600154600254604080519283526020830191909152016100d4565b61010160015481565b6040519081526020016100d4565b61010160025481565b6100a0610126366004610357565b61026d565b6000546001600160a01b0316331461015e5760405162461bcd60e51b81526004016101559061039d565b60405180910390fd5b60028190556040518181527f0f45948e42a1e34f851df8452200e744563dae2029a99b8178ff6530458bd3df906020015b60405180910390a150565b6000546001600160a01b031633146101c45760405162461bcd60e51b81526004016101559061039d565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6000546001600160a01b031633146102385760405162461bcd60e51b81526004016101559061039d565b60018190556040518181527fae46785019700e30375a5d7b4f91e32f8060ef085111f896ebf889450aa2ab5a9060200161018f565b6000546001600160a01b031633146102975760405162461bcd60e51b81526004016101559061039d565b6001600160a01b0381166102fc5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610155565b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b600060208284031215610368578081fd5b81356001600160a01b038116811461037e578182fd5b9392505050565b600060208284031215610396578081fd5b5035919050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260408201526060019056fea264697066735822122069737c178b79be7973d71a433150ff2571d211a983a7f5a5ba0f7dba3d57fd7664736f6c63430008040033 -------------------------------------------------------------------------------- /orchestrator/builder/bee-docker-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | 6 | dockerfile() { 7 | cat << DOCKERFILE > "$1" 8 | FROM ethersphere/bee:$2 9 | 10 | # Sample docker file 11 | COPY --chown=bee:bee . /home/bee/.bee 12 | DOCKERFILE 13 | } 14 | 15 | dockerbuild() { 16 | BLOCKCHAIN_VERSION=$("$MY_PATH/utils/env-variable-value.sh" BLOCKCHAIN_VERSION) 17 | IMAGE_NAME=$(basename "$1") 18 | IMAGE_NAME="$4/$IMAGE_NAME" 19 | docker build "$1" --no-cache -f "$2" -t "$IMAGE_NAME:$3" --label "org.fairdatasociety.fdp-play.blockchain-version=$BLOCKCHAIN_VERSION" 20 | } 21 | 22 | MY_PATH=$(dirname "$0") 23 | MY_PATH=$( cd "$MY_PATH" && pwd ) 24 | BEE_DIRS=$(ls -d "$MY_PATH"/bee-data-dirs/*/) 25 | BEE_VERSION=$("$MY_PATH/utils/env-variable-value.sh" BEE_VERSION) 26 | BEE_IMAGE_PREFIX=$("$MY_PATH/utils/env-variable-value.sh" BEE_IMAGE_PREFIX) 27 | STATE_COMMIT=$("$MY_PATH/utils/env-variable-value.sh" STATE_COMMIT) 28 | OFFICIAL_BEE_IMAGE="ethersphere/bee:$BEE_VERSION" 29 | 30 | # Make sure we the user has permission all the files 31 | echo "Build Bee Docker images..." 32 | echo "Update common dockerfile" 33 | dockerfile "$MY_PATH/bee-data-dirs/Dockerfile" "$BEE_VERSION" 34 | 35 | ### BEE_VERSION ALTERNATIONS START 36 | 37 | # If the user has been set the COMMIT_VERSION_TAG env variable 38 | # The image will be built with the tag that is the bee version string 39 | COMMIT_VERSION_TAG="$("$MY_PATH/utils/env-variable-value.sh" COMMIT_VERSION_TAG)" 40 | if [ "$COMMIT_VERSION_TAG" == "true" ] ; then 41 | echo "Image version tag will be extracted from the bee version command from image $OFFICIAL_BEE_IMAGE" 42 | docker pull $OFFICIAL_BEE_IMAGE 43 | # somehow the version command's output goes to the stderr 44 | BEE_VERSION=$(docker run --rm $OFFICIAL_BEE_IMAGE version 2>&1) 45 | echo "Extracted Bee version: $BEE_VERSION" 46 | "$MY_PATH/utils/build-image-tag.sh" set "$BEE_VERSION" 47 | fi 48 | 49 | if [ "$STATE_COMMIT" == 'false' ] ; then 50 | echo "The bee image will be built without their state" 51 | "$MY_PATH/utils/build-image-tag.sh" set "$BEE_VERSION" 52 | echo "Stateless Bee version: $BEE_VERSION" 53 | fi 54 | 55 | ### BEE_VERSION ALERNATIONS END 56 | 57 | echo "Build Dockerfiles" 58 | for BEE_DIR in $BEE_DIRS 59 | do 60 | echo "Build Bee version $BEE_VERSION on $BEE_DIR" 61 | dockerbuild "$BEE_DIR" "$MY_PATH/bee-data-dirs/Dockerfile" "$BEE_VERSION" "$BEE_IMAGE_PREFIX" 62 | done 63 | 64 | echo "Docker image builds were successful!" 65 | -------------------------------------------------------------------------------- /src/command/logs.ts: -------------------------------------------------------------------------------- 1 | import { Argument, LeafCommand, Option } from 'furious-commander' 2 | import { RootCommand } from './root-command' 3 | import { ContainerType, DEFAULT_ENV_PREFIX, DEFAULT_BEE_IMAGE_PREFIX, Docker } from '../utils/docker' 4 | import { DEFAULT_BLOCKCHAIN_IMAGE } from '../constants' 5 | 6 | export class Logs extends RootCommand implements LeafCommand { 7 | public readonly name = 'logs' 8 | 9 | public readonly description = `Prints logs for given container. Valid container's names are: ${Object.values( 10 | ContainerType, 11 | ).join(', ')}` 12 | 13 | @Option({ 14 | key: 'bee-image-prefix', 15 | type: 'string', 16 | description: 'Docker bee image name prefix', 17 | envKey: 'FACTORY_IMAGE_PREFIX', 18 | default: DEFAULT_BEE_IMAGE_PREFIX, 19 | }) 20 | public beeImagePrefix!: string 21 | 22 | @Option({ 23 | key: 'env-prefix', 24 | type: 'string', 25 | description: "Docker container's names prefix", 26 | envKey: 'FACTORY_ENV_PREFIX', 27 | default: DEFAULT_ENV_PREFIX, 28 | }) 29 | public envPrefix!: string 30 | 31 | @Option({ 32 | key: 'blockchain-image', 33 | type: 'string', 34 | description: 'Docker image name of the used blockchain', 35 | envKey: 'FDP_PLAY_BLOCKCHAIN_IMAGE', 36 | default: DEFAULT_BLOCKCHAIN_IMAGE, 37 | }) 38 | public blockchainImageName!: string 39 | 40 | @Option({ 41 | key: 'follow', 42 | alias: 'f', 43 | type: 'boolean', 44 | description: 'Stays attached to the container and output any new logs.', 45 | default: false, 46 | }) 47 | public follow!: boolean 48 | 49 | @Option({ 50 | key: 'tail', 51 | alias: 't', 52 | type: 'number', 53 | description: 'Prints specified number of last log lines.', 54 | }) 55 | public tail!: number 56 | 57 | @Argument({ key: 'container', description: 'Container name as described above', required: true }) 58 | public container!: ContainerType 59 | 60 | public async run(): Promise { 61 | await super.init() 62 | 63 | if (!Object.values(ContainerType).includes(this.container)) { 64 | throw new Error(`Passed container name is not valid! Valid values: ${Object.values(ContainerType).join(', ')}`) 65 | } 66 | 67 | const docker = new Docker({ 68 | console: this.console, 69 | envPrefix: this.envPrefix, 70 | beeImagePrefix: this.beeImagePrefix, 71 | blockchainImageName: this.blockchainImageName, 72 | }) 73 | await docker.logs(this.container, process.stdout, this.follow, this.tail) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /orchestrator/builder/build-environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | 6 | usage() { 7 | cat << USAGE >&2 8 | USAGE: 9 | $ build-environment.sh [PARAMETERS] 10 | PARAMETERS: 11 | --bee-repository Custom repository for bee images; Default: ethersphere/bee.git 12 | --build-base-bee The base bee image will be built from source code 13 | --base-bee-commit-hash=string the source code commit hash of the base bee; Default: HEAD; Dependency: --build-base-bee 14 | USAGE 15 | exit 1 16 | } 17 | 18 | echoerr() { 19 | >&2 echo "$@" 20 | } 21 | 22 | build_bee() { 23 | # Clone source code 24 | BEE_SOURCE_PATH=$MY_PATH/../bee 25 | if [ -d "$BEE_SOURCE_PATH" ] ; then 26 | rm -rf "$BEE_SOURCE_PATH" 27 | fi 28 | mkdir "$BEE_SOURCE_PATH" && cd "$BEE_SOURCE_PATH" || exit 1 29 | git init 30 | git remote add origin https://github.com/$BEE_REPOSITORY 31 | git fetch origin --depth=1 "$COMMIT_HASH" 32 | git reset --hard FETCH_HEAD 33 | # Build bee and make docker image 34 | export BEE_VERSION=${COMMIT_HASH::7}-commit 35 | export REACHABILITY_OVERRIDE_PUBLIC=true 36 | echo "Bee image will be built with version: $BEE_VERSION" 37 | docker build . \ 38 | --tag ethersphere/bee:$BEE_VERSION \ 39 | --build-arg REACHABILITY_OVERRIDE_PUBLIC=$REACHABILITY_OVERRIDE_PUBLIC \ 40 | --file Dockerfile.dev 41 | cd "$MY_PATH" || exit 1 42 | # Set build image tag so that other terminal session can retrieve 43 | "$MY_PATH/utils/build-image-tag.sh" set "$BEE_VERSION" 44 | } 45 | 46 | MY_PATH=$(dirname "$0") 47 | MY_PATH=$( cd "$MY_PATH" && pwd ) 48 | COMMIT_HASH=HEAD 49 | BUILD_BASE_BEE=false 50 | BEE_REPOSITORY=ethersphere/bee.git 51 | # Bee version here means the base bee version on which the images will be built 52 | BEE_VERSION=$("$MY_PATH/utils/env-variable-value.sh" BEE_VERSION) 53 | 54 | # handle passed options 55 | while [ $# -gt 0 ] 56 | do 57 | case "$1" in 58 | --build-base-bee) 59 | BUILD_BASE_BEE=true 60 | shift 1 61 | ;; 62 | --base-bee-commit-hash=*) 63 | COMMIT_HASH="${1#*=}" 64 | shift 1 65 | ;; 66 | --bee-repository=*) 67 | BEE_REPOSITORY="${1#*=}" 68 | shift 1 69 | ;; 70 | *) 71 | echoerr "Unknown argument: $1" 72 | usage 73 | ;; 74 | esac 75 | done 76 | 77 | # cleanup for start from an empty state 78 | "$MY_PATH/bee-cleanup.sh" 79 | 80 | if $BUILD_BASE_BEE ; then 81 | build_bee 82 | fi 83 | "$MY_PATH/network.sh" 84 | "$MY_PATH/blockchain/init.sh" 85 | "$MY_PATH/blockchain/start.sh" 86 | SLEEP_TIME=10 87 | echo "wait $SLEEP_TIME seconds for blockchain connection" 88 | sleep $SLEEP_TIME 89 | docker logs fdp-play-blockchain 90 | npm run migrate:contracts 91 | npm run supply 92 | chmod -R 777 "$MY_PATH/bee-data-dirs/" 93 | 94 | "$MY_PATH/bee-docker-build.sh" 95 | "$MY_PATH/blockchain/docker-build.sh" 96 | -------------------------------------------------------------------------------- /src/utils/blockchain/jsonrpc.ts: -------------------------------------------------------------------------------- 1 | import http from 'http' 2 | 3 | interface RpcRequest { 4 | id: number 5 | method: string 6 | params?: unknown[] 7 | // default is 2.0 8 | jsonrpc?: string 9 | } 10 | 11 | interface RpcResponse { 12 | id: number 13 | // default is 2.0 14 | jsonrpc: string 15 | result?: unknown 16 | error?: { 17 | code: number 18 | message: string 19 | } 20 | } 21 | 22 | function getHost(addr: string): string { 23 | addr = addr.startsWith('http://') ? addr.slice('http://'.length) : addr 24 | 25 | return addr.includes(':') ? addr.slice(0, addr.indexOf(':')) : addr 26 | } 27 | 28 | function getPort(addr: string): number { 29 | let port = 80 30 | 31 | if (addr.includes('http://')) addr = addr.slice(addr.indexOf('/') + 2) 32 | else if (addr.includes('https://')) { 33 | addr = addr.slice(addr.indexOf('/') + 2) 34 | port = 443 35 | } 36 | 37 | if (addr.includes(':')) port = Number(addr.slice(addr.indexOf(':') + 1)) 38 | 39 | if (Number.isNaN(port)) throw new Error('port number is wrong in rpc address') 40 | 41 | return port 42 | } 43 | 44 | export class JsonRpc { 45 | constructor(private rpcAddress: string) {} 46 | 47 | public async post(payload: RpcRequest, path = '/'): Promise { 48 | payload.jsonrpc ||= '2.0' 49 | 50 | return new Promise((resolve, reject) => { 51 | const stringifiedPayload = JSON.stringify(payload) 52 | const req = http.request( 53 | { 54 | host: getHost(this.rpcAddress), 55 | port: getPort(this.rpcAddress), 56 | method: 'POST', 57 | path, 58 | headers: { 59 | 'content-type': 'application/json', 60 | 'content-length': Buffer.byteLength(stringifiedPayload), 61 | }, 62 | }, 63 | res => { 64 | let data = '' 65 | 66 | // A chunk of data has been received. 67 | res.on('data', chunk => { 68 | data += chunk 69 | }) 70 | 71 | // The whole response has been received. 72 | res.on('end', () => { 73 | try { 74 | const jsonData = JSON.parse(data) as RpcResponse 75 | 76 | if (jsonData.error) { 77 | reject(`jsonrpc: ${jsonData.error.code} - ${jsonData.error.message}`) 78 | } 79 | 80 | if (!jsonData.result) { 81 | reject('jsonrpc: result is not defined') 82 | } 83 | resolve(jsonData) 84 | } catch (e) { 85 | throw new Error('jsonrpc: cannot parse data to json') 86 | } 87 | }) 88 | }, 89 | ) 90 | 91 | // Handle errors 92 | req.on('error', error => { 93 | reject(error) 94 | }) 95 | 96 | // Write the JSON data to the request 97 | req.write(stringifiedPayload) 98 | 99 | // End the request 100 | req.end() 101 | }) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fairdatasociety/fdp-play", 3 | "version": "3.3.0", 4 | "description": "Orchestration CLI for spinning up local development Bee cluster and FDP environment with Docker", 5 | "keywords": [ 6 | "bee", 7 | "swarm", 8 | "decentralised", 9 | "storage", 10 | "ethereum", 11 | "typescript", 12 | "p2p", 13 | "docker", 14 | "fdp", 15 | "fair data protocol", 16 | "fdp-play", 17 | "fdp-playground" 18 | ], 19 | "homepage": "https://github.com/fairDataSociety/fdp-play", 20 | "bugs": { 21 | "url": "https://github.com/fairDataSociety/fdp-play/issues/" 22 | }, 23 | "license": "BSD-3-Clause", 24 | "repository": { 25 | "type": "git", 26 | "url": "https://github.com/fairDataSociety/fdp-play.git" 27 | }, 28 | "bin": { 29 | "fdp-play": "./dist/index.js" 30 | }, 31 | "main": "dist/index.js", 32 | "files": [ 33 | "dist" 34 | ], 35 | "scripts": { 36 | "prepublishOnly": "npm run build", 37 | "build": "rimraf dist && webpack --progress --env mode=production", 38 | "start": "webpack --progress", 39 | "test": "jest --verbose --config=jest.config.ts -i", 40 | "types:check": "tsc --project tsconfig.test.json", 41 | "lint": "eslint --fix \"src/**/*.ts\" \"test/**/*.ts\" && prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 42 | "lint:check": "eslint \"src/**/*.ts\" \"test/**/*.ts\" && prettier --check \"src/**/*.ts\" \"test/**/*.ts\"", 43 | "depcheck": "depcheck ." 44 | }, 45 | "dependencies": { 46 | "@ethersphere/bee-js": "^8.3.0", 47 | "bufferutil": "^4.0.8", 48 | "chalk": "^4.1.2", 49 | "dockerode": "^4.0.2", 50 | "ethers": "^5.7.2", 51 | "furious-commander": "^1.7.1", 52 | "node-fetch": "^2", 53 | "ora": "^5.3.0", 54 | "utf-8-validate": "^5.0.10" 55 | }, 56 | "devDependencies": { 57 | "@babel/core": "^7.18.5", 58 | "@babel/plugin-proposal-class-properties": "^7.17.12", 59 | "@babel/plugin-proposal-decorators": "^7.24.0", 60 | "@babel/plugin-transform-runtime": "^7.18.5", 61 | "@babel/plugin-transform-strict-mode": "^7.16.7", 62 | "@babel/preset-env": "^7.18.2", 63 | "@babel/preset-typescript": "^7.23.3", 64 | "@commitlint/cli": "^17.0.2", 65 | "@commitlint/config-conventional": "^17.0.2", 66 | "@fluffy-spoon/substitute": "^1.208.0", 67 | "@jest/types": "^28.1.0", 68 | "@types/dockerode": "^3.3.24", 69 | "@types/jest": "^27.4.1", 70 | "@types/node": "^18.11.8", 71 | "@types/node-fetch": "^2.6.11", 72 | "@types/terser-webpack-plugin": "^5.2.0", 73 | "@types/webpack": "^5.28.0", 74 | "@types/webpack-bundle-analyzer": "^4.4.1", 75 | "@typescript-eslint/eslint-plugin": "^7.13.0", 76 | "babel-jest": "^29.2.2", 77 | "babel-loader": "^9.1.3", 78 | "depcheck": "^1.4.3", 79 | "eslint": "^8.57.0", 80 | "eslint-config-prettier": "^9.1.0", 81 | "eslint-plugin-jest": "^27.9.0", 82 | "eslint-plugin-prettier": "^5.1.3", 83 | "eslint-plugin-unused-imports": "^3.1.0", 84 | "glob": "^8.0.3", 85 | "jest": "^27.4.7", 86 | "prettier": "^3.2.5", 87 | "rimraf": "^3.0.2", 88 | "terser-webpack-plugin": "^5.3.3", 89 | "ts-node": "^10.8.1", 90 | "typescript": "^5.3.3", 91 | "webpack": "^5.73.0", 92 | "webpack-bundle-analyzer": "^4.5.0", 93 | "webpack-cli": "^4.10.0" 94 | }, 95 | "engines": { 96 | "node": ">=12.0.0", 97 | "npm": ">=6.0.0" 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /orchestrator/scripts/supply.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import { ethers } from 'hardhat' 3 | import beeAddresses from '../bee-eth-addresses.json' 4 | import contractAddresses from '../contract-addresses.json' 5 | import { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers' 6 | 7 | function getRawTokenAmount(amount: string, decimals = 18) { 8 | const amountBn = BigInt(amount) 9 | const rawAmount = amountBn * 10n ** BigInt(decimals) 10 | 11 | return rawAmount 12 | } 13 | 14 | /** Supply given address with Ether */ 15 | async function supplyEther(supplierAccount: HardhatEthersSigner, recepientAddress: string, etherAmount = '1') { 16 | try { 17 | const transaction = await supplierAccount.sendTransaction({ 18 | gasLimit: 6721975, 19 | gasPrice: ethers.parseUnits('10', 'gwei'), 20 | value: ethers.parseUnits(etherAmount, 'ether'), 21 | from: supplierAccount, 22 | to: recepientAddress, 23 | }) 24 | 25 | await transaction.wait() 26 | const receipt = await supplierAccount.provider.getTransactionReceipt(transaction.hash) 27 | 28 | if (!receipt) throw Error('No receipt') 29 | 30 | console.log( 31 | `Supplying address ${recepientAddress} with Ether from account ${supplierAccount.address} was successful! \n` + 32 | `\tGiven Ether Amount: ${etherAmount}\n` + 33 | `\tTransaction ID: ${transaction.hash}`, 34 | ) 35 | console.log('-'.repeat(process.stdout.columns)) 36 | } catch (e) { 37 | console.error('Supply Ether Error', e) 38 | throw new Error(`Error happened at supplying address ${recepientAddress} from account ${supplierAccount.address}`) 39 | } 40 | } 41 | 42 | /** Supply given address with the given Token amount */ 43 | async function mintToken(supplierAccount: HardhatEthersSigner, recepientAddress: string, tokenAmount = '100') { 44 | const instance = await ethers.getContractAt('ERC20PresetMinterPauser', contractAddresses.bzzToken, supplierAccount) 45 | const rawTokenAmount = getRawTokenAmount(tokenAmount) 46 | try { 47 | const transaction = await instance.mint(recepientAddress, rawTokenAmount) 48 | await transaction.wait() 49 | 50 | console.log( 51 | `Supplying address ${recepientAddress} with Token from account ${supplierAccount.address} was successful! \n` + 52 | `\tGiven Token Amount: ${tokenAmount}\n` + 53 | `\tTransaction ID: ${transaction.hash}`, 54 | ) 55 | console.log('-'.repeat(process.stdout.columns)) 56 | } catch (e) { 57 | console.error('Supply Token Error', e) 58 | throw new Error(`Error happened at supplying address ${recepientAddress} from account ${supplierAccount.address}`) 59 | } 60 | } 61 | 62 | /** Supply ERC20 tokens to all configured Bee client overlay addresses */ 63 | async function supplyTokenForBees(supplierAccount: HardhatEthersSigner) { 64 | console.log(`Supply ERC20 tokens to the configured Bee addresses`) 65 | console.log('='.repeat(process.stdout.columns)) 66 | 67 | for (const beeAddress of beeAddresses) { 68 | await mintToken(supplierAccount, beeAddress) 69 | } 70 | } 71 | 72 | /** Supply ether to all configured Bee client overlay addresses */ 73 | async function supplyEtherForBees(supplierAccount: HardhatEthersSigner) { 74 | console.log('Supply Ether to the configured Bee addresses') 75 | console.log('='.repeat(process.stdout.columns)) 76 | 77 | for (const beeAddress of beeAddresses) { 78 | await supplyEther(supplierAccount, beeAddress) 79 | } 80 | } 81 | 82 | async function main() { 83 | const accounts = await ethers.getSigners() 84 | await supplyTokenForBees(accounts[0]) 85 | await supplyEtherForBees(accounts[0]) 86 | } 87 | 88 | main().catch(error => { 89 | console.error(error) 90 | process.exitCode = 1 91 | }) 92 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['plugin:@typescript-eslint/recommended', 'prettier', 'plugin:prettier/recommended'], 3 | parserOptions: { 4 | sourceType: 'module', 5 | ecmaVersion: 2018, 6 | project: ['./tsconfig.test.json', './orchestrator/tsconfig.json'], 7 | }, 8 | env: { 9 | jest: true, 10 | }, 11 | globals: { 12 | browser: true, 13 | page: true, 14 | }, 15 | plugins: ['jest', 'unused-imports'], 16 | rules: { 17 | 'array-bracket-newline': ['error', 'consistent'], 18 | strict: ['error', 'safe'], 19 | 'block-scoped-var': 'error', 20 | complexity: 'warn', 21 | 'default-case': 'error', 22 | 'dot-notation': 'warn', 23 | eqeqeq: 'error', 24 | 'guard-for-in': 'warn', 25 | 'linebreak-style': ['warn', 'unix'], 26 | 'no-alert': 'error', 27 | 'no-case-declarations': 'error', 28 | 'no-console': 'error', 29 | 'no-constant-condition': 'error', 30 | 'no-continue': 'warn', 31 | 'no-div-regex': 'error', 32 | 'no-empty': 'warn', 33 | 'no-empty-pattern': 'error', 34 | 'no-implicit-coercion': 'error', 35 | 'prefer-arrow-callback': 'warn', 36 | 'no-labels': 'error', 37 | 'no-loop-func': 'error', 38 | 'no-nested-ternary': 'warn', 39 | 'no-script-url': 'error', 40 | 'no-warning-comments': 'warn', 41 | 'quote-props': ['error', 'as-needed'], 42 | 'require-yield': 'error', 43 | 'max-nested-callbacks': ['error', 4], 44 | 'max-depth': ['error', 4], 45 | 'space-before-function-paren': [ 46 | 'error', 47 | { 48 | anonymous: 'never', 49 | named: 'never', 50 | asyncArrow: 'always', 51 | }, 52 | ], 53 | 'padding-line-between-statements': [ 54 | 'error', 55 | { blankLine: 'always', prev: '*', next: 'if' }, 56 | { blankLine: 'always', prev: '*', next: 'function' }, 57 | { blankLine: 'always', prev: '*', next: 'return' }, 58 | ], 59 | 'no-useless-constructor': 'off', 60 | 'no-dupe-class-members': 'off', 61 | 'no-unused-expressions': 'off', 62 | curly: ['error', 'multi-line'], 63 | 'object-curly-spacing': ['error', 'always'], 64 | 'comma-dangle': ['error', 'always-multiline'], 65 | '@typescript-eslint/no-useless-constructor': 'error', 66 | '@typescript-eslint/no-unused-expressions': 'error', 67 | '@typescript-eslint/member-delimiter-style': [ 68 | 'error', 69 | { 70 | multiline: { 71 | delimiter: 'none', 72 | requireLast: true, 73 | }, 74 | singleline: { 75 | delimiter: 'semi', 76 | requireLast: false, 77 | }, 78 | }, 79 | ], 80 | '@typescript-eslint/ban-ts-comment': [ 81 | 'error', 82 | { 83 | 'ts-expect-error': 'allow-with-description', 84 | 'ts-ignore': 'allow-with-description', 85 | 'ts-nocheck': 'allow-with-description', 86 | 'ts-check': 'allow-with-description', 87 | minimumDescriptionLength: 6, 88 | }, 89 | ], 90 | 'require-await': 'off', 91 | '@typescript-eslint/promise-function-async': 'error', 92 | '@typescript-eslint/require-await': 'off', 93 | '@typescript-eslint/no-non-null-assertion': 'off', 94 | '@typescript-eslint/no-unused-vars': 'off', 95 | 'unused-imports/no-unused-imports': 'error', 96 | 'unused-imports/no-unused-vars': [ 97 | 'warn', 98 | { vars: 'all', varsIgnorePattern: '^_', args: 'after-used', argsIgnorePattern: '^_' }, 99 | ], 100 | }, 101 | overrides: [ 102 | { 103 | files: ['*.spec.ts'], 104 | rules: { 105 | 'max-nested-callbacks': ['error', 10], // allow describe/it nesting 106 | }, 107 | }, 108 | ], 109 | } 110 | -------------------------------------------------------------------------------- /webpack.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import Path from 'path' 3 | import TerserPlugin from 'terser-webpack-plugin' 4 | import { BannerPlugin, Configuration, DefinePlugin, WebpackPluginInstance } from 'webpack' 5 | import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer' 6 | import PackageJson from './package.json' 7 | 8 | interface WebpackEnvParams { 9 | debug: boolean 10 | mode: 'production' | 'development' 11 | fileName: string 12 | } 13 | 14 | const base = (env?: Partial): Configuration => { 15 | const isProduction = env?.mode === 'production' 16 | const filename = env?.fileName || ['index.js'].filter(Boolean).join('') 17 | const entry = Path.resolve(__dirname, 'src') 18 | const path = Path.resolve(__dirname, 'dist') 19 | const target = 'node' 20 | const plugins: WebpackPluginInstance[] = [ 21 | new DefinePlugin({ 22 | 'process.env.ENV': env?.mode || 'development', 23 | 'process.env.IS_WEBPACK_BUILD': 'true', 24 | }), 25 | new BannerPlugin({ banner: '#!/usr/bin/env node', raw: true }), 26 | ] 27 | 28 | return { 29 | bail: Boolean(isProduction), 30 | mode: env?.mode || 'development', 31 | devtool: isProduction ? false : 'cheap-module-source-map', 32 | entry, 33 | output: { 34 | path, 35 | filename, 36 | sourceMapFilename: filename + '.map', 37 | library: PackageJson.name, 38 | libraryTarget: 'umd', 39 | globalObject: 'this', 40 | }, 41 | module: { 42 | rules: [ 43 | { 44 | test: /\.(ts|js)$/, 45 | // include: entry, 46 | use: { 47 | loader: 'babel-loader', 48 | }, 49 | }, 50 | { 51 | test: /\.node/, 52 | type: 'asset/resource' 53 | } 54 | ], 55 | }, 56 | resolve: { 57 | extensions: ['.ts', '.js'], 58 | fallback: { 59 | path: false, 60 | fs: false, 61 | }, 62 | }, 63 | optimization: { 64 | minimize: isProduction, 65 | minimizer: [ 66 | // This is only used in production mode 67 | new TerserPlugin({ 68 | terserOptions: { 69 | parse: { 70 | // we want terser to parse ecma 8 code. However, we don't want it 71 | // to apply any minfication steps that turns valid ecma 5 code 72 | // into invalid ecma 5 code. This is why the 'compress' and 'output' 73 | // sections only apply transformations that are ecma 5 safe 74 | // https://github.com/facebook/create-react-app/pull/4234 75 | ecma: 2018, 76 | }, 77 | compress: { 78 | ecma: 5, 79 | }, 80 | mangle: { 81 | safari10: true, 82 | }, 83 | output: { 84 | ecma: 5, 85 | comments: false, 86 | }, 87 | }, 88 | // Use multi-process parallel running to improve the build speed 89 | // Default number of concurrent runs: os.cpus().length - 1 90 | parallel: true, 91 | }), 92 | ], 93 | }, 94 | plugins, 95 | target, 96 | node: { 97 | global: true, 98 | __filename: 'mock', 99 | __dirname: 'mock', 100 | }, 101 | performance: { 102 | hints: false, 103 | }, 104 | watch: !isProduction, 105 | } 106 | } 107 | 108 | export default async (env?: Partial): Promise => { 109 | // eslint-disable-next-line no-console 110 | console.log('env', env) 111 | 112 | if (env?.debug) { 113 | const config = { 114 | ...(await base(env)), 115 | plugins: [new BundleAnalyzerPlugin()], 116 | profile: true, 117 | } 118 | 119 | return config 120 | } 121 | 122 | return base(env) 123 | } 124 | -------------------------------------------------------------------------------- /test/integration/stop.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import Dockerode from 'dockerode' 3 | import crypto from 'crypto' 4 | 5 | import { run } from '../utils/run' 6 | import { ENV_ENV_PREFIX_KEY } from '../../src/constants' 7 | import { findContainer } from '../utils/docker' 8 | 9 | describe('stop command', () => { 10 | let docker: Dockerode 11 | const envPrefix = `bee-play-test-${crypto.randomBytes(4).toString('hex')}` 12 | 13 | beforeAll(() => { 14 | docker = new Dockerode() 15 | 16 | // This will force Bee Factory to create 17 | process.env[ENV_ENV_PREFIX_KEY] = envPrefix 18 | }) 19 | 20 | afterAll(async () => { 21 | await run(['stop', '--rm']) // Cleanup the testing containers 22 | }) 23 | 24 | describe('should stop cluster', () => { 25 | beforeAll(async () => { 26 | // As spinning the cluster with --detach the command will exit once the cluster is up and running 27 | await run(['start', '--detach']) 28 | }) 29 | 30 | it('', async () => { 31 | await expect(findContainer(docker, 'queen')).resolves.toHaveProperty('State.Status', 'running') 32 | await expect(findContainer(docker, 'blockchain')).resolves.toHaveProperty('State.Status', 'running') 33 | await expect(findContainer(docker, 'worker-1')).resolves.toHaveProperty('State.Status', 'running') 34 | await expect(findContainer(docker, 'worker-2')).resolves.toHaveProperty('State.Status', 'running') 35 | await expect(findContainer(docker, 'worker-3')).resolves.toHaveProperty('State.Status', 'running') 36 | await expect(findContainer(docker, 'worker-4')).resolves.toHaveProperty('State.Status', 'running') 37 | 38 | await run(['stop']) 39 | 40 | await expect(findContainer(docker, 'queen')).resolves.toHaveProperty('State.Status', 'exited') 41 | await expect(findContainer(docker, 'blockchain')).resolves.toHaveProperty('State.Status', 'exited') 42 | await expect(findContainer(docker, 'worker-1')).resolves.toHaveProperty('State.Status', 'exited') 43 | await expect(findContainer(docker, 'worker-2')).resolves.toHaveProperty('State.Status', 'exited') 44 | await expect(findContainer(docker, 'worker-3')).resolves.toHaveProperty('State.Status', 'exited') 45 | await expect(findContainer(docker, 'worker-4')).resolves.toHaveProperty('State.Status', 'exited') 46 | }) 47 | }) 48 | 49 | describe('should stop cluster and remove containers', () => { 50 | beforeAll(async () => { 51 | // As spinning the cluster with --detach the command will exit once the cluster is up and running 52 | await run(['start', '--detach']) 53 | }) 54 | 55 | it('', async () => { 56 | await expect(findContainer(docker, 'queen')).resolves.toHaveProperty('State.Status', 'running') 57 | await expect(findContainer(docker, 'blockchain')).resolves.toHaveProperty('State.Status', 'running') 58 | await expect(findContainer(docker, 'worker-1')).resolves.toHaveProperty('State.Status', 'running') 59 | await expect(findContainer(docker, 'worker-2')).resolves.toHaveProperty('State.Status', 'running') 60 | await expect(findContainer(docker, 'worker-3')).resolves.toHaveProperty('State.Status', 'running') 61 | await expect(findContainer(docker, 'worker-4')).resolves.toHaveProperty('State.Status', 'running') 62 | 63 | await run(['stop', '--rm']) 64 | 65 | await expect(findContainer(docker, 'queen')).rejects.toHaveProperty('statusCode', 404) 66 | await expect(findContainer(docker, 'blockchain')).rejects.toHaveProperty('statusCode', 404) 67 | await expect(findContainer(docker, 'worker-1')).rejects.toHaveProperty('statusCode', 404) 68 | await expect(findContainer(docker, 'worker-2')).rejects.toHaveProperty('statusCode', 404) 69 | await expect(findContainer(docker, 'worker-3')).rejects.toHaveProperty('statusCode', 404) 70 | await expect(findContainer(docker, 'worker-4')).rejects.toHaveProperty('statusCode', 404) 71 | }) 72 | }) 73 | }) 74 | -------------------------------------------------------------------------------- /.github/workflows/publish-docker.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | repository_dispatch: 3 | types: [build-images] 4 | workflow_dispatch: 5 | inputs: 6 | buildImage: 7 | description: 'Build and push Docker Image according to the environment' 8 | default: 'false' 9 | commitVersionTag: 10 | description: 'The image tag will be retrieved from the bee version command' 11 | default: 'false' 12 | beeVersion: 13 | description: 'The official bee image tag that the image will be built on. Default: last supported version' 14 | default: 'latest' 15 | beeVersionAsCommitHash: 16 | description: 17 | 'The beeVersion parameter will be interpreted as a source code commit hash that the bee base image will be 18 | built on' 19 | default: 'false' 20 | stateCommit: 21 | description: 'The images will have cheques by the traffic generation' 22 | default: 'false' 23 | latest: 24 | description: 'The images will be tagged with latest as well' 25 | default: 'false' 26 | 27 | env: 28 | BEE_IMAGE_PREFIX: 'fairdatasociety' 29 | BUILD_IMAGE: 'false' 30 | COMMIT_VERSION_TAG: 'false' 31 | STATE_COMMIT: 'false' 32 | BEE_VERSION: '${{ github.event.client_payload.tag }}' 33 | LATEST: 'false' 34 | 35 | jobs: 36 | bee-images: 37 | name: Build and publish images 38 | runs-on: ubuntu-latest 39 | steps: 40 | - uses: actions/setup-node@v3 41 | with: 42 | node-version: 18 43 | registry-url: 'https://registry.npmjs.org' 44 | 45 | - name: Checkout 46 | uses: actions/checkout@v3 47 | 48 | - name: Set up QEMU 49 | uses: docker/setup-qemu-action@v2 50 | 51 | - name: Override inputs from `workflow_dispatch` 52 | run: | 53 | if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then 54 | echo "BEE_VERSION=${{ github.event.inputs.beeVersion }}" >> $GITHUB_ENV 55 | echo "BUILD_IMAGE=${{ github.event.inputs.beeVersionAsCommitHash }}" >> $GITHUB_ENV 56 | echo "COMMIT_VERSION_TAG=${{ github.event.inputs.commitVersionTag }}" >> $GITHUB_ENV 57 | echo "STATE_COMMIT=${{ github.event.inputs.stateCommit }}" >> $GITHUB_ENV 58 | echo "LATEST=${{ github.event.inputs.latest }}" >> $GITHUB_ENV 59 | else 60 | echo "BEE_VERSION=${BEE_VERSION/v}" >> $GITHUB_ENV 61 | fi 62 | 63 | - name: Auth to Docker Hub 64 | if: 65 | ${{ github.event_name == 'repository_dispatch' || (github.event.inputs.buildImage == 'true' && success()) }} 66 | run: | 67 | echo "${{ secrets.DOCKERHUB_TOKEN }}" | docker login -u ${{secrets.DOCKERHUB_USERNAME}} --password-stdin 68 | 69 | - uses: actions/cache@v3 70 | id: cache-npm 71 | with: 72 | path: orchestrator/node_modules 73 | key: ${{ runner.os }}-${{ hashFiles('orchestrator/package-lock.json') }} 74 | 75 | - name: Install npm deps 76 | if: steps.cache-npm.outputs.cache-hit != 'true' 77 | run: cd ./orchestrator && npm ci 78 | 79 | - name: Build images 80 | id: build 81 | run: | 82 | cd ./orchestrator 83 | BUILD_PARAMS="" 84 | if [ "$BUILD_IMAGE" = 'true' ]; then 85 | BUILD_PARAMS+=" --build-base-bee --base-bee-commit-hash=$BEE_VERSION" 86 | fi 87 | if [ "$STATE_COMMIT" = 'true' ]; then 88 | BUILD_PARAMS+=" --gen-traffic" 89 | fi 90 | if [ -n "$BUILD_PARAMS" ]; then 91 | BUILD_PARAMS="-- $BUILD_PARAMS" 92 | fi 93 | npm run build:env $BUILD_PARAMS 94 | 95 | - name: Release images 96 | id: release 97 | run: | 98 | cd ./orchestrator 99 | PUBLISH_PARAMS="" 100 | if [ "$LATEST" = 'true' ]; then 101 | PUBLISH_PARAMS+=" --latest" 102 | fi 103 | if [ -n "$PUBLISH_PARAMS" ]; then 104 | PUBLISH_PARAMS="-- $PUBLISH_PARAMS" 105 | fi 106 | npm run publish:env $PUBLISH_PARAMS 107 | -------------------------------------------------------------------------------- /src/utils/wait.ts: -------------------------------------------------------------------------------- 1 | import { default as __fetch, FetchError } from 'node-fetch' 2 | import { sleep } from './index' 3 | import { TimeoutError } from './error' 4 | import { Bee } from '@ethersphere/bee-js' 5 | import { AllStatus } from './docker' 6 | 7 | const AWAIT_SLEEP = 3_000 8 | 9 | const BLOCKCHAIN_BODY_REQUEST = JSON.stringify({ jsonrpc: '2.0', method: 'eth_chainId', id: 1 }) 10 | const EXPECTED_CHAIN_ID = '0xfb4' 11 | const ALLOWED_ERRORS = ['ECONNREFUSED', 'ECONNRESET', 'UND_ERR_SOCKET'] 12 | 13 | function isAllowedError(e: FetchError): boolean { 14 | //@ts-ignore: Node 18 native fetch returns error where the underlying error is wrapped and placed in e.cause 15 | if (e.cause) { 16 | //@ts-ignore: Node 18 native fetch returns error where the underlying error is wrapped and placed in e.cause 17 | e = e.cause 18 | } 19 | 20 | if (e.code && ALLOWED_ERRORS.includes(e.code)) { 21 | return true 22 | } 23 | 24 | // Errors from Bee-js does not have the `FetchError` structure (eq. `code` property) 25 | // so we assert message itself. 26 | if (e.message.includes('socket hang up')) { 27 | return true 28 | } 29 | 30 | // Error when requesting from Node 18 to bee node (1.11.1). 31 | // Happens in `beeDebug.getNodeAddresses()` method. 32 | // It looks like the bee node sends a response with a missing header, or the response is processed incorrectly. 33 | if (e.message.includes('other side closed')) { 34 | return true 35 | } 36 | 37 | return ALLOWED_ERRORS.some(substring => e.message.includes(substring)) 38 | } 39 | 40 | export async function waitForBlockchain(waitingIterations = 30): Promise { 41 | for (let i = 0; i < waitingIterations; i++) { 42 | try { 43 | const request = await __fetch('http://127.0.0.1:9545', { 44 | method: 'POST', 45 | body: BLOCKCHAIN_BODY_REQUEST, 46 | headers: { 'Content-Type': 'application/json' }, 47 | }) 48 | const response = (await request.json()) as { result: string } 49 | 50 | if (response.result === EXPECTED_CHAIN_ID) { 51 | return 52 | } 53 | } catch (e) { 54 | if (!isAllowedError(e as FetchError)) { 55 | throw e 56 | } 57 | } 58 | 59 | await sleep(AWAIT_SLEEP) 60 | } 61 | 62 | throw new TimeoutError('Waiting for blockchain container timed-out') 63 | } 64 | 65 | export async function waitForQueen(verifyQueenIsUp: () => Promise, waitingIterations = 120): Promise { 66 | const beeDebug = new Bee('http://127.0.0.1:1633') 67 | 68 | for (let i = 0; i < waitingIterations; i++) { 69 | try { 70 | if (!(await verifyQueenIsUp())) { 71 | throw new Error('Queen node is not running!') 72 | } 73 | 74 | const addresses = await beeDebug.getNodeAddresses() 75 | 76 | if (addresses.underlay.length > 0) { 77 | const addr = addresses.underlay.find(addr => !addr.includes('127.0.0.1')) 78 | 79 | if (addr) { 80 | return addr 81 | } 82 | } 83 | } catch (e) { 84 | if (!isAllowedError(e as FetchError)) { 85 | throw e 86 | } 87 | } 88 | 89 | await sleep(AWAIT_SLEEP) 90 | } 91 | 92 | throw new TimeoutError('Waiting for queen container timed-out') 93 | } 94 | 95 | export async function waitForWorkers( 96 | workerCount: number, 97 | getStatus: () => Promise, 98 | waitingIterations = 120, 99 | ): Promise { 100 | const beeDebug = new Bee('http://127.0.0.1:1633') 101 | 102 | const status = await getStatus() 103 | for (let i = 1; i <= workerCount; i++) { 104 | if (status[`worker${i}` as keyof AllStatus] !== 'running') { 105 | throw new Error('Some of the workers node is not running!') 106 | } 107 | } 108 | 109 | for (let i = 0; i < waitingIterations; i++) { 110 | try { 111 | const peers = await beeDebug.getPeers() 112 | 113 | if (peers.length >= workerCount) { 114 | return 115 | } 116 | } catch (e) { 117 | if ((e as { message: string }).message.includes('503')) { 118 | // expexted 119 | } else if (!isAllowedError(e as FetchError)) { 120 | throw e 121 | } 122 | } 123 | 124 | await sleep(AWAIT_SLEEP) 125 | } 126 | 127 | throw new TimeoutError('Waiting for worker nodes timed-out') 128 | } 129 | -------------------------------------------------------------------------------- /orchestrator/builder/environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | 6 | usage() { 7 | cat << USAGE >&2 8 | USAGE: 9 | $ environment.sh [COMMAND] [PARAMETERS] 10 | COMMANDS: 11 | start create Bee cluster with the given parameters 12 | stop stop Bee cluster 13 | PARAMETERS: 14 | --ephemeral create ephemeral container for bee-client. Data won't be persisted. 15 | --restrict(=string) turns on Restricted API support. If string is given then it uses it as the main password otherwise 'SwarmToTheMoon' is used 16 | --workers=number all Bee nodes in the test environment. Default is 4. 17 | --detach It will not log the output of Queen node at the end of the process. 18 | --port-maps=number map ports of the cluster nodes to the hosting machine in the following manner: 19 | 1. 1633:1634 20 | 2. 11633:11634 21 | 3. 21633:21634 (...) 22 | number represents the nodes number to map from. Default is 2. 23 | --hostname=string Interface to which should the nodes be bound (default 127.0.0.0). 24 | USAGE 25 | exit 1 26 | } 27 | 28 | 29 | stop() { 30 | #Stop Bee nodes 31 | docker stop "$SWARM_BLOCKCHAIN_NAME" 32 | #Stop blockchain nodes 33 | "$MY_PATH/bee.sh" stop 34 | 35 | trap - SIGINT 36 | exit 0; 37 | } 38 | 39 | MY_PATH=$(dirname "$0") # relative 40 | MY_PATH=$( cd "$MY_PATH" && pwd ) # absolutized and normalized 41 | # Check used system variable set 42 | BEE_ENV_PREFIX=$("$MY_PATH/utils/env-variable-value.sh" BEE_ENV_PREFIX) 43 | BEE_IMAGE_PREFIX=$("$MY_PATH/utils/env-variable-value.sh" BEE_IMAGE_PREFIX) 44 | BLOCKCHAIN_VERSION=$("$MY_PATH/utils/env-variable-value.sh" BLOCKCHAIN_VERSION) 45 | BLOCKCHAIN_RUN_ARGS=$("$MY_PATH//utils/env-variable-value.sh" BLOCKCHAIN_RUN_ARGS) 46 | 47 | # Init variables 48 | EPHEMERAL=false 49 | RESTRICTED=false 50 | RESTRICTED_PASSWORD="SwarmToTheMoon" 51 | WORKERS=4 52 | LOG=true 53 | SWARM_BLOCKCHAIN_NAME="$BEE_ENV_PREFIX-blockchain" 54 | SWARM_NETWORK="$BEE_ENV_PREFIX-network" 55 | PORT_MAPS=2 56 | HOSTNAME="127.0.0.1" 57 | 58 | # Decide script action 59 | case "$1" in 60 | start) 61 | shift 1 62 | ;; 63 | stop) 64 | stop 65 | ;; 66 | *) 67 | echoerr "Unknown command: $1" 68 | usage 69 | ;; 70 | esac 71 | 72 | 73 | # Alter variables from flags 74 | while [ $# -gt 0 ] 75 | do 76 | case "$1" in 77 | --ephemeral) 78 | EPHEMERAL=true 79 | shift 1 80 | ;; 81 | --workers=*) 82 | WORKERS=${1#*=} 83 | shift 1 84 | ;; 85 | --port-maps=*) 86 | PORT_MAPS="${1#*=}" 87 | shift 1 88 | ;; 89 | --detach) 90 | LOG=false 91 | shift 1 92 | ;; 93 | --hostname=*) 94 | HOSTNAME="${1#*=}" 95 | shift 1 96 | ;; 97 | --restrict*) 98 | RESTRICTED=true 99 | if [ "${1#*=}" != "--restrict" ] ; then 100 | RESTRICTED_PASSWORD="${1#*=}" 101 | fi 102 | shift 1 103 | ;; 104 | --help) 105 | usage 106 | ;; 107 | *) 108 | echoerr "Unknown argument: $1" 109 | usage 110 | ;; 111 | esac 112 | done 113 | 114 | echo "Create Docker network..." 115 | "$MY_PATH/network.sh" 116 | 117 | # Start blockchain node 118 | echo "Start Blockchain node..." 119 | BLOCKCHAIN_CONTAINER=$(docker container ls -qaf name=$SWARM_BLOCKCHAIN_NAME) 120 | if [ -z "$BLOCKCHAIN_CONTAINER" ] ; then 121 | BLOCKCHAIN_ARGUMENTS="--name $SWARM_BLOCKCHAIN_NAME --network $SWARM_NETWORK -p $HOSTNAME:9545:9545 -d" 122 | if $EPHEMERAL ; then 123 | BLOCKCHAIN_ARGUMENTS="$BLOCKCHAIN_ARGUMENTS --rm" 124 | fi 125 | docker run $BLOCKCHAIN_ARGUMENTS $BEE_IMAGE_PREFIX/$SWARM_BLOCKCHAIN_NAME:$BLOCKCHAIN_VERSION $BLOCKCHAIN_RUN_ARGS 126 | else 127 | docker start $BLOCKCHAIN_CONTAINER 128 | fi 129 | 130 | # Wait for blockchain service initializes 131 | while : ; do 132 | CHAINID=$(curl -s -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","method":"eth_chainId","id":1}' http://localhost:9545 | grep "0xfb4") 133 | [[ ! -z "$CHAINID" ]] && break 134 | echo "waiting for blockchain service is up..." 135 | sleep 3 136 | done 137 | 138 | # Build up bee.sh parameters 139 | BEE_SH_ARGUMENTS="--workers=$WORKERS --port-maps=$PORT_MAPS --hostname=$HOSTNAME" 140 | if $EPHEMERAL ; then 141 | BEE_SH_ARGUMENTS="$BEE_SH_ARGUMENTS --ephemeral" 142 | fi 143 | if $RESTRICTED ; then 144 | BEE_SH_ARGUMENTS="$BEE_SH_ARGUMENTS --restrict=$RESTRICTED_PASSWORD" 145 | fi 146 | if ! $LOG ; then 147 | BEE_SH_ARGUMENTS="$BEE_SH_ARGUMENTS --detach" 148 | fi 149 | 150 | # Call bee.sh with the passed arguments 151 | echo "Start Bee nodes..." 152 | "$MY_PATH/bee.sh" start $BEE_SH_ARGUMENTS 153 | 154 | # If the code run reach this point without detach flag, 155 | # then the user interrupted the log process in the bee.sh 156 | if $LOG ; then 157 | docker stop $SWARM_BLOCKCHAIN_NAME 158 | fi 159 | -------------------------------------------------------------------------------- /orchestrator/README.md: -------------------------------------------------------------------------------- 1 | # Bee Factory Generator 2 | This project builds up a test environment with Bee clients and with a test blockchain. 3 | The created environment is runnable on local machine as well. 4 | 5 | All services run in `Docker` containers only. 6 | 7 | Currently, the repository supports running Bee nodes up to 5 by default. 8 | 9 | ## Maintainers 10 | 11 | - [nugaon](https://github.com/nugaon) 12 | 13 | See what "Maintainer" means [here](https://github.com/ethersphere/repo-maintainer). 14 | 15 | ## Usage 16 | The whole Bee environment (with blockchain) can be started by [running one script](###Run-Environment), 17 | but for that you need to have the necessary Docker images, which is possible to build yourself by [running some scripts](###Setup-the-environment) 18 | 19 | First you may want to set all global variables that the scripts will use. 20 | For that, there is a [.env](builder/.env) file which contains all necessary variables that you need. 21 | 22 | ```sh 23 | set -a && source ./builder/.env && set +a 24 | ``` 25 | 26 | If you do not set these global variables, the scripts will use those which are available in the [.env](builder/.env) file. 27 | 28 | ## Setup the environment 29 | 30 | Create the common Docker network for the environment with 31 | 32 | ```sh 33 | ./builder/network.sh 34 | ``` 35 | 36 | To start the blockchain, run the following command in the root directory of the project: 37 | 38 | ```sh 39 | ./builder/blockchain/init.sh && ./builder/blockchain/start.sh 40 | ``` 41 | 42 | After that, it's possible to deploy Swarm smart contracts 43 | 44 | ```sh 45 | npm run migrate:contracts 46 | ``` 47 | 48 | Before you start the Bee nodes with the deployed Swap Factory, you have to fund your overlay addresses of your Bee nodes for the successful start. 49 | The [supply.js](src/supply.js) script can fund the addresses which are defined in [bee-overlay-addresses.json](bee-overlay-addresses.json) file. 50 | To run this script just execute 51 | 52 | ```sh 53 | npm run supply 54 | ``` 55 | 56 | and the configured accounts will get 1 ether and 100 BZZ Token. 57 | 58 | After all above went successfully you can start the Bee nodes 59 | 60 | ```sh 61 | ./builder/bee.sh start --workers=4 62 | ``` 63 | 64 | OR it is possible to build docker images on a desired state, so that a fresh environment can be started on each run. 65 | 66 | ### Build Docker Images 67 | 68 | Basically, a full-featured Bee environment has 2 types of Docker image: 69 | 70 | - Bee images: Bee clients with pre-defined keys (and optionally including the state which you nodes have in its [data-dirs](scripts/bee-data-dirs)) 71 | ```sh 72 | ./builder/bee-docker-build.sh 73 | ``` 74 | - Blockchain image: Ganache blockchain which you may want to take a snapshot of after the contracts are deployed and the pre-defined Bee client keys are funded already. 75 | ```sh 76 | ./builder/blockchain/docker-build.sh 77 | ``` 78 | 79 | ## Start Environment 80 | 81 | If you have all Docker images that your [environment file](scripts/.env) requires, 82 | start the Bee cluster 83 | 84 | ```sh 85 | ./builder/environment.sh start 86 | ``` 87 | 88 | ### Keep Bee datastore between different environments 89 | When different bee client builds need to be tested and the same data must be available in different environments, 90 | `environment.sh` script can be used to spin up any specific bee image. 91 | 92 | To define what is the bee image in question, change the `./builder/utils/.commit-version-tag` to the tag that the docker image has and suffix that with `-commit`. 93 | 94 | The bee data is binded to the `./builder/bee-data-dirs` folder so it requires the right permission to be able to write to it. To do that, run 95 | 96 | ```sh 97 | sudo chown -R 999 builder/bee-data-dirs/ 98 | ``` 99 | 100 | Execute 101 | ```sh 102 | export COMMIT_VERSION_TAG=true && export BUILD_IMAGE=true && ./builder/environment.sh start 103 | ``` 104 | 105 | it will retain the data generated by the bee clients in the bee-data-dirs folder. 106 | Next time when the environment starts with another bee images (after changing the .commit-version-tag), it will remember all uploaded files and other states. 107 | 108 | ### Restricted API 109 | 110 | If you want to enable permission check feature of Bee on the API endpoints you can use `--restrict` flag. This will 111 | use default password `SwarmToTheMoon` or if you want you can pass your own password as `--restrict=someOtherPassword`. 112 | 113 | This feature requires to have `htpasswd` command available which is part of the `apache2-utils` package. 114 | 115 | ## Utilities 116 | 117 | It is possible to generate random traffic in your cluster: 118 | 119 | ```sh 120 | $ npm run gen:traffic 121 | ``` 122 | 123 | The script is in an infinite loop, so if you want to stop the generation you have to terminate it manually in your terminal by pressing `Ctrl^C`. 124 | 125 | If you don't specify any parameters it will produce 400 chunks/0.5 sec that the script tries to upload on the `http://localhost:1633` - that is the binded port of the queen node if you orchestrated the environment with the `envrionment.sh`. 126 | 127 | The following way you can pass parameter 128 | 129 | 1. MIN_CHEQUE_NUMBER - Minimum required cheques for Bee under the given BEE_DEBUG_API_URL. If -1 then it does not check for cheques [Number,Default:-1] 130 | 2. BEE_API_URL;BEE_DEBUG_API_URL - Bee API and Debug API URL separated by semicolon. The random data will sent to the Bee API URL, and the generated cheques will be checked on the Bee Debug URL. The two URLs should belong to different Bee clients as the generated data will propagate from that client to the network. [string,Default:'http://localhost:1633;http://localhost:11635'] 131 | 132 | ```sh 133 | $ npm run gen:traffic -- (...) 134 | ``` 135 | 136 | e.g. 137 | 138 | ```sh 139 | $ npm run gen:traffic -- 2 http://localhost:1633;http://localhost:11635 140 | ``` 141 | 142 | With the example above, random data will be generated until _minimum_ two cheques will generated on Bee client that serves debug endpoint `http://localhost:11635` 143 | -------------------------------------------------------------------------------- /orchestrator/builder/gen-traffic.js: -------------------------------------------------------------------------------- 1 | const { Bee, BeeDebug } = require('@ethersphere/bee-js') 2 | 3 | class BeePair { 4 | /** 5 | * @param {BeeDebug} chequeReceiverBeeDebug 6 | * @param {Bee} uploaderBee 7 | * @param {BeeDebug} uploaderBeeDebug 8 | * @param {string} uploaderStamp 9 | */ 10 | constructor(chequeReceiverBeeDebug, uploaderBee, uploaderBeeDebug, uploaderStamp) { 11 | this.chequeReceiverBeeDebug = chequeReceiverBeeDebug 12 | this.uploaderBee = uploaderBee 13 | this.uploaderBeeDebug = uploaderBeeDebug 14 | this.uploaderStamp = uploaderStamp 15 | } 16 | } 17 | 18 | const SLEEP_BETWEEN_UPLOADS_MS = 1000 19 | const POSTAGE_STAMPS_AMOUNT = '10000' 20 | const POSTAGE_STAMPS_DEPTH = 32 21 | 22 | /** 23 | * Lehmer random number generator with seed (minstd_rand in C++11) 24 | * !!! Very fast but not well distributed pseudo-random function !!! 25 | * 26 | * @param seed Seed for the pseudo-random generator 27 | */ 28 | function lrng(seed) { 29 | return () => ((2 ** 31 - 1) & (seed = Math.imul(48271, seed))) / 2 ** 31 30 | } 31 | 32 | /** 33 | * Utility function for generating random Buffer 34 | * !!! IT IS NOT CRYPTO SAFE !!! 35 | * For that use `crypto.randomBytes()` 36 | * 37 | * @param length Number of bytes to generate 38 | * @param seed Seed for the pseudo-random generator 39 | */ 40 | function randomByteArray(length, seed = 500) { 41 | const rand = lrng(seed) 42 | const buf = new Uint8Array(length) 43 | 44 | for (let i = 0; i < length; ++i) { 45 | buf[i] = (rand() * 0xff) << 0 46 | } 47 | 48 | return buf 49 | } 50 | 51 | /** 52 | * @param {BeePair} beePair 53 | */ 54 | async function uploadRandomBytes(beePair, seed = 500, bytes = 1024 * 4 * 400) { 55 | const randomBytes = randomByteArray(bytes, seed) 56 | const reference = await beePair.uploaderBee.uploadData(beePair.uploaderStamp, randomBytes) 57 | console.log(`${beePair.uploaderBee.url} uploaded ${bytes} bytes to ${reference}`) 58 | } 59 | 60 | const DEFAULT_POLLING_FREQUENCY = 1_000 61 | const DEFAULT_STAMP_USABLE_TIMEOUT = 120_000 62 | async function waitUntilStampUsable(batchId, beeDebug, options = {}) { 63 | const timeout = options?.timeout || DEFAULT_STAMP_USABLE_TIMEOUT 64 | const pollingFrequency = options?.pollingFrequency || DEFAULT_POLLING_FREQUENCY 65 | 66 | for (let i = 0; i < timeout; i += pollingFrequency) { 67 | const stamp = await beeDebug.getPostageBatch(batchId) 68 | 69 | if (stamp.usable) return stamp 70 | await sleep(pollingFrequency) 71 | } 72 | 73 | throw new Error('Wait until stamp usable timeout has been reached') 74 | } 75 | 76 | /** 77 | * Generate traffic on Bee node(s) 78 | * 79 | * @param {BeePair[]} beePairs 80 | */ 81 | async function genTrafficOnOpenPorts(beePairs) { 82 | const promises = beePairs.map(beePair => { 83 | return uploadRandomBytes(beePair, Date.now()) 84 | }) 85 | await Promise.all(promises) 86 | } 87 | 88 | function sleep(ms) { 89 | return new Promise(resolve => setTimeout(resolve, ms)) 90 | } 91 | 92 | /** 93 | * 94 | * Generates cheques on the given Bee API EP 95 | * 96 | * The hosts parameter has to be assimetric in the API;DEBUG_API paired string 97 | * because on the API EP the data will be generated, so the cheques should be 98 | * 99 | * @param {string[]} hosts API;DEBUG_API URL strings of the target Bee (e.g. http://localhost:1635;http://localhost:11633;http://localhost:11635) 100 | * @param {number} minCheques 101 | */ 102 | async function genTrafficLoop(hosts, minCheques) { 103 | const promises = hosts.map(async host => { 104 | const [chequeReceiverBeeDebugUrl, uploaderBeeUrl, uploaderBeeDebugUrl] = host.split(';') 105 | const chequeReceiverBeeDebug = new BeeDebug(chequeReceiverBeeDebugUrl) 106 | const uploaderBee = new Bee(uploaderBeeUrl) 107 | const uploaderBeeDebug = new BeeDebug(uploaderBeeDebugUrl) 108 | 109 | console.log(`Creating postage stamp on ${uploaderBeeDebugUrl}...`) 110 | const postageBatchId = await uploaderBeeDebug.createPostageBatch(POSTAGE_STAMPS_AMOUNT, POSTAGE_STAMPS_DEPTH) 111 | console.log(`Generated ${postageBatchId} postage stamp on ${uploaderBeeDebugUrl}. Waiting until it is usable.`) 112 | 113 | await waitUntilStampUsable(postageBatchId, uploaderBeeDebug) 114 | console.log('Postage stamp usable.') 115 | 116 | return new BeePair(chequeReceiverBeeDebug, uploaderBee, uploaderBeeDebug, postageBatchId) 117 | }) 118 | 119 | const bees = await Promise.all(promises) 120 | 121 | while (true) { 122 | await genTrafficOnOpenPorts(bees) 123 | 124 | if (!isNaN(minCheques)) { 125 | const beesUncashedCheques = [] 126 | for (const beePair of bees) { 127 | const { chequeReceiverBeeDebug } = beePair 128 | const { lastcheques } = await chequeReceiverBeeDebug.getLastCheques() 129 | const incomingCheques = lastcheques.filter(cheque => !!cheque.lastreceived) 130 | 131 | const uncashedCheques = [] 132 | const lastCashOutPromises = incomingCheques.map(({ peer }) => chequeReceiverBeeDebug.getLastCashoutAction(peer)) 133 | const lastCashOuts = await Promise.all(lastCashOutPromises) 134 | for (const [index, lastCashOut] of lastCashOuts.entries()) { 135 | if (BigInt(lastCashOut.uncashedAmount) > 0) { 136 | uncashedCheques.push(incomingCheques[index]) 137 | } 138 | } 139 | 140 | beesUncashedCheques.push(uncashedCheques) 141 | } 142 | if (beesUncashedCheques.every(uncashedCheques => uncashedCheques.length >= minCheques)) { 143 | console.log(`Generated at least ${minCheques} for every node on the given Debug API endpoints`) 144 | process.exit() 145 | } else { 146 | console.log( 147 | `There is not enough uncashed cheques on Bee node(s)`, 148 | beesUncashedCheques.map(beeCheques => beeCheques.length), 149 | ) 150 | } 151 | } 152 | 153 | await sleep(SLEEP_BETWEEN_UPLOADS_MS) 154 | } 155 | } 156 | 157 | let inputArray = process.argv.slice(2) 158 | // if there is no related input to the minimum required cheques count, 159 | // then the traffic generation will go infinitely 160 | let minCheques = parseInt(inputArray[0]) 161 | let hosts = inputArray.slice(1) 162 | if (hosts.length === 0) { 163 | hosts = ['http://localhost:1635;http://localhost:11633;http://localhost:11635'] 164 | } 165 | 166 | genTrafficLoop(hosts, minCheques) 167 | -------------------------------------------------------------------------------- /test/integration/start.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import Dockerode from 'dockerode' 3 | import crypto from 'crypto' 4 | 5 | import { run } from '../utils/run' 6 | import { ENV_ENV_PREFIX_KEY } from '../../src/constants' 7 | import { Bee, Reference } from '@ethersphere/bee-js' 8 | import { DockerError } from '../../src/utils/docker' 9 | import { findContainer, waitForUsablePostageStamp } from '../utils/docker' 10 | 11 | let testFailed = false 12 | 13 | function wrapper(fn: () => Promise): () => Promise { 14 | return async () => { 15 | try { 16 | return await fn() 17 | } catch (e) { 18 | testFailed = true 19 | throw e 20 | } 21 | } 22 | } 23 | 24 | async function stopNodes() { 25 | await run(['stop', '--rm']) // Cleanup the testing containers 26 | } 27 | 28 | describe('start command', () => { 29 | let docker: Dockerode 30 | let bee: Bee, beeDebug: Bee 31 | const envPrefix = `fdp-play-test-${crypto.randomBytes(4).toString('hex')}` 32 | 33 | beforeAll(() => { 34 | docker = new Dockerode() 35 | bee = new Bee('http://127.0.0.1:1633') 36 | beeDebug = new Bee('http://127.0.0.1:1633') 37 | 38 | // This will force Bee Factory to create 39 | process.env[ENV_ENV_PREFIX_KEY] = envPrefix 40 | }) 41 | 42 | afterEach(async () => { 43 | if (testFailed) { 44 | await run(['logs', 'queen']) 45 | } 46 | 47 | await stopNodes() 48 | }) 49 | 50 | it( 51 | 'should start cluster', 52 | wrapper(async () => { 53 | // As spinning the cluster with --detach the command will exit once the cluster is up and running 54 | await run(['start', '--detach']) 55 | 56 | await expect(findContainer(docker, 'queen')).resolves.toBeDefined() 57 | await expect(findContainer(docker, 'blockchain')).resolves.toBeDefined() 58 | await expect(findContainer(docker, 'worker-1')).resolves.toBeDefined() 59 | await expect(findContainer(docker, 'worker-2')).resolves.toBeDefined() 60 | await expect(findContainer(docker, 'worker-3')).resolves.toBeDefined() 61 | await expect(findContainer(docker, 'worker-4')).resolves.toBeDefined() 62 | 63 | await expect(beeDebug.getHealth()).resolves.toHaveProperty('status') 64 | }), 65 | ) 66 | 67 | describe('should start cluster without bee nodes', () => { 68 | beforeAll(async () => { 69 | await stopNodes() 70 | }) 71 | 72 | it( 73 | '', 74 | wrapper(async () => { 75 | // As spinning the cluster with --detach the command will exit once the cluster is up and running 76 | await run(['start', '--without-bees']) 77 | 78 | await expect(findContainer(docker, 'blockchain')).resolves.toBeDefined() 79 | await expect(findContainer(docker, 'queen')).rejects.toHaveProperty('statusCode', 404) 80 | await expect(findContainer(docker, 'worker-1')).rejects.toHaveProperty('statusCode', 404) 81 | await expect(findContainer(docker, 'worker-2')).rejects.toHaveProperty('statusCode', 404) 82 | await expect(findContainer(docker, 'worker-3')).rejects.toHaveProperty('statusCode', 404) 83 | await expect(findContainer(docker, 'worker-4')).rejects.toHaveProperty('statusCode', 404) 84 | }), 85 | ) 86 | }) 87 | 88 | describe('should start cluster with fairos node', () => { 89 | beforeAll(async () => { 90 | await stopNodes() 91 | }) 92 | 93 | it( 94 | '', 95 | wrapper(async () => { 96 | // As spinning the cluster with --detach the command will exit once the cluster is up and running 97 | await run(['start', '--fairos']) 98 | 99 | await expect(findContainer(docker, 'blockchain')).resolves.toBeDefined() 100 | await expect(findContainer(docker, 'worker-1')).resolves.toBeDefined() 101 | await expect(findContainer(docker, 'worker-2')).resolves.toBeDefined() 102 | await expect(findContainer(docker, 'worker-3')).resolves.toBeDefined() 103 | await expect(findContainer(docker, 'worker-4')).resolves.toBeDefined() 104 | await expect(findContainer(docker, 'fairos')).resolves.toBeDefined() 105 | }), 106 | ) 107 | }) 108 | 109 | describe('should start cluster with just few workers', () => { 110 | beforeAll(async () => { 111 | await stopNodes() 112 | }) 113 | 114 | it( 115 | '', 116 | wrapper(async () => { 117 | // As spinning the cluster with --detach the command will exit once the cluster is up and running 118 | await run(['start', '--detach', '--workers', '2']) 119 | 120 | await expect(findContainer(docker, 'queen')).resolves.toBeDefined() 121 | await expect(findContainer(docker, 'blockchain')).resolves.toBeDefined() 122 | await expect(findContainer(docker, 'worker-1')).resolves.toBeDefined() 123 | await expect(findContainer(docker, 'worker-2')).resolves.toBeDefined() 124 | await expect(findContainer(docker, 'worker-3')).rejects.toHaveProperty('statusCode', 404) 125 | await expect(findContainer(docker, 'worker-4')).rejects.toHaveProperty('statusCode', 404) 126 | 127 | await expect(beeDebug.getHealth()).resolves.toHaveProperty('status') 128 | }), 129 | ) 130 | }) 131 | 132 | describe('should create docker network', () => { 133 | beforeAll(async () => { 134 | await stopNodes() 135 | 136 | try { 137 | // Make sure the network does not exists 138 | await docker.getNetwork(`${envPrefix}-network`).remove() 139 | } catch (e) { 140 | if ((e as DockerError).statusCode !== 404) { 141 | throw e 142 | } 143 | } 144 | }) 145 | 146 | it( 147 | '', 148 | wrapper(async () => { 149 | await run(['start', '--detach', '--without-bees']) 150 | 151 | expect(docker.getNetwork(`${envPrefix}-network`)).toBeDefined() 152 | }), 153 | ) 154 | }) 155 | 156 | describe('should remove containers with --fresh option', () => { 157 | let reference: Reference, data: string 158 | 159 | beforeAll(async () => { 160 | console.log('(before) Starting up Bee Factory') 161 | await run(['start', '--detach']) 162 | 163 | console.log('(before) Creating postage stamp ') 164 | const postage = await beeDebug.createPostageBatch('414720000', 18) 165 | 166 | console.log('(before) Waiting for the postage stamp to be usable') 167 | await waitForUsablePostageStamp(beeDebug, postage) 168 | data = `hello from ${Date.now()}` 169 | reference = (await bee.uploadData(postage, data, { deferred: false })).reference 170 | 171 | // Lets just verify that it the current container has the data 172 | expect((await bee.downloadData(reference)).text()).toEqual(data) 173 | 174 | console.log('(before) Stopping the Bee Factory') 175 | await run(['stop']) 176 | }) 177 | 178 | it( 179 | '', 180 | wrapper(async () => { 181 | console.log('(test) Starting the Bee Factory') 182 | await run(['start', '--fresh', '--detach']) 183 | 184 | console.log('(test) Trying to fetch the data') 185 | // locally 'timeout of 1000ms exceeded' 186 | // ci 'Request failed with status code 404' 187 | expect(bee.downloadData(reference, { timeout: 1000 })).rejects.toBeTruthy() 188 | }), 189 | ) 190 | }) 191 | }) 192 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FDP Play 2 | 3 | [![Tests](https://github.com/fairDataSociety/fdp-play/actions/workflows/test.yaml/badge.svg)](https://github.com/fairDataSociety/fdp-play/actions/workflows/test.yaml) 4 | [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard) 5 | ![](https://img.shields.io/badge/npm-%3E%3D8.1.0-orange.svg?style=flat-square) 6 | ![](https://img.shields.io/badge/Node.js-%3E%3D16.0.0-orange.svg?style=flat-square) 7 | 8 | > CLI tool to spin up local development Bee cluster and FDP environment with Docker 9 | 10 | **Warning: This project is in beta state. There might (and most probably will) be changes in the future to its API and working. Also, no guarantees can be made about its stability, efficiency, and security at this stage.** 11 | 12 | ## [Short tutorial video](https://www.youtube.com/watch?v=Mt5468WzWaA) ▶️ ⏯️ 13 | 14 | ## Table of Contents 15 | - [Install](#install) 16 | - [Usage](#usage) 17 | - [Blockchain](#blockchain) 18 | - [Contribute](#contribute) 19 | - [Maintainers](#maintainers) 20 | - [Troubleshooting](#troubleshooting) 21 | - [License](#license) 22 | 23 | ## Install 24 | 25 | **Requirements:** Docker 26 | 27 | ```shell 28 | $ npm install -g @fairdatasociety/fdp-play 29 | ``` 30 | 31 | ## Usage 32 | 33 | ```shell 34 | # Listing of available commands and print help menu 35 | $ fdp-play --help 36 | 37 | # The spins up the cluster using the latest supported Bee version. 38 | $ fdp-play start --detach 39 | 40 | # This spins up the cluster for specific Bee version and exits 41 | $ fdp-play start -d --bee-version 1.6.1 42 | 43 | # This spins up the environment without Bee nodes 44 | $ fdp-play start --without-bees 45 | 46 | # Starts the blockchain with FDP Contracts 47 | $ fdp-play start --fdp-contracts 48 | 49 | # Or start a fairOS instance that will use the Queen Bee node. 50 | $ fdp-play start --fairos 51 | 52 | # This will clean the containers before start (fresh) and tries to pull the latest images from the Docker repository (pull) 53 | # NOTE: best to use this if something went wrong. 54 | $ fdp-play start --pull --fresh 55 | 56 | # The spins up the cluster using specific blockchain image. 57 | # NOTE: The fairdatasociety/fdp-play-blockchain is the base blockchain image that only contains pre-funded accounts for Bee nodes. 58 | $ fdp-play start --detach --blockchain-image fairdatasociety/fdp-play-blockchain 59 | 60 | # This attaches to the Queen container and displays its logs 61 | $ fdp-play logs queen --follow 62 | 63 | # This stops the cluster and keeping the containers so next time they are spinned up the data are kept 64 | # but data are not persisted across version's bump! 65 | $ fdp-play stop 66 | 67 | # You can also spin up the cluster without the --detach which then directly 68 | # attaches to the Queen logs and the cluster is terminated upon SIGINT (Ctrl+C) 69 | $ fdp-play start 70 | 71 | # Send ETH to any address 72 | $ fdp-play eth send --to 0xb0baf37740204020402040204020402040204020 -a 0.5 73 | 74 | # Query the ETH balance of any Ethereum address 75 | $ fdp-play eth balance 0xCEeE442a149784faa65C35e328CCd64d874F9a02 76 | ``` 77 | 78 | For more details see the `--help` page of the CLI and its commands. 79 | 80 | ## Blockchain 81 | 82 | A [go-ethereum](https://geth.ethereum.org/) node is runnig for operating blockchain in the FDP Play environment. 83 | Set http://localhost:9545 for RPC connection and its websocket connection is available on ws://localhost:9546. 84 | 85 | The CLI offers some interactions with the blockchain with the `fdp-play eth` subcommands. 86 | 87 | A new block is generated every 5 seconds. 88 | 89 | ### Funded Wallets 90 | 91 | Some addresses are pre-funded on the [fdp-play-blockchain](https://hub.docker.com/r/fairdatasociety/fdp-play-blockchain) 92 | [by the genesis block](orchestrator/builder/blockchain/genesis.json). 93 | 94 | All legacy test wallets (from Ganache era) are funded with 1000 ETH: 95 | ``` 96 | Wallet addresses 97 | ================== 98 | (0) 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1 (1000 ETH) -> minus expenses for contract creations 99 | (1) 0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0 (1000 ETH) 100 | (2) 0x22d491Bde2303f2f43325b2108D26f1eAbA1e32b (1000 ETH) 101 | (3) 0xE11BA2b4D45Eaed5996Cd0823791E0C93114882d (1000 ETH) 102 | (4) 0xd03ea8624C8C5987235048901fB614fDcA89b117 (1000 ETH) 103 | (5) 0x95cED938F7991cd0dFcb48F0a06a40FA1aF46EBC (1000 ETH) 104 | (6) 0x3E5e9111Ae8eB78Fe1CC3bb8915d5D461F3Ef9A9 (1000 ETH) 105 | (7) 0x28a8746e75304c0780E011BEd21C72cD78cd535E (1000 ETH) 106 | (8) 0xACa94ef8bD5ffEE41947b4585a84BdA5a3d3DA6E (1000 ETH) 107 | (9) 0x1dF62f291b2E969fB0849d99D9Ce41e2F137006e (1000 ETH) 108 | 109 | Private Keys 110 | ================== 111 | (0) 0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d 112 | (1) 0x6cbed15c793ce57650b9877cf6fa156fbef513c4e6134f022a85b1ffdd59b2a1 113 | (2) 0x6370fd033278c143179d81c5526140625662b8daa446c22ee2d73db3707e620c 114 | (3) 0x646f1ce2fdad0e6deeeb5c7e8e5543bdde65e86029e2fd9fc169899c440a7913 115 | (4) 0xadd53f9a7e588d003326d1cbf9e4a43c061aadd9bc938c843a79e7b4fd2ad743 116 | (5) 0x395df67f0c2d2d9fe1ad08d1bc8b6627011959b79c53d7dd6a3536a33ab8a4fd 117 | (6) 0xe485d098507f54e7733a205420dfddbe58db035fa577fc294ebd14db90767a52 118 | (7) 0xa453611d9419d0e56f499079478fd72c37b251a94bfde4d19872c44cf65386e3 119 | (8) 0x829e924fdf021ba3dbbc4225edfece9aca04b929d6e75613329ca6f1d31c0bb4 120 | (9) 0xb0057716d5917badaf911b193b12b910811c1497b5bada8d7711f758981c3773 121 | ``` 122 | 123 | Additionally, the miner account - its wallet is in the [geth data folder](orchestrator/builder/blockchain/.ethereum) - 124 | is also founded with 1000 ETH. Its address is `0xCEeE442a149784faa65C35e328CCd64d874F9a02`. 125 | 126 | ### Bee Node Keys 127 | 128 | The bee node keys are stored in the [bee-data-dirs](orchestrator/builder/bee-data-dirs) folder. Their private keys are the following: 129 | ``` 130 | fdp-play-queen: 566058308ad5fa3888173c741a1fb902c9f1f19559b11fc2738dfc53637ce4e9 131 | fdp-play-worker-1: 195cf6324303f6941ad119d0a1d2e862d810078e1370b8d205552a543ff40aab 132 | fdp-play-worker-2: fd212ce5d23adbd3738fa5e34e57e538ace57c628bacb6b25630732582dfd495 133 | fdp-play-worker-3: 91700026709ddaddb282c07a0eff0295be8a44f2d92449068936b6d87db1a12e 134 | fdp-play-worker-4: cfb3f14067e2146a72c31f903dc3944a7ec25ecb84d9e688d6ec40a7570dfe8b 135 | ``` 136 | 137 | With the geth wallet any sort of action can be performed since it is unlocked and all API services are open. 138 | 139 | ### Docker Images 140 | 141 | Bee Factory as the NPM package that you can install, like mentioned above, works in a way that it orchestrates launching FDP Play Docker images 142 | in correct order and awaits for certain initializations to happen in correct form. 143 | 144 | ## Contribute 145 | 146 | There are some ways you can make this module better: 147 | 148 | - Consult our [open issues](https://github.com/fairDataSociety/fdp-play/issues) and take on one of them 149 | - Help our tests reach 100% coverage! 150 | - Join us in our [FDS Discord chat](https://discord.gg/KrVTmahcUA) in the #fdp-general channel if you have questions or want to give feedback 151 | 152 | ### Developing 153 | 154 | You can run the CLI while developing using `npm start -- ...`. 155 | 156 | ## Maintainers 157 | 158 | - [nugaon](https://github.com/nugaon) 159 | 160 | ## Troubleshooting 161 | 162 | ### Message: Failed to run command : connect EACCES /var/run/docker.sock 163 | Running `npm` + `command` results in message: 164 | ``` 165 | █ Failed to run command! 166 | 167 | connect EACCES /var/run/docker.sock 168 | ``` 169 | 170 | Try troubleshooting Docker as guided on [Stackoverflow thread](https://stackoverflow.com/questions/52364905/after-executing-following-code-of-dockerode-npm-getting-error-connect-eacces-v). 171 | 172 | ## License 173 | 174 | [BSD-3-Clause](./LICENSE) 175 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [3.3.0](https://github.com/fairDataSociety/fdp-play/compare/v3.2.0...v3.3.0) (2025-05-12) 4 | 5 | 6 | ### Features 7 | 8 | * update contracts ([3075c60](https://github.com/fairDataSociety/fdp-play/commit/3075c6091982a11deebf2940cccf734fa48d5411)) 9 | 10 | 11 | ### Bug Fixes 12 | 13 | * add allow-private-cidrs bee configuration ([88c66fe](https://github.com/fairDataSociety/fdp-play/commit/88c66fe8b41e1dfa3318f5f1c86bff919b963839)) 14 | * add blockchain rpc endpoint option ([#151](https://github.com/fairDataSociety/fdp-play/issues/151)) ([8a479e4](https://github.com/fairDataSociety/fdp-play/commit/8a479e4dd56bd57f7708a88e5f07175577eba639)) 15 | * api changes for peers endpoint in bee ([142f53c](https://github.com/fairDataSociety/fdp-play/commit/142f53c2164efca827f69ff0c0db66fe1e28bc2f)) 16 | * bee instead of beedebug with bee-js ([6e44910](https://github.com/fairDataSociety/fdp-play/commit/6e44910228f3c773ceaaf5cbcb6bd866b5db3fa3)) 17 | * blockchain start from bash ([50ab1bb](https://github.com/fairDataSociety/fdp-play/commit/50ab1bba889f7d6ded8974d1a3a9484afa418ed3)) 18 | * blocked connection from blocklisted peer ([e22e004](https://github.com/fairDataSociety/fdp-play/commit/e22e004ab22fc4363bc4403f3505bea4bb95832f)) 19 | * **hardhat:** fdpPlay with ipv4 url ([4560984](https://github.com/fairDataSociety/fdp-play/commit/4560984813cccab008272b6f6ee410ca3c7ad3e1)) 20 | * set bootnode false and warmup time ([1c96887](https://github.com/fairDataSociety/fdp-play/commit/1c968875d2365ce3358f9d6257ee6c9519520796)) 21 | 22 | ## [3.2.0](https://github.com/fairDataSociety/fdp-play/compare/v3.1.0...v3.2.0) (2024-09-12) 23 | 24 | 25 | ### Features 26 | 27 | * bee 2.2 ([#147](https://github.com/fairDataSociety/fdp-play/issues/147)) ([3c91aa6](https://github.com/fairDataSociety/fdp-play/commit/3c91aa6bfb19601d382666fa87916e3882870b3b)) 28 | 29 | ## [3.1.0](https://github.com/fairDataSociety/fdp-play/compare/v3.0.1...v3.1.0) (2024-06-14) 30 | 31 | 32 | ### Features 33 | 34 | * fdp contracts option ([#123](https://github.com/fairDataSociety/fdp-play/issues/123)) ([147d41d](https://github.com/fairDataSociety/fdp-play/commit/147d41da27391aa40851b0159ee9f30ccd943860)) 35 | 36 | 37 | ### Bug Fixes 38 | 39 | * node 20 support ([#141](https://github.com/fairDataSociety/fdp-play/issues/141)) ([d615c01](https://github.com/fairDataSociety/fdp-play/commit/d615c0155cae4c06cf1980462c3ca69a3eec2bfc)) 40 | * node fetch ([#144](https://github.com/fairDataSociety/fdp-play/issues/144)) ([ef6d623](https://github.com/fairDataSociety/fdp-play/commit/ef6d623c33d8837f21904e785b6493593d47ba9e)) 41 | 42 | ## [3.0.1](https://github.com/fairDataSociety/fdp-play/compare/v3.0.0...v3.0.1) (2024-03-03) 43 | 44 | 45 | ### Bug Fixes 46 | 47 | * biggest painpoints ([#111](https://github.com/fairDataSociety/fdp-play/issues/111)) ([62cc552](https://github.com/fairDataSociety/fdp-play/commit/62cc552537b6df40ba35df7b497e4dc0d7064103)) 48 | 49 | ## [3.0.0](https://github.com/fairDataSociety/fdp-play/compare/v2.2.0...v3.0.0) (2023-11-29) 50 | 51 | 52 | ### ⚠ BREAKING CHANGES 53 | 54 | * geth blockchain and hardhat ([#105](https://github.com/fairDataSociety/fdp-play/issues/105)) 55 | 56 | ### Features 57 | 58 | * bee 1.17.2 ([#97](https://github.com/fairDataSociety/fdp-play/issues/97)) ([1fea708](https://github.com/fairDataSociety/fdp-play/commit/1fea708aa9610d79ba031e7127cf50d8adedee23)) 59 | * geth blockchain and hardhat ([#105](https://github.com/fairDataSociety/fdp-play/issues/105)) ([6d6da0b](https://github.com/fairDataSociety/fdp-play/commit/6d6da0b19a9b014bb11b396a8d62c2d65774a0c0)) 60 | * phase4 redis ([#102](https://github.com/fairDataSociety/fdp-play/issues/102)) ([c6a6f7f](https://github.com/fairDataSociety/fdp-play/commit/c6a6f7fd94f1f4fe7a53d8003a63cef60aea7a3e)) 61 | 62 | 63 | ### Bug Fixes 64 | 65 | * byte updates ([a9a01aa](https://github.com/fairDataSociety/fdp-play/commit/a9a01aa6da9ca6435b3964f39ab4d19ddc23cf94)) 66 | * contract migration for the new bytecode ([0a6f877](https://github.com/fairDataSociety/fdp-play/commit/0a6f877827ab23baac0e842b726e3d2eaeecd902)) 67 | * update contract addresses ([548bc22](https://github.com/fairDataSociety/fdp-play/commit/548bc22d86a7177ba239fb568de3c64d0bc69d0e)) 68 | * update contracts bytecode ([1aa9ee2](https://github.com/fairDataSociety/fdp-play/commit/1aa9ee28ed5610da74b8b2b895dafdd553eace7e)) 69 | 70 | ## [2.2.0](https://github.com/fairDataSociety/fdp-play/compare/v2.1.1...v2.2.0) (2023-06-07) 71 | 72 | 73 | ### Features 74 | 75 | * eth command ([#73](https://github.com/fairDataSociety/fdp-play/issues/73)) ([0ae40de](https://github.com/fairDataSociety/fdp-play/commit/0ae40de30abfbc4414ee8609c3996dc077e5977a)) 76 | * stateful environment script ([#87](https://github.com/fairDataSociety/fdp-play/issues/87)) ([50b2037](https://github.com/fairDataSociety/fdp-play/commit/50b2037e63f695e416dc94d4465dab9a10d2bcb3)) 77 | 78 | 79 | ### Bug Fixes 80 | 81 | * **generator:** bee worker startup ([b49f172](https://github.com/fairDataSociety/fdp-play/commit/b49f1723ed09835b254e248a67f894250676d467)) 82 | * node 18 error ([#92](https://github.com/fairDataSociety/fdp-play/issues/92)) ([6b39a98](https://github.com/fairDataSociety/fdp-play/commit/6b39a98a75a666cd98cda2d73772b3f685a5dbfa)) 83 | 84 | ## [2.1.1](https://github.com/fairDataSociety/fdp-play/compare/v2.1.0...v2.1.1) (2023-02-02) 85 | 86 | 87 | ### Bug Fixes 88 | 89 | * starting bee cli params ([#83](https://github.com/fairDataSociety/fdp-play/issues/83)) ([dc9477c](https://github.com/fairDataSociety/fdp-play/commit/dc9477c8873aef20da5f803fe2681ef183647781)) 90 | 91 | ## [2.1.0](https://github.com/fairDataSociety/fdp-play/compare/v2.0.2...v2.1.0) (2023-01-31) 92 | 93 | 94 | ### Features 95 | 96 | * update bee and blockchains ([#81](https://github.com/fairDataSociety/fdp-play/issues/81)) ([f840737](https://github.com/fairDataSociety/fdp-play/commit/f840737e4b084fd51749ba848c667043a073d123)) 97 | 98 | ## [2.0.2](https://github.com/fairDataSociety/fdp-play/compare/v2.0.1...v2.0.2) (2022-08-18) 99 | 100 | 101 | ### Bug Fixes 102 | 103 | * latest fairos support ([#29](https://github.com/fairDataSociety/fdp-play/issues/29)) ([3bd37e2](https://github.com/fairDataSociety/fdp-play/commit/3bd37e24402207da42e6a847bd4488fba0f8f915)) 104 | 105 | ## [2.0.1](https://github.com/fairDataSociety/fdp-play/compare/v2.0.0...v2.0.1) (2022-07-18) 106 | 107 | 108 | ### Bug Fixes 109 | 110 | * pull option ([91228f2](https://github.com/fairDataSociety/fdp-play/commit/91228f2ad2748686143fcaeb1aaf8f0d9437d858)) 111 | 112 | ## [2.0.0](https://github.com/fairDataSociety/fdp-play/compare/v1.0.2...v2.0.0) (2022-07-07) 113 | 114 | ### Breaking Changes 115 | * From now, the bee version is possible to be defined with the `--bee-version` option instead of passing as an argument. 116 | * Bee versions cannot be defined by package.json or bee-factory.json files. 117 | 118 | ### Features 119 | 120 | * fairos support ([#21](https://github.com/fairDataSociety/fdp-play/issues/21)) ([7556674](https://github.com/fairDataSociety/fdp-play/commit/75566746000a36296fddf3efe737038be39ff25b)) 121 | * use latest image for bee containers and pull option ([#18](https://github.com/fairDataSociety/fdp-play/issues/18)) ([077989d](https://github.com/fairDataSociety/fdp-play/commit/077989dfe747cd5d1c10a4cc29b8104315fd3c9b)) 122 | * start environment without bees ([#20](https://github.com/fairDataSociety/fdp-play/issues/20)) ([2a36a09](https://github.com/fairDataSociety/fdp-play/commit/2a36a097f1a90294772be2ef9574f890f67566b3)) 123 | 124 | 125 | ### Miscellaneous Chores 126 | 127 | * release 2.0.0 ([3307ca2](https://github.com/fairDataSociety/fdp-play/commit/3307ca25f61b721122cec856a58bc59f31a4b413)) 128 | 129 | ## [1.0.2](https://github.com/fairDataSociety/fdp-play/compare/v1.0.1...v1.0.2) (2022-06-29) 130 | 131 | 132 | ### Bug Fixes 133 | 134 | * pass blockchain image option with version ([#13](https://github.com/fairDataSociety/fdp-play/issues/13)) ([8ddaa71](https://github.com/fairDataSociety/fdp-play/commit/8ddaa710f92c17462e7a16c8a944fac5da588b77)) 135 | 136 | ## [1.0.1](https://github.com/fairDataSociety/fdp-play/compare/v1.0.0...v1.0.1) (2022-06-28) 137 | 138 | 139 | ### Bug Fixes 140 | 141 | * package json ([#11](https://github.com/fairDataSociety/fdp-play/issues/11)) ([92bb879](https://github.com/fairDataSociety/fdp-play/commit/92bb879c8de6910f005ee338926b72a7ff74d9a4)) 142 | 143 | ## 1.0.0 (2022-06-28) 144 | 145 | 146 | ### Features 147 | 148 | * generator ([#2](https://github.com/fairDataSociety/fdp-play/issues/2)) ([7104e7b](https://github.com/fairDataSociety/fdp-play/commit/7104e7b42b9c4e4ce8e9091e3cead5a571add81d)) 149 | * init ([e3168e3](https://github.com/fairDataSociety/fdp-play/commit/e3168e3aba522e3bade8fca84905f8c3c9dc6a59)) 150 | * let's roll ([#1](https://github.com/fairDataSociety/fdp-play/issues/1)) ([e18bf88](https://github.com/fairDataSociety/fdp-play/commit/e18bf882fe2c55e97adc9a0069263b26be36ac9e)) 151 | -------------------------------------------------------------------------------- /src/command/start.ts: -------------------------------------------------------------------------------- 1 | import { LeafCommand, Option } from 'furious-commander' 2 | import { RootCommand } from './root-command' 3 | import { 4 | ContainerType, 5 | DEFAULT_ENV_PREFIX, 6 | DEFAULT_BEE_IMAGE_PREFIX, 7 | Docker, 8 | RunOptions, 9 | WORKER_COUNT, 10 | } from '../utils/docker' 11 | import { waitForBlockchain, waitForQueen, waitForWorkers } from '../utils/wait' 12 | import ora from 'ora' 13 | import { VerbosityLevel } from './root-command/logging' 14 | import { stripCommit } from '../utils/config-sources' 15 | import { DEFAULT_BLOCKCHAIN_IMAGE, DEFAULT_FAIROS_IMAGE, ENV_ENV_PREFIX_KEY, FDP_BLOCKCHAIN_IMAGE } from '../constants' 16 | 17 | const DEFAULT_BEE_REPO = 'fairdatasociety' 18 | const ENV_IMAGE_PREFIX_KEY = 'FDP_PLAY_IMAGE_PREFIX' 19 | const ENV_REPO_KEY = 'FDP_PLAY_DOCKER_REPO' 20 | const ENV_DETACH_KEY = 'FDP_PLAY_DETACH' 21 | const ENV_WORKERS_KEY = 'FDP_PLAY_WORKERS' 22 | const ENV_FRESH_KEY = 'FDP_PLAY_FRESH' 23 | 24 | export class Start extends RootCommand implements LeafCommand { 25 | public readonly name = 'start' 26 | 27 | public readonly description = 'Spin up the FDP Play cluster. Before this, please make sure Docker Service is running!' 28 | 29 | @Option({ 30 | key: 'fresh', 31 | alias: 'f', 32 | type: 'boolean', 33 | description: 'The cluster data will be purged before start', 34 | envKey: ENV_FRESH_KEY, 35 | default: false, 36 | }) 37 | public fresh!: boolean 38 | 39 | @Option({ 40 | key: 'pull', 41 | type: 'boolean', 42 | description: 'The Docker images will be pulled from the Docker repository', 43 | default: false, 44 | }) 45 | public pullImage!: boolean 46 | 47 | @Option({ 48 | key: 'detach', 49 | alias: 'd', 50 | type: 'boolean', 51 | description: 'Spin up the cluster and exit. No logging is outputted.', 52 | envKey: ENV_DETACH_KEY, 53 | default: false, 54 | }) 55 | public detach!: boolean 56 | 57 | @Option({ 58 | key: 'workers', 59 | alias: 'w', 60 | type: 'number', 61 | description: `Number of workers to spin. Value between 0 and ${WORKER_COUNT} including.`, 62 | envKey: ENV_WORKERS_KEY, 63 | default: WORKER_COUNT, 64 | }) 65 | public workers!: number 66 | 67 | @Option({ 68 | key: 'bee-repo', 69 | type: 'string', 70 | description: 'Docker repository of the Bee images', 71 | envKey: ENV_REPO_KEY, 72 | default: DEFAULT_BEE_REPO, 73 | }) 74 | public beeRepo!: string 75 | 76 | @Option({ 77 | key: 'blockchain-image', 78 | type: 'string', 79 | description: 'Docker image name of the used blockchain', 80 | envKey: 'FDP_PLAY_BLOCKCHAIN_IMAGE', 81 | }) 82 | public blockchainImageName!: string 83 | 84 | @Option({ 85 | key: 'fdp-contracts', 86 | type: 'boolean', 87 | description: 'Blockchain includes FDP Contracts', 88 | default: false, 89 | conflicts: 'blockchain-image', 90 | }) 91 | public fdpContracts!: boolean 92 | 93 | @Option({ 94 | key: 'bee-image-prefix', 95 | type: 'string', 96 | description: 'Docker bee image name prefix', 97 | envKey: ENV_IMAGE_PREFIX_KEY, 98 | default: DEFAULT_BEE_IMAGE_PREFIX, 99 | }) 100 | public beeImagePrefix!: string 101 | 102 | @Option({ 103 | key: 'env-prefix', 104 | type: 'string', 105 | description: "Docker container's names prefix", 106 | envKey: ENV_ENV_PREFIX_KEY, 107 | default: DEFAULT_ENV_PREFIX, 108 | }) 109 | public envPrefix!: string 110 | 111 | @Option({ key: 'bee-version', description: 'Bee image version', required: false }) 112 | public beeVersion!: string 113 | 114 | @Option({ 115 | key: 'without-bees', 116 | type: 'boolean', 117 | description: 'Start environment without Bee clients', 118 | required: false, 119 | default: false, 120 | }) 121 | public withoutBees!: boolean 122 | 123 | @Option({ 124 | key: 'fairos', 125 | type: 'boolean', 126 | description: 'Start FairOS instance', 127 | required: false, 128 | default: false, 129 | }) 130 | public fairos!: boolean 131 | 132 | @Option({ key: 'fairos-image', description: 'FairOS Docker image', required: false, default: DEFAULT_FAIROS_IMAGE }) 133 | public fairosImage!: string 134 | 135 | public async run(): Promise { 136 | await super.init() 137 | 138 | if (this.workers < 0 || this.workers > WORKER_COUNT) { 139 | throw new Error(`Worker count has to be between 0 and ${WORKER_COUNT} including.`) 140 | } 141 | 142 | if (!this.beeVersion) { 143 | this.beeVersion = 'latest' 144 | this.console.info('Bee version not specified. Using image with the latest tag') 145 | } 146 | 147 | this.beeVersion = stripCommit(this.beeVersion) 148 | 149 | this.setBlockchainImage() 150 | 151 | const dockerOptions = this.buildDockerOptions() 152 | const docker = new Docker({ 153 | console: this.console, 154 | envPrefix: this.envPrefix, 155 | beeImagePrefix: this.beeImagePrefix, 156 | blockchainImageName: this.blockchainImageName, 157 | beeRepo: this.beeRepo, 158 | fairOsImage: this.fairosImage, 159 | }) 160 | const status = await docker.getAllStatus() 161 | 162 | if (Object.values(status).every(st => st === 'running')) { 163 | this.console.log('All containers are up and running') 164 | 165 | if (this.detach) { 166 | return 167 | } 168 | 169 | await docker.logs(ContainerType.QUEEN, process.stdout) 170 | } 171 | 172 | let queenAddress: string 173 | 174 | process.on('SIGINT', async () => { 175 | try { 176 | await docker.stopAll(false) 177 | } catch (e) { 178 | this.console.error(`Error: ${e}`) 179 | } 180 | 181 | process.exit() 182 | }) 183 | 184 | const networkSpinner = ora({ 185 | text: 'Spawning network...', 186 | spinner: 'point', 187 | color: 'yellow', 188 | isSilent: this.verbosity === VerbosityLevel.Quiet, 189 | }).start() 190 | 191 | try { 192 | await docker.createNetwork() 193 | networkSpinner.succeed('Network is up') 194 | } catch (e) { 195 | networkSpinner.fail(`Impossible to spawn network!`) 196 | throw e 197 | } 198 | 199 | const blockchainSpinner = ora({ 200 | text: 'Getting blockchain image version...', 201 | spinner: 'point', 202 | color: 'yellow', 203 | isSilent: this.verbosity === VerbosityLevel.Quiet, 204 | }).start() 205 | 206 | try { 207 | blockchainSpinner.text = 'Starting blockchain node...' 208 | await docker.startBlockchainNode(dockerOptions) 209 | blockchainSpinner.text = 'Waiting until blockchain is ready...' 210 | await waitForBlockchain() 211 | blockchainSpinner.succeed('Blockchain node is up and listening') 212 | } catch (e) { 213 | blockchainSpinner.fail(`Impossible to start blockchain node!`) 214 | await this.stopDocker(docker) 215 | throw e 216 | } 217 | 218 | if (this.withoutBees) { 219 | if (!this.detach) { 220 | await docker.logs(ContainerType.BLOCKCHAIN, process.stdout, true) 221 | } 222 | 223 | return 224 | } 225 | 226 | const queenSpinner = ora({ 227 | text: 'Starting queen Bee node...', 228 | spinner: 'point', 229 | color: 'yellow', 230 | isSilent: this.verbosity === VerbosityLevel.Quiet, 231 | }).start() 232 | 233 | try { 234 | await docker.startQueenNode(this.beeVersion, dockerOptions) 235 | queenSpinner.text = 'Waiting until queen node is ready...' 236 | queenAddress = await waitForQueen( 237 | async () => (await docker.getStatusForContainer(ContainerType.QUEEN)) === 'running', 238 | ) 239 | queenSpinner.succeed('Queen node is up and listening') 240 | } catch (e) { 241 | queenSpinner.fail(`Impossible to start queen node: ${(e as Error).message}`) 242 | await this.stopDocker(docker) 243 | throw e 244 | } 245 | 246 | if (this.workers > 0) { 247 | const workerSpinner = ora({ 248 | text: 'Starting worker Bee nodes...', 249 | spinner: 'point', 250 | color: 'yellow', 251 | isSilent: this.verbosity === VerbosityLevel.Quiet, 252 | }).start() 253 | 254 | try { 255 | for (let i = 1; i <= this.workers; i++) { 256 | await docker.startWorkerNode(this.beeVersion, i, queenAddress, dockerOptions) 257 | } 258 | 259 | workerSpinner.text = 'Waiting until all workers connect to queen...' 260 | await waitForWorkers(this.workers, docker.getAllStatus.bind(docker)) 261 | workerSpinner.succeed('Worker nodes are up and listening') 262 | } catch (e) { 263 | workerSpinner.fail(`Impossible to start worker nodes! ${(e as Error).message}`) 264 | await this.stopDocker(docker) 265 | throw e 266 | } 267 | } 268 | 269 | // start FairOS instance 270 | if (this.fairos) { 271 | const workerSpinner = ora({ 272 | text: 'Starting FairOS node...', 273 | spinner: 'point', 274 | color: 'yellow', 275 | isSilent: this.verbosity === VerbosityLevel.Quiet, 276 | }).start() 277 | 278 | try { 279 | await docker.startFairOs(dockerOptions) 280 | 281 | workerSpinner.succeed('FairOS node is up and listening') 282 | } catch (e) { 283 | workerSpinner.fail(`Impossible to start FairOS node!`) 284 | await this.stopDocker(docker) 285 | throw e 286 | } 287 | 288 | if (!this.detach) { 289 | await docker.logs(ContainerType.FAIROS, process.stdout, true) 290 | } 291 | 292 | return 293 | } 294 | 295 | if (!this.detach) { 296 | await docker.logs(ContainerType.QUEEN, process.stdout, true) 297 | } 298 | } 299 | 300 | private setBlockchainImage() { 301 | if (this.fdpContracts || this.fairos) { 302 | this.blockchainImageName = FDP_BLOCKCHAIN_IMAGE 303 | } else { 304 | this.blockchainImageName ||= DEFAULT_BLOCKCHAIN_IMAGE 305 | } 306 | } 307 | 308 | private async stopDocker(docker: Docker) { 309 | const dockerSpinner = ora({ 310 | text: 'Stopping all containers...', 311 | spinner: 'point', 312 | color: 'red', 313 | isSilent: this.verbosity === VerbosityLevel.Quiet, 314 | }).start() 315 | 316 | await docker.stopAll(false) 317 | 318 | dockerSpinner.stop() 319 | } 320 | 321 | private buildDockerOptions(): RunOptions { 322 | return { 323 | fresh: this.fresh, 324 | pullImage: this.pullImage, 325 | } 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /orchestrator/scripts/deploy.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import FS from 'fs' 3 | import Path from 'path' 4 | import { ethers } from 'hardhat' 5 | import type { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers' 6 | import AccessControl from '@openzeppelin/contracts/build/contracts/AccessControl.json' 7 | import { keccak256 } from 'ethers' 8 | 9 | const contractAddressesPath = Path.join(__dirname, '..', 'contract-addresses.json') 10 | const NETWORK_ID = 4020 11 | 12 | function saveContractAddresses(jsonData: unknown) { 13 | FS.writeFileSync(contractAddressesPath, JSON.stringify(jsonData, null, 2)) 14 | } 15 | 16 | function prefixedAddressParamToByteCode(address: string) { 17 | // the first 2 chars removal removes 0x prefix 18 | return address.substring(2).toLowerCase().padStart(64, '0') 19 | } 20 | 21 | function intToByteCode(intParam: number) { 22 | return intParam.toString(16).padStart(64, '0') 23 | } 24 | 25 | function getSimpleSwapFactoryBin(tokenAddress: string) { 26 | const binPath = Path.join(__dirname, '..', 'contracts', 'SimpleSwapFactory.bytecode') 27 | const bin = FS.readFileSync(binPath, 'utf8').toString() 28 | tokenAddress = prefixedAddressParamToByteCode(tokenAddress) 29 | 30 | //add tokenaddress for param to the end of the bytecode 31 | return bin + tokenAddress 32 | } 33 | 34 | function getPostageStampBin(tokenAddress: string, adminAddress: string, minimumBucketDepth = 16) { 35 | const binPath = Path.join(__dirname, '..', 'contracts', 'PostageStamp.bytecode') 36 | const bin = FS.readFileSync(binPath, 'utf8').toString() 37 | tokenAddress = prefixedAddressParamToByteCode(tokenAddress) 38 | adminAddress = prefixedAddressParamToByteCode(adminAddress) 39 | const minimumBucketDepthParam = intToByteCode(minimumBucketDepth) 40 | 41 | //add tokenaddress for param to the end of the bytecode 42 | return bin + tokenAddress + minimumBucketDepthParam + adminAddress 43 | } 44 | 45 | function getPostagePriceOracleBin(tokenAddress: string, adminAddress: string) { 46 | const binPath = Path.join(__dirname, '..', 'contracts', 'PostagePriceOracle.bytecode') 47 | const bin = FS.readFileSync(binPath, 'utf8').toString().trim() 48 | tokenAddress = prefixedAddressParamToByteCode(tokenAddress) 49 | adminAddress = prefixedAddressParamToByteCode(adminAddress) 50 | 51 | //add tokenaddress for param to the end of the bytecode 52 | return bin + tokenAddress + adminAddress 53 | } 54 | 55 | function getSwapPriceOracleBin(price: number, chequeValueDeduction: number) { 56 | const binPath = Path.join(__dirname, '..', 'contracts', 'SwapPriceOracle.bytecode') 57 | const bin = FS.readFileSync(binPath, 'utf8').toString().trim() 58 | const priceAbi = intToByteCode(price) 59 | const chequeValueAbi = intToByteCode(chequeValueDeduction) 60 | 61 | //add tokenaddress for param to the end of the bytecode 62 | return bin + priceAbi + chequeValueAbi 63 | } 64 | 65 | function getStakeRegistryBin(tokenAddress: string, adminAddress: string) { 66 | const binPath = Path.join(__dirname, '..', 'contracts', 'StakeRegistry.bytecode') 67 | const bin = FS.readFileSync(binPath, 'utf8').toString().trim() 68 | tokenAddress = prefixedAddressParamToByteCode(tokenAddress) 69 | adminAddress = prefixedAddressParamToByteCode(adminAddress) 70 | const networkIdAbi = intToByteCode(NETWORK_ID) 71 | 72 | //add tokenaddress and encoded network ID for param to the end of the bytecode 73 | return bin + tokenAddress + networkIdAbi + adminAddress 74 | } 75 | 76 | function getRedistributionBin( 77 | stakingAddress: string, 78 | postageContractAddress: string, 79 | oracleContractAddress: string, 80 | adminAddress: string, 81 | ) { 82 | const binPath = Path.join(__dirname, '..', 'contracts', 'Redistribution.bytecode') 83 | const bin = FS.readFileSync(binPath, 'utf8').toString().trim() 84 | stakingAddress = prefixedAddressParamToByteCode(stakingAddress) 85 | postageContractAddress = prefixedAddressParamToByteCode(postageContractAddress) 86 | oracleContractAddress = prefixedAddressParamToByteCode(oracleContractAddress) 87 | adminAddress = prefixedAddressParamToByteCode(adminAddress) 88 | 89 | //add staking address, postage address and oracle contract address for param to the end of the bytecode 90 | return bin + stakingAddress + postageContractAddress + oracleContractAddress + adminAddress 91 | } 92 | 93 | function printContractAddress(contractName: string, contractAddress: string, txAddress: string) { 94 | console.log( 95 | `${contractName} contract creation was successful!\n` + 96 | `\tTransaction ID: ${txAddress}\n` + 97 | `\tContract ID: ${contractAddress}`, 98 | ) 99 | } 100 | 101 | /** Returns back contract hash */ 102 | async function createContract( 103 | contractName: string, 104 | data: string, 105 | creatorAccount: HardhatEthersSigner, 106 | ): Promise { 107 | try { 108 | const transaction = await creatorAccount.sendTransaction({ 109 | data, 110 | gasLimit: 6721975, 111 | gasPrice: ethers.parseUnits('10', 'gwei'), 112 | from: creatorAccount, 113 | }) 114 | await transaction.wait() 115 | const receipt = await creatorAccount.provider.getTransactionReceipt(transaction.hash) 116 | 117 | if (!receipt || !receipt.contractAddress) throw Error('No receipt') 118 | 119 | printContractAddress(contractName, receipt.contractAddress, receipt.hash) 120 | 121 | return receipt.contractAddress 122 | } catch (e) { 123 | console.error(`${contractName} contract creation Error`, e) 124 | throw new Error(`Error happened at creating ${contractName} contract creation`) 125 | } 126 | } 127 | 128 | /** 129 | * 130 | * @param creatorAccount 131 | * @param price current price in PLUR per accounting unit 132 | * @param chequeValueDeduction value deducted from first received cheque from a peer in PLUR 133 | */ 134 | async function createSwapPriceOracleContract( 135 | creatorAccount: HardhatEthersSigner, 136 | price = 100000, 137 | chequeValueDeduction = 100, 138 | ) { 139 | return createContract('SwapPriceOracle', getSwapPriceOracleBin(price, chequeValueDeduction), creatorAccount) 140 | } 141 | 142 | async function createPostagePriceOracleContract(creatorAccount: HardhatEthersSigner, erc20ContractAddress: string) { 143 | return createContract( 144 | 'PostagePriceOracle', 145 | getPostagePriceOracleBin(erc20ContractAddress, creatorAccount.address), 146 | creatorAccount, 147 | ) 148 | } 149 | 150 | async function createSimpleSwapFactoryContract(creatorAccount: HardhatEthersSigner, erc20ContractAddress: string) { 151 | return createContract('SimpleSwapFactory', getSimpleSwapFactoryBin(erc20ContractAddress), creatorAccount) 152 | } 153 | 154 | async function createPostageStampContract(creatorAccount: HardhatEthersSigner, erc20ContractAddress: string) { 155 | return createContract( 156 | 'PostageStamp', 157 | getPostageStampBin(erc20ContractAddress, creatorAccount.address), 158 | creatorAccount, 159 | ) 160 | } 161 | 162 | async function createStakeRegistryContract(creatorAccount: HardhatEthersSigner, erc20ContractAddress: string) { 163 | return createContract( 164 | 'StakeRegistry', 165 | getStakeRegistryBin(erc20ContractAddress, creatorAccount.address), 166 | creatorAccount, 167 | ) 168 | } 169 | 170 | async function createRedistributionContract( 171 | creatorAccount: HardhatEthersSigner, 172 | stakeRegistryAddress: string, 173 | postageStampAddress: string, 174 | postagePriceOracleAddress: string, 175 | ) { 176 | return createContract( 177 | 'Redistribution', 178 | getRedistributionBin(stakeRegistryAddress, postageStampAddress, postagePriceOracleAddress, creatorAccount.address), 179 | creatorAccount, 180 | ) 181 | } 182 | 183 | async function updateRoles( 184 | postageAddress: string, 185 | postagePriceOracleAddress: string, 186 | stakeRegistryAddress: string, 187 | redistributionAddress: string, 188 | ) { 189 | // Change roles on current stamp contract 190 | const stamp = await ethers.getContractAt(AccessControl.abi, postageAddress) 191 | const redistributorRole = keccak256(new TextEncoder().encode('REDISTRIBUTOR_ROLE')) 192 | const tx = await stamp.grantRole(redistributorRole, redistributionAddress) 193 | console.log('Changed REDISTRIBUTOR ROLE in Stamp contract to Redistribution contract', tx.hash) 194 | 195 | const oracleRole = keccak256(new TextEncoder().encode('PRICE_ORACLE_ROLE')) 196 | const tx2 = await stamp.grantRole(oracleRole, postagePriceOracleAddress) 197 | console.log('Changed ORACLE ROLE in Stamp contract to Stamp Price Oracle contract', tx2.hash) 198 | 199 | // Change roles on current oracle contract 200 | const oracle = await ethers.getContractAt(AccessControl.abi, postagePriceOracleAddress) 201 | const updaterRole = keccak256(new TextEncoder().encode('PRICE_UPDATER')) 202 | const tx3 = await oracle.grantRole(updaterRole, redistributionAddress) 203 | console.log('Changed UPDATER ROLE in Stamp Oracle contract to Redistribution contract', tx3.hash) 204 | 205 | // Change roles on current staking contract 206 | const stake = await ethers.getContractAt(AccessControl.abi, stakeRegistryAddress) 207 | const tx4 = await stake.grantRole(redistributorRole, redistributionAddress) 208 | console.log('Changed REDISTRIBUTOR ROLE in Staking contract to Redistribution contract', tx4.hash) 209 | } 210 | 211 | async function main() { 212 | const accounts = await ethers.getSigners() 213 | const creatorAccount = accounts[0] 214 | const erc20Token = await ethers.deployContract('ERC20PresetMinterPauser', ['Swarm Token', 'BZZ']) 215 | await erc20Token.waitForDeployment() 216 | const erc20Address = await erc20Token.getAddress() 217 | printContractAddress('ERC20Token', erc20Address, erc20Token.deploymentTransaction()!.hash) 218 | 219 | const swapPriceOracleAddress = await createSwapPriceOracleContract(creatorAccount) 220 | const swapFactoryAddress = await createSimpleSwapFactoryContract(creatorAccount, erc20Address) 221 | const postageAddress = await createPostageStampContract(creatorAccount, erc20Address) 222 | const postagePriceOracleAddress = await createPostagePriceOracleContract(creatorAccount, erc20Address) 223 | const stakeRegistryAddress = await createStakeRegistryContract(creatorAccount, erc20Address) 224 | const redistributionAddress = await createRedistributionContract( 225 | creatorAccount, 226 | stakeRegistryAddress, 227 | postageAddress, 228 | postagePriceOracleAddress, 229 | ) 230 | await updateRoles(postageAddress, postagePriceOracleAddress, stakeRegistryAddress, redistributionAddress) 231 | 232 | saveContractAddresses({ 233 | bzzToken: erc20Address, 234 | swapPriceOrcale: swapPriceOracleAddress, 235 | swapFactory: swapFactoryAddress, 236 | postage: postageAddress, 237 | postagePriceOracle: postagePriceOracleAddress, 238 | stakeRegistry: stakeRegistryAddress, 239 | redistribution: redistributionAddress, 240 | }) 241 | } 242 | 243 | // We recommend this pattern to be able to use async/await everywhere 244 | // and properly handle errors. 245 | main().catch(error => { 246 | console.error(error) 247 | process.exitCode = 1 248 | }) 249 | -------------------------------------------------------------------------------- /orchestrator/builder/bee.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | set -o pipefail 5 | 6 | echoerr() { if [[ $QUIET -ne 1 ]] ; then echo "$@" 1>&2; fi } 7 | 8 | usage() { 9 | cat << USAGE >&2 10 | USAGE: 11 | $ bee.sh [COMMAND] [PARAMETERS] 12 | COMMANDS: 13 | start create Bee cluster with the given parameters 14 | stop stop Bee cluster 15 | PARAMETERS: 16 | --restrict=string turns on Restricted API support with given string as password 17 | --ephemeral create ephemeral container for bee-client. Data won't be persisted. 18 | --workers=number all Bee nodes in the test environment. Default is 4. 19 | --port-maps=number map ports of the cluster nodes to the hosting machine in the following manner: 20 | 1. 1633:1634 21 | 2. 11633:11634 22 | 3. 21633:21634 (...) 23 | number represents the nodes number to map from. Default is 2. 24 | --password=string password for Bee client(s). 25 | --own-image If passed, the used Docker image names will be identical as the name of the workers. 26 | --version=x.y.z used version of Bee client. 27 | --detach It will not log the output of Queen node at the end of the process. 28 | USAGE 29 | exit 1 30 | } 31 | 32 | stop_containers() { 33 | echo "Stop Bee following containers:" 34 | docker container stop "$QUEEN_CONTAINER_NAME"; 35 | WORKER_NAMES=$(docker container ls -f name="$WORKER_CONTAINER_NAME*" --format "{{.Names}}") 36 | for WORKER_NAME in $WORKER_NAMES; do 37 | docker container stop "$WORKER_NAME" 38 | done 39 | } 40 | 41 | stop() { 42 | stop_containers 43 | trap - SIGINT 44 | exit 0; 45 | } 46 | 47 | queen_failure() { 48 | docker logs "$QUEEN_CONTAINER_NAME" 49 | stop_containers 50 | echo "Some error occured, exit from the process.." 51 | exit 1 52 | } 53 | 54 | check_queen_is_running() { 55 | QUEEN_RUNNING=$(docker container inspect -f "{{.State.Running}}" $QUEEN_CONTAINER_NAME) 56 | if [ "$QUEEN_RUNNING" == 'false' ] ; then 57 | echo "Queen container has been stopped... stop environment start process..." 58 | queen_failure 59 | fi 60 | } 61 | 62 | get_token() { 63 | echo "$(curl -X POST -s "http://$HOSTNAME:1633/auth" -u "_:$1" -d '{"role":"maintainer","expiry":400}' | python -c 'import json,sys; obj=json.load(sys.stdin); print(obj["key"]);')" 64 | } 65 | 66 | fetch_queen_underlay_addr() { 67 | set +e 68 | 69 | if [[ -n "$QUEEN_UNDERLAY_ADDRESS" ]] ; then return; fi 70 | ELAPSED_TIME=0 71 | WAITING_TIME=5 72 | # Wait 2 mins for queen start 73 | TIMEOUT=$((2*12*WAITING_TIME)) 74 | while (( TIMEOUT > ELAPSED_TIME )) ; do 75 | check_queen_is_running 76 | QUEEN_UNDERLAY_ADDRESS=$(curl -s "$HOSTNAME:1633/addresses" | python -mjson.tool | grep "/ip4/" | awk "!/127.0.0.1/" | sed 's/,$//' | xargs) 77 | if [[ -z "$QUEEN_UNDERLAY_ADDRESS" ]] ; then 78 | echo "Waiting for the Queen initialization..." 79 | ELAPSED_TIME=$((ELAPSED_TIME+WAITING_TIME)) 80 | sleep $WAITING_TIME 81 | else 82 | echo "Queen underlay address: $QUEEN_UNDERLAY_ADDRESS" 83 | break; 84 | fi 85 | done 86 | set -e 87 | 88 | if (( TIMEOUT == ELAPSED_TIME )) ; then 89 | queen_failure 90 | fi 91 | 92 | } 93 | 94 | log_queen() { 95 | trap stop SIGINT 96 | docker logs --tail 25 -f "$QUEEN_CONTAINER_NAME" 97 | } 98 | 99 | count_connected_peers() { 100 | COUNT=$( (curl -s "http://$HOSTNAME:1633/peers" -H "Authorization: Bearer $1" | python -c 'import json,sys; obj=json.load(sys.stdin); print (len(obj["peers"]));') || echo 0 ) 101 | 102 | echo "$COUNT" 103 | } 104 | 105 | MY_PATH=$(dirname "$0") # relative 106 | MY_PATH=$( cd "$MY_PATH" && pwd ) # absolutized and normalized 107 | # Check used system variable set 108 | BEE_VERSION=$("$MY_PATH/utils/build-image-tag.sh" get) 109 | BEE_ENV_PREFIX=$("$MY_PATH/utils/env-variable-value.sh" BEE_ENV_PREFIX) 110 | 111 | # Init variables 112 | EPHEMERAL=false 113 | WORKERS=4 114 | LOG=true 115 | RESTRICTED=false 116 | RESTRICTED_PASSWORD="" 117 | QUEEN_CONTAINER_NAME="$BEE_ENV_PREFIX-queen" 118 | WORKER_CONTAINER_NAME="$BEE_ENV_PREFIX-worker" 119 | SWARM_BLOCKCHAIN_NAME="$BEE_ENV_PREFIX-blockchain" 120 | NETWORK="$BEE_ENV_PREFIX-network" 121 | QUEEN_CONTAINER_IN_DOCKER=$(docker container ls -qaf name="$QUEEN_CONTAINER_NAME") 122 | BEE_BASE_IMAGE="ethersphere/bee" 123 | BEE_PASSWORD="password" 124 | QUEEN_BOOTNODE="" 125 | PORT_MAPS=2 126 | SWAP=true 127 | INIT_ROOT_DATA_DIR="$MY_PATH/bee-data-dirs" 128 | HOSTNAME="127.0.0.1" 129 | # TODO: take these from contract-addresses.json 130 | SWAP_FACTORY_ADDRESS="0xCfEB869F69431e42cdB54A4F4f105C19C080A601" 131 | POSTAGE_STAMP_ADDRESS="0x254dffcd3277C0b1660F6d42EFbB754edaBAbC2B" 132 | SWAP_PRICE_ORACLE_ADDRESS="0x5b1869D9A4C187F2EAa108f3062412ecf0526b24" 133 | REDISTRIBUTION_ADDRESS="0x9561C133DD8580860B6b7E504bC5Aa500f0f06a7" 134 | STAKING_ADDRESS="0xD833215cBcc3f914bD1C9ece3EE7BF8B14f841bb" 135 | 136 | # Decide script action 137 | case "$1" in 138 | start) 139 | shift 1 140 | ;; 141 | stop) 142 | stop 143 | ;; 144 | *) 145 | echoerr "Unknown command: $1" 146 | usage 147 | ;; 148 | esac 149 | 150 | # Alter variables from flags 151 | while [ $# -gt 0 ] 152 | do 153 | case "$1" in 154 | --ephemeral) 155 | EPHEMERAL=true 156 | shift 1 157 | ;; 158 | --workers=*) 159 | WORKERS=${1#*=} 160 | shift 1 161 | ;; 162 | --password=*) 163 | BEE_PASSWORD="${1#*=}" 164 | shift 1 165 | ;; 166 | --version=*) 167 | BEE_VERSION="${1#*=}" 168 | shift 1 169 | ;; 170 | --hostname=*) 171 | HOSTNAME="${1#*=}" 172 | shift 1 173 | ;; 174 | --port-maps=*) 175 | PORT_MAPS="${1#*=}" 176 | shift 1 177 | ;; 178 | --detach) 179 | LOG=false 180 | shift 1 181 | ;; 182 | --help) 183 | usage 184 | ;; 185 | *) 186 | echoerr "Unknown argument: $1" 187 | usage 188 | ;; 189 | esac 190 | done 191 | 192 | BEE_IMAGE="$BEE_BASE_IMAGE:$BEE_VERSION" 193 | 194 | if $EPHEMERAL ; then 195 | EXTRA_DOCKER_PARAMS="--rm" 196 | fi 197 | 198 | # Start Bee Queen 199 | if [ -z "$QUEEN_CONTAINER_IN_DOCKER" ] || $EPHEMERAL ; then 200 | DOCKER_IMAGE="$BEE_IMAGE" 201 | EXTRA_QUEEN_PARAMS="-v $INIT_ROOT_DATA_DIR/$QUEEN_CONTAINER_NAME:/home/bee/.bee" 202 | if [ "$PORT_MAPS" -ge 1 ] ; then 203 | EXTRA_QUEEN_PARAMS="$EXTRA_QUEEN_PARAMS -p 1633-1634:1633-1634" 204 | fi 205 | 206 | echo "start Bee Queen process" 207 | if [ $RESTRICTED == "true" ]; then 208 | echo "Enabled Restricted API with password: $RESTRICTED_PASSWORD" 209 | fi 210 | docker run \ 211 | -d \ 212 | --network="$NETWORK" \ 213 | --name="$QUEEN_CONTAINER_NAME" \ 214 | $EXTRA_DOCKER_PARAMS \ 215 | $EXTRA_QUEEN_PARAMS \ 216 | $DOCKER_IMAGE \ 217 | start \ 218 | --warmup-time=10s \ 219 | --password "$BEE_PASSWORD" \ 220 | --bootnode="$QUEEN_BOOTNODE" \ 221 | --bootnode-mode=false \ 222 | --allow-private-cidrs=true \ 223 | --verbosity=4 \ 224 | --mainnet=false \ 225 | --block-time=5 \ 226 | --api-addr=0.0.0.0:1633 \ 227 | --swap-enable=$SWAP \ 228 | --blockchain-rpc-endpoint="http://$SWARM_BLOCKCHAIN_NAME:9545" \ 229 | --swap-factory-address=$SWAP_FACTORY_ADDRESS \ 230 | --postage-stamp-address=$POSTAGE_STAMP_ADDRESS \ 231 | --price-oracle-address=$SWAP_PRICE_ORACLE_ADDRESS \ 232 | --staking-address=$STAKING_ADDRESS \ 233 | --redistribution-address=$REDISTRIBUTION_ADDRESS \ 234 | --network-id 4020 \ 235 | --full-node=true \ 236 | --welcome-message="You have found the queen of the beehive..." \ 237 | --cors-allowed-origins="*" \ 238 | --postage-stamp-start-block=1 239 | else 240 | docker start "$QUEEN_CONTAINER_IN_DOCKER" 241 | fi 242 | 243 | # Start Bee workers 244 | for i in $(seq 1 1 "$WORKERS"); do 245 | WORKER_NAME="$WORKER_CONTAINER_NAME-$i" 246 | WORKER_CONTAINER_IN_DOCKER=$(docker container ls -qaf name="$WORKER_NAME") 247 | if [ -z "$WORKER_CONTAINER_IN_DOCKER" ] || $EPHEMERAL ; then 248 | # fetch queen underlay address 249 | fetch_queen_underlay_addr 250 | 251 | # construct additional params 252 | EXTRA_WORKER_PARAMS="" 253 | DOCKER_IMAGE="$BEE_IMAGE" 254 | EXTRA_WORKER_PARAMS="$EXTRA_WORKER_PARAMS -v $INIT_ROOT_DATA_DIR/$WORKER_NAME:/home/bee/.bee" 255 | if [ $PORT_MAPS -gt $i ] ; then 256 | PORT_START=$((1633+(10000*i))) 257 | PORT_END=$((PORT_START + 1)) 258 | EXTRA_WORKER_PARAMS="$EXTRA_WORKER_PARAMS -p $PORT_START-$PORT_END:1633-1634" 259 | fi 260 | 261 | # run docker container 262 | echo "start Bee worker $i process" 263 | docker run \ 264 | -d \ 265 | --network="$NETWORK" \ 266 | --name="$WORKER_NAME" \ 267 | $EXTRA_DOCKER_PARAMS \ 268 | $EXTRA_WORKER_PARAMS \ 269 | $DOCKER_IMAGE \ 270 | start \ 271 | --warmup-time=10s \ 272 | --password "$BEE_PASSWORD" \ 273 | --bootnode="$QUEEN_UNDERLAY_ADDRESS" \ 274 | --allow-private-cidrs=true \ 275 | --verbosity=4 \ 276 | --mainnet=false \ 277 | --block-time=5 \ 278 | --api-addr=0.0.0.0:1633 \ 279 | --swap-enable=$SWAP \ 280 | --blockchain-rpc-endpoint="http://$SWARM_BLOCKCHAIN_NAME:9545" \ 281 | --swap-factory-address=$SWAP_FACTORY_ADDRESS \ 282 | --postage-stamp-address=$POSTAGE_STAMP_ADDRESS \ 283 | --price-oracle-address=$SWAP_PRICE_ORACLE_ADDRESS \ 284 | --staking-address=$STAKING_ADDRESS \ 285 | --redistribution-address=$REDISTRIBUTION_ADDRESS \ 286 | --network-id 4020 \ 287 | --full-node=true \ 288 | --welcome-message="I'm just Bee worker ${i} in the beehive." \ 289 | --cors-allowed-origins="*" \ 290 | --postage-stamp-start-block=1 291 | else 292 | docker start "$WORKER_CONTAINER_IN_DOCKER" 293 | fi 294 | done 295 | 296 | echo "Check whether the queen node has been connected to every worker..." 297 | ELAPSED_TIME=0 298 | WAITING_TIME=2 299 | TIMEOUT=$((6*30*WAITING_TIME)) 300 | RESTRICTED_TOKEN="" 301 | while (( TIMEOUT > ELAPSED_TIME )) ; do 302 | check_queen_is_running 303 | if [ $RESTRICTED == "true" ] && [ -z "$RESTRICTED_TOKEN" ]; then 304 | RESTRICTED_TOKEN=$(get_token "$RESTRICTED_PASSWORD") 305 | echo "Fetched Bearer token: $RESTRICTED_TOKEN" 306 | fi; 307 | 308 | COUNT=$(count_connected_peers "$RESTRICTED_TOKEN") 309 | [[ $COUNT -lt $WORKERS ]] || break 310 | echo "Only $COUNT peers have been connected to the Queen Bee node yet. Waiting until $WORKERS" 311 | ELAPSED_TIME=$((ELAPSED_TIME+WAITING_TIME)) 312 | sleep $WAITING_TIME 313 | done 314 | if (( ELAPSED_TIME >= TIMEOUT )) ; then 315 | queen_failure 316 | fi 317 | 318 | # log Bee Queen 319 | if $LOG ; then 320 | log_queen 321 | fi 322 | -------------------------------------------------------------------------------- /orchestrator/contracts/PostagePriceOracle.bytecode: -------------------------------------------------------------------------------- 1 | 0x600180546201000160a21b64ffffffffff60a01b1990911617905560028054600160401b600160c01b0319811663ffffffff68010000000000000000600160401b600160801b03199093166e1000000177000000000000000000009081179390930416600160801b02171790556101c06040526210034960a09081526210027660c052621001a460e052621000d2610100526210000061012052620fff2e61014052620ffe5c61016052620ffd8a61018052620ffcb86101a052620000c990600390600962000291565b50348015620000d757600080fd5b506040516200181938038062001819833981016040819052620000fa916200034a565b62000107600033620001cd565b600180546001600160a01b0319166001600160a01b0383161790556200012c620001dd565b600280546001600160401b0319166001600160401b03929092169190911790557fd96ba01d6560c2ab35f2940dd8d70c5f5fe06236c72674237120515918198fb06080527fae46785019700e30375a5d7b4f91e32f8060ef085111f896ebf889450aa2ab5a620001ae600254600160801b9004600a1c663fffffffffffff1690565b60405163ffffffff909116815260200160405180910390a1506200039f565b620001d98282620001f1565b5050565b6000620001ec6098436200037c565b905090565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16620001d9576000828152602081815260408083206001600160a01b03851684529091529020805460ff191660011790556200024d3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b600283019183908215620003215791602002820160005b83821115620002ed57835183826101000a81548163ffffffff021916908362ffffff1602179055509260200192600401602081600301049283019260010302620002a8565b80156200031f5782816101000a81549063ffffffff0219169055600401602081600301049283019260010302620002ed565b505b506200032f92915062000333565b5090565b5b808211156200032f576000815560010162000334565b6000602082840312156200035d57600080fd5b81516001600160a01b03811681146200037557600080fd5b9392505050565b6000826200039a57634e487b7160e01b600052601260045260246000fd5b500490565b608051611457620003c2600039600081816103ac01526108bf01526114576000f3fe608060405234801561001057600080fd5b50600436106101825760003560e01c806391d14854116100d8578063daafe0a51161008c578063f7b188a511610066578063f7b188a51461039f578063fb8d8101146103a7578063ffb98fe3146103ce57600080fd5b8063daafe0a514610346578063dcd8ffb814610359578063e0632c641461037457600080fd5b8063a217fddf116100bd578063a217fddf14610317578063b187bd261461031f578063d547741f1461033357600080fd5b806391d14854146102c55780639d1b464a146102fc57600080fd5b80637310561b1161013a5780637f386b6c116101145780637f386b6c146102995780638456cb59146102b55780638a19c8bc146102bd57600080fd5b80637310561b1461023557806374e7493b1461026a5780637e8effdd1461027d57600080fd5b80632f2ff15d1161016b5780632f2ff15d146101e057806336568abe146101f557806355bdcd231461020857600080fd5b806301ffc9a714610187578063248a9ca3146101af575b600080fd5b61019a6101953660046110e2565b6103e1565b60405190151581526020015b60405180910390f35b6101d26101bd366004611124565b60009081526020819052604090206001015490565b6040519081526020016101a6565b6101f36101ee36600461113d565b61047a565b005b6101f361020336600461113d565b6104a4565b60025461021c9067ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016101a6565b600254610255906c01000000000000000000000000900463ffffffff1681565b60405163ffffffff90911681526020016101a6565b610255610278366004611124565b610535565b6002546102559068010000000000000000900463ffffffff1681565b600254680100000000000000009004600a1c623fffff16610255565b6101f3610565565b61021c6105c9565b61019a6102d336600461113d565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b600254600160801b9004600a1c663fffffffffffff16610255565b6101d2600081565b60015461019a90600160c01b900460ff1681565b6101f361034136600461113d565b6105db565b61019a610354366004611179565b610600565b60025461021c90600160801b900467ffffffffffffffff1681565b600154610387906001600160a01b031681565b6040516001600160a01b0390911681526020016101a6565b6101f3610847565b6101d27f000000000000000000000000000000000000000000000000000000000000000081565b61019a6103dc36600461119f565b6108a5565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b00000000000000000000000000000000000000000000000000000000148061047457507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60008281526020819052604090206001015461049581610d4b565b61049f8383610d58565b505050565b6001600160a01b03811633146105275760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084015b60405180910390fd5b6105318282610df6565b5050565b6003816009811061054557600080fd5b60089182820401919006600402915054906101000a900463ffffffff1681565b3360009081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604090205460ff166105b45760405163036c8cf960e11b815260040160405180910390fd5b6001805460ff60c01b1916600160c01b179055565b60006105d66098436111ef565b905090565b6000828152602081905260409020600101546105f681610d4b565b61049f8383610df6565b3360009081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604081205460ff1661064f5760405163036c8cf960e11b815260040160405180910390fd5b60025463fffffc00600a84901b169068010000000000000000900463ffffffff168082101561067c578091505b6002805467ffffffffffffffff8416600160801b027fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff9091161790556001546000906001600160a01b03166106e4600254663fffffffffffff600160801b909104600a1c1690565b63ffffffff166040516024016106fc91815260200190565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166391b7f5ed60e01b179052516107469190611227565b6000604051808303816000865af19150503d8060008114610783576040519150601f19603f3d011682016040523d82523d6000602084013e610788565b606091505b50509050806107ec576002547fd2821f4adadf1d64076091cc92984c0f001a300541dd58cff8e8e29d6290c33d90600160801b9004600a1c663fffffffffffff1660405163ffffffff909116815260200160405180910390a1506000949350505050565b6002547fae46785019700e30375a5d7b4f91e32f8060ef085111f896ebf889450aa2ab5a90600160801b9004600a1c663fffffffffffff1660405163ffffffff909116815260200160405180910390a1506001949350505050565b3360009081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604090205460ff166108965760405163036c8cf960e11b815260040160405180910390fd5b6001805460ff60c01b19169055565b600154600090600160c01b900460ff1615158103610d43577f000000000000000000000000000000000000000000000000000000000000000060009081526020818152604080832033845290915290205460ff1661092f576040517f262791ec00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600061093a6105c9565b60025490915067ffffffffffffffff90811690821611610986576040517f08a5cb4c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8361ffff166000036109c4576040517fb5ca2d7100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600154600090610a0c9061ffff760100000000000000000000000000000000000000000000820481169174010000000000000000000000000000000000000000900416611243565b90508061ffff168561ffff161115610a22578092505b60025467ffffffffffffffff600160801b820481169163ffffffff6801000000000000000082048116926c01000000000000000000000000830490911691600091600191610a71911688611265565b610a7b9190611265565b9050600060038861ffff1660098110610a9657610a96611286565b60088104919091015460079091166004026101000a900463ffffffff90811691508316610ac3868361129c565b610acd91906112c8565b945067ffffffffffffffff821615610b41575060035463ffffffff1660005b8267ffffffffffffffff168167ffffffffffffffff161015610b3f578363ffffffff16868363ffffffff16610b21919061129c565b610b2b91906112c8565b955080610b37816112ef565b915050610aec565b505b8367ffffffffffffffff168567ffffffffffffffff161015610b61578394505b600280547fffffffffffffffff0000000000000000ffffffffffffffff000000000000000016600160801b67ffffffffffffffff888116820267ffffffffffffffff191692909217918a1691909117918290556001546000926001600160a01b03909116919004600a1c663fffffffffffff1663ffffffff16604051602401610bec91815260200190565b60408051601f198184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff166391b7f5ed60e01b17905251610c369190611227565b6000604051808303816000865af19150503d8060008114610c73576040519150601f19603f3d011682016040523d82523d6000602084013e610c78565b606091505b5050905080610ce2576002547fd2821f4adadf1d64076091cc92984c0f001a300541dd58cff8e8e29d6290c33d90600160801b9004600a1c663fffffffffffff1660405163ffffffff909116815260200160405180910390a15060009a9950505050505050505050565b6002547fae46785019700e30375a5d7b4f91e32f8060ef085111f896ebf889450aa2ab5a90600160801b9004600a1c663fffffffffffff1660405163ffffffff909116815260200160405180910390a15060019a9950505050505050505050565b506000919050565b610d558133610e75565b50565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16610531576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055610db23390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1615610531576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1661053157610ea681610ee8565b610eb1836020610efa565b604051602001610ec2929190611316565b60408051601f198184030181529082905262461bcd60e51b825261051e91600401611397565b60606104746001600160a01b03831660145b60606000610f098360026113ca565b610f149060026113e1565b67ffffffffffffffff811115610f2c57610f2c6113f4565b6040519080825280601f01601f191660200182016040528015610f56576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110610f8d57610f8d611286565b60200101906001600160f81b031916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110610fd857610fd8611286565b60200101906001600160f81b031916908160001a9053506000610ffc8460026113ca565b6110079060016113e1565b90505b600181111561108c577f303132333435363738396162636465660000000000000000000000000000000085600f166010811061104857611048611286565b1a60f81b82828151811061105e5761105e611286565b60200101906001600160f81b031916908160001a90535060049490941c936110858161140a565b905061100a565b5083156110db5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161051e565b9392505050565b6000602082840312156110f457600080fd5b81357fffffffff00000000000000000000000000000000000000000000000000000000811681146110db57600080fd5b60006020828403121561113657600080fd5b5035919050565b6000806040838503121561115057600080fd5b8235915060208301356001600160a01b038116811461116e57600080fd5b809150509250929050565b60006020828403121561118b57600080fd5b813563ffffffff811681146110db57600080fd5b6000602082840312156111b157600080fd5b813561ffff811681146110db57600080fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000826111fe576111fe6111c3565b500490565b60005b8381101561121e578181015183820152602001611206565b50506000910152565b60008251611239818460208701611203565b9190910192915050565b61ffff81811683821601908082111561125e5761125e6111d9565b5092915050565b67ffffffffffffffff82811682821603908082111561125e5761125e6111d9565b634e487b7160e01b600052603260045260246000fd5b67ffffffffffffffff8181168382160280821691908281146112c0576112c06111d9565b505092915050565b600067ffffffffffffffff808416806112e3576112e36111c3565b92169190910492915050565b600067ffffffffffffffff80831681810361130c5761130c6111d9565b6001019392505050565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161134e816017850160208801611203565b7f206973206d697373696e6720726f6c6520000000000000000000000000000000601791840191820152835161138b816028840160208801611203565b01602801949350505050565b60208152600082518060208401526113b6816040850160208701611203565b601f01601f19169190910160400192915050565b8082028115828204841417610474576104746111d9565b80820180821115610474576104746111d9565b634e487b7160e01b600052604160045260246000fd5b600081611419576114196111d9565b50600019019056fea264697066735822122084a8a5aa1fb7ce825bff5a4d1ef108ea49cb6d790170c054455e8550f0e8314564736f6c63430008130033 -------------------------------------------------------------------------------- /orchestrator/contracts/StakeRegistry.bytecode: -------------------------------------------------------------------------------- 1 | 0x60a06040523480156200001157600080fd5b5060405162001d0a38038062001d0a83398101604081905262000034916200015a565b6001805460ff19169055600380546001600160a01b03808616608052831668010000000000000000026001600160e01b03199091166001600160401b03851617179055620000846000336200008d565b505050620001b2565b6200009982826200009d565b5050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1662000099576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055620000f93390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b80516001600160a01b03811681146200015557600080fd5b919050565b6000806000606084860312156200017057600080fd5b6200017b846200013d565b60208501519093506001600160401b03811681146200019957600080fd5b9150620001a9604085016200013d565b90509250925092565b608051611b27620001e3600039600081816102ea0152818161057c015281816106cb0152610bce0152611b276000f3fe608060405234801561001057600080fd5b50600436106101a35760003560e01c80636f1c5de7116100ee578063a6471a1d11610097578063d48bfaeb11610071578063d48bfaeb14610452578063d547741f14610465578063e8e8d87e14610478578063f7b188a51461048b57600080fd5b8063a6471a1d146103ec578063d009b2d614610413578063d1052d1f1461042657600080fd5b806391d14854116100c857806391d148541461038457806397adedb5146103bb578063a217fddf146103e457600080fd5b80636f1c5de714610361578063830a053e146103695780638456cb591461037c57600080fd5b80632f2ff15d116101505780635c975abb1161012a5780635c975abb14610324578063663e54f91461032f57806369da91141461034257600080fd5b80632f2ff15d146102bf57806336568abe146102d2578063420fc4db146102e557600080fd5b806316934fc41161018157806316934fc4146101e25780631d5480531461024d578063248a9ca31461028e57600080fd5b806301ffc9a7146101a85780630aed7b0d146101d057806312236a77146101da575b600080fd5b6101bb6101b63660046116d5565b610493565b60405190151581526020015b60405180910390f35b6101d861052c565b005b6101d8610645565b6102226101f0366004611733565b600260208190526000918252604090912080546001820154928201546003830154600490930154919392909160ff1685565b60408051958652602086019490945292840191909152606083015260ff16608082015260a0016101c7565b61027c61025b366004611733565b6001600160a01b031660009081526002602052604090206004015460ff1690565b60405160ff90911681526020016101c7565b6102b161029c36600461174e565b60009081526020819052604090206001015490565b6040519081526020016101c7565b6101d86102cd366004611767565b61079c565b6101d86102e0366004611767565b6107c6565b61030c7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101c7565b60015460ff166101bb565b6101d861033d366004611793565b610853565b60035461030c906801000000000000000090046001600160a01b031681565b6102b1610cff565b6102b1610377366004611733565b610d3f565b6101d8610da0565b6101bb610392366004611767565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b6102b16103c9366004611733565b6001600160a01b031660009081526002602052604090205490565b6102b1600081565b6102b17f3e35b14a9f4fef84b59f9bdcd3044fc28783144b7e42bfb2cd075e6a02cb082881565b6101d86104213660046117d2565b610df7565b6102b1610434366004611733565b6001600160a01b031660009081526002602052604090206003015490565b6101d86104603660046117fc565b610ed3565b6101d8610473366004611767565b610f5f565b6101d86104863660046117d2565b610f84565b6101d86110ff565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b00000000000000000000000000000000000000000000000000000000148061052657507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b610534611156565b336000908152600260205260409020600301541561064357336000818152600260208190526040918290200154905163a9059cbb60e01b8152600481019290925260248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a9059cbb906044016020604051808303816000875af11580156105cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f19190611826565b61060e576040516312171d8360e31b815260040160405180910390fd5b3360009081526002602081905260408220828155600181018390559081018290556003810191909155600401805460ff191690555b565b336000908152600260208190526040822090810154600182015460049092015490929161067691849060ff166111a8565b610680908361185e565b905080156107985733600090815260026020819052604082200180548392906106aa90849061185e565b909155505060405163a9059cbb60e01b8152336004820152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a9059cbb906044016020604051808303816000875af115801561071c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107409190611826565b61075d576040516312171d8360e31b815260040160405180910390fd5b60408051338152602081018390527f8108595eb6bad3acefa9da467d90cc2217686d5c5ac85460f8b7849c840645fc91015b60405180910390a15b5050565b6000828152602081905260409020600101546107b781611265565b6107c18383611272565b505050565b6001600160a01b03811633146108495760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084015b60405180910390fd5b6107988282611310565b61085b61138f565b336000818152600260209081526040822080546003918201549154909491939265ff000000ff00600883811b91821664ff000000ff9490911c93841617601090811b67ff000000ff0000009290921666ff000000ff0000949094169390931790921c9190911780821b6bffffffffffffffff0000000016911c63ffffffff161760405160609290921b6bffffffffffffffffffffffff1916602083015260c01b7fffffffffffffffff000000000000000000000000000000000000000000000000166034820152603c8101879052605c016040516020818303038152906040528051906020012090508360026109519190611955565b6109639067016345785d8a0000611964565b8510801561096f575081155b156109a6576040517f8ecf3d0300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81158015906109c75750336000908152600260205260409020600301544311155b156109fe576040517fa8cab3d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33600090815260026020819052604090912090810154600190910154808715610b1f573360009081526002602081905260409091200154610a4090899061197b565b92506000610a4f886002611955565b600360089054906101000a90046001600160a01b03166001600160a01b0316639d1b464a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610aa2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ac6919061198e565b63ffffffff16610ad69190611964565b610ae090856119b4565b905081811015610b1c576040517f6b8eb4c800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b91505b6040805160a0810182528581526020808201858152828401878152436060850190815260ff8d8116608087019081523360009081526002968790529790972095518655925160018601559051928401929092559051600383015591516004909101805460ff1916919092161790558715610cb3576040517f23b872dd000000000000000000000000000000000000000000000000000000008152336004820152306024820152604481018990527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906323b872dd906064016020604051808303816000875af1158015610c1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c439190611826565b610c60576040516312171d8360e31b815260040160405180910390fd5b604080518381526020810185905290810185905243606082015260ff8816608082015233907f8fb3da6e133de1007392b10d429548e9c2565c791c52e1498b01b65d8797e74c9060a00160405180910390a25b838614610cf45760408051338152602081018690527fe188baf4bcb1c4413d19ba0eaef908614d30e8df9570d6fc4d584b75f4a37a3a910160405180910390a15b505050505050505050565b33600090815260026020819052604082209081015460018201546004909201549091610d2f91839060ff166111a8565b610d39908261185e565b91505090565b6001600160a01b0381166000908152600260205260408120600301544311610d68576000610526565b6001600160a01b038216600090815260026020819052604090912060018101549181015460049091015461052692919060ff166111a8565b3360009081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604090205460ff16610def57604051631d77d47760e21b815260040160405180910390fd5b6106436113e2565b3360009081527f10fa80d2e578284bf61a483a3da84fbdd683a7e7256a9895dc4461ac543f4a16602052604090205460ff16610e4657604051630490edbf60e31b815260040160405180910390fd5b6001600160a01b0382166000908152600260205260409020600301541561079857610e71814361197b565b6001600160a01b038316600081815260026020908152604091829020600381019490945592548151928352928201929092529081018290527fd7863535575e7de6678ea9d340a4bfe0b84d77323bfa2ef602c3c1df82aea7809060600161078f565b3360009081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604090205460ff16610f3b576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003805467ffffffffffffffff191667ffffffffffffffff92909216919091179055565b600082815260208190526040902060010154610f7a81611265565b6107c18383611310565b3360009081527f10fa80d2e578284bf61a483a3da84fbdd683a7e7256a9895dc4461ac543f4a16602052604090205460ff16610fd357604051630490edbf60e31b815260040160405180910390fd5b6001600160a01b038216600090815260026020526040902060030154156110aa576001600160a01b0382166000908152600260208190526040909120015481101561106c576001600160a01b0382166000908152600260208190526040822001805483929061104390849061185e565b90915550506001600160a01b0382166000908152600260205260409020436003909101556110aa565b6001600160a01b03821660009081526002602081905260408220828155600181018390559081018290556003810191909155600401805460ff191690555b6001600160a01b0382166000818152600260209081526040918290205482519384529083015281018290527f5f52ce031e6eba64e8a81288196b93a3e422096e80aea812f1f5a77866359b3a9060600161078f565b3360009081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604090205460ff1661114e57604051631d77d47760e21b815260040160405180910390fd5b610643611436565b60015460ff166106435760405162461bcd60e51b815260206004820152601460248201527f5061757361626c653a206e6f74207061757365640000000000000000000000006044820152606401610840565b600080600360089054906101000a90046001600160a01b03166001600160a01b0316639d1b464a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111fe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611222919061198e565b63ffffffff1685611234856002611955565b61123e9190611964565b6112489190611964565b90508381101561125957905061125e565b839150505b9392505050565b61126f813361146f565b50565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16610798576000828152602081815260408083206001600160a01b03851684529091529020805460ff191660011790556112cc3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1615610798576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60015460ff16156106435760405162461bcd60e51b815260206004820152601060248201527f5061757361626c653a20706175736564000000000000000000000000000000006044820152606401610840565b6113ea61138f565b6001805460ff1916811790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258335b6040516001600160a01b03909116815260200160405180910390a1565b61143e611156565b6001805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33611419565b6000828152602081815260408083206001600160a01b038516845290915290205460ff16610798576114a0816114e2565b6114ab8360206114f4565b6040516020016114bc9291906119fa565b60408051601f198184030181529082905262461bcd60e51b825261084091600401611a7b565b60606105266001600160a01b03831660145b60606000611503836002611964565b61150e90600261197b565b67ffffffffffffffff81111561152657611526611aae565b6040519080825280601f01601f191660200182016040528015611550576020820181803683370190505b5090507f30000000000000000000000000000000000000000000000000000000000000008160008151811061158757611587611ac4565b60200101906001600160f81b031916908160001a9053507f7800000000000000000000000000000000000000000000000000000000000000816001815181106115d2576115d2611ac4565b60200101906001600160f81b031916908160001a90535060006115f6846002611964565b61160190600161197b565b90505b6001811115611686577f303132333435363738396162636465660000000000000000000000000000000085600f166010811061164257611642611ac4565b1a60f81b82828151811061165857611658611ac4565b60200101906001600160f81b031916908160001a90535060049490941c9361167f81611ada565b9050611604565b50831561125e5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610840565b6000602082840312156116e757600080fd5b81357fffffffff000000000000000000000000000000000000000000000000000000008116811461125e57600080fd5b80356001600160a01b038116811461172e57600080fd5b919050565b60006020828403121561174557600080fd5b61125e82611717565b60006020828403121561176057600080fd5b5035919050565b6000806040838503121561177a57600080fd5b8235915061178a60208401611717565b90509250929050565b6000806000606084860312156117a857600080fd5b8335925060208401359150604084013560ff811681146117c757600080fd5b809150509250925092565b600080604083850312156117e557600080fd5b6117ee83611717565b946020939093013593505050565b60006020828403121561180e57600080fd5b813567ffffffffffffffff8116811461125e57600080fd5b60006020828403121561183857600080fd5b8151801515811461125e57600080fd5b634e487b7160e01b600052601160045260246000fd5b8181038181111561052657610526611848565b600181815b808511156118ac57816000190482111561189257611892611848565b8085161561189f57918102915b93841c9390800290611876565b509250929050565b6000826118c357506001610526565b816118d057506000610526565b81600181146118e657600281146118f05761190c565b6001915050610526565b60ff84111561190157611901611848565b50506001821b610526565b5060208310610133831016604e8410600b841016171561192f575081810a610526565b6119398383611871565b806000190482111561194d5761194d611848565b029392505050565b600061125e60ff8416836118b4565b808202811582820484141761052657610526611848565b8082018082111561052657610526611848565b6000602082840312156119a057600080fd5b815163ffffffff8116811461125e57600080fd5b6000826119d157634e487b7160e01b600052601260045260246000fd5b500490565b60005b838110156119f15781810151838201526020016119d9565b50506000910152565b7f416363657373436f6e74726f6c3a206163636f756e7420000000000000000000815260008351611a328160178501602088016119d6565b7f206973206d697373696e6720726f6c65200000000000000000000000000000006017918401918201528351611a6f8160288401602088016119d6565b01602801949350505050565b6020815260008251806020840152611a9a8160408501602087016119d6565b601f01601f19169190910160400192915050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b600081611ae957611ae9611848565b50600019019056fea2646970667358221220ac4805cb5e81ca8b79f40e50f835d06d65837da4c48bc2588e90ef9276d566f764736f6c63430008130033 -------------------------------------------------------------------------------- /orchestrator/contracts/SimpleSwapFactory.bytecode: -------------------------------------------------------------------------------- 1 | 0x608060405234801561001057600080fd5b50604051611df2380380611df283398101604081905261002f91610112565b600180546001600160a01b0319166001600160a01b03831617905560405160009061005990610105565b604051809103906000f080158015610075573d6000803e3d6000fd5b506040516343431f6360e11b81529091506001600160a01b038216906386863ec6906100ab906001906000908190600401610140565b600060405180830381600087803b1580156100c557600080fd5b505af11580156100d9573d6000803e3d6000fd5b5050600280546001600160a01b0319166001600160a01b03949094169390931790925550610164915050565b6118e58061050d83390190565b600060208284031215610123578081fd5b81516001600160a01b0381168114610139578182fd5b9392505050565b6001600160a01b039384168152919092166020820152604081019190915260600190565b61039a806101736000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806315efd8a714610051578063a6021ace1461007a578063c70242ad14610082578063ee97f7f3146100a2575b600080fd5b61006461005f3660046102d6565b6100aa565b6040516100719190610308565b60405180910390f35b6100646101bb565b6100956100903660046102b5565b6101ca565b6040516100719190610359565b6100646101df565b60025460405160009182916100ee916001600160a01b0316906100d3903390879060200161031c565b604051602081830303815290604052805190602001206101ee565b6001546040516343431f6360e11b81529192506001600160a01b03808416926386863ec692610126928a929116908990600401610335565b600060405180830381600087803b15801561014057600080fd5b505af1158015610154573d6000803e3d6000fd5b5050506001600160a01b03821660009081526020819052604090819020805460ff19166001179055517fc0ffc525a1c7689549d7f79b49eca900e61ac49b43d977f680bcc3b36224c00491506101ab908390610308565b60405180910390a1949350505050565b6001546001600160a01b031681565b60006020819052908152604090205460ff1681565b6002546001600160a01b031681565b6000604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b81528360601b60148201526e5af43d82803e903d91602b57fd5bf360881b6028820152826037826000f59150506001600160a01b038116610293576040805162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c6564000000000000000000604482015290519081900360640190fd5b92915050565b80356001600160a01b03811681146102b057600080fd5b919050565b6000602082840312156102c6578081fd5b6102cf82610299565b9392505050565b6000806000606084860312156102ea578182fd5b6102f384610299565b95602085013595506040909401359392505050565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b90151581526020019056fea2646970667358221220186c321a2442413d58858aac3960b8440cb5e558ae8361aefefac6b31923710564736f6c63430007060033608060405234801561001057600080fd5b506118c5806100206000396000f3fe608060405234801561001057600080fd5b506004361061014d5760003560e01c8063946f46a2116100c3578063b7ec1a331161007c578063b7ec1a331461027f578063c49f91d314610287578063c76a4d311461028f578063d4c9a8e8146102a2578063e0bcf13a146102b5578063fc0c546a146102bd5761014d565b8063946f46a214610211578063b6343b0d14610224578063b648b41714610247578063b69ef8a81461025c578063b777035014610264578063b7998907146102775761014d565b80631d143848116101155780631d143848146101a85780632e1a7d4d146101bd578063338f3fed146101d0578063488b017c146101e357806381f03fcb146101eb57806386863ec6146101fe5761014d565b80630d5f26591461015257806312101021146101675780631357e1dc1461018557806315c3343f1461018d5780631633fb1d14610195575b600080fd5b61016561016036600461146b565b6102c5565b005b61016f6102d8565b60405161017c9190611563565b60405180910390f35b61016f6102de565b61016f6102e4565b6101656101a33660046113ae565b610308565b6101b061036c565b60405161017c919061152b565b6101656101cb3660046114e0565b61037b565b6101656101de366004611442565b610473565b61016f610556565b61016f6101f9366004611359565b61057a565b61016561020c366004611373565b61058c565b61016561021f366004611359565b610610565b610237610232366004611359565b6106d0565b60405161017c94939291906115c7565b61024f6106f7565b60405161017c9190611558565b61016f610707565b610165610272366004611442565b61078d565b61016f610867565b61016f61088b565b61016f6108a1565b61016f61029d366004611359565b6108c5565b6101656102b036600461146b565b6108f8565b61016f6109b4565b6101b06109ba565b6102d33384846000856109c9565b505050565b60005481565b60035481565b7f48ebe6deff4a5ee645c01506a026031e2a945d6f41f1f4e5098ad65347492c1281565b61031e6103183033878987610dd2565b84610e30565b6001600160a01b0316866001600160a01b0316146103575760405162461bcd60e51b815260040161034e906117d5565b60405180910390fd5b61036486868685856109c9565b505050505050565b6006546001600160a01b031681565b6006546001600160a01b031633146103a55760405162461bcd60e51b815260040161034e906115e2565b6103ad61088b565b8111156103cc5760405162461bcd60e51b815260040161034e90611695565b60015460065460405163a9059cbb60e01b81526001600160a01b039283169263a9059cbb9261040292911690859060040161153f565b602060405180830381600087803b15801561041c57600080fd5b505af1158015610430573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061045491906114c0565b6104705760405162461bcd60e51b815260040161034e9061180c565b50565b6006546001600160a01b0316331461049d5760405162461bcd60e51b815260040161034e90611665565b6104a5610707565b6005546104b29083610e7f565b11156104d05760405162461bcd60e51b815260040161034e90611730565b6001600160a01b038216600090815260046020526040902080546104f49083610e7f565b81556005546105039083610e7f565b6005556000600382015580546040516001600160a01b038516917f2506c43272ded05d095b91dbba876e66e46888157d3e078db5691496e96c5fad916105499190611563565b60405180910390a2505050565b7f7d824962dd0f01520922ea1766c987b1db570cd5db90bdba5ccf5e320607950281565b60026020526000908152604090205481565b6001600160a01b0383166105b25760405162461bcd60e51b815260040161034e9061163d565b6006546001600160a01b0316156105db5760405162461bcd60e51b815260040161034e906116cc565b600680546001600160a01b039485166001600160a01b0319918216179091556001805493909416921691909117909155600055565b6001600160a01b03811660009081526004602052604090206003810154421080159061063f5750600381015415155b61065b5760405162461bcd60e51b815260040161034e90611606565b6001810154815461066b91610ee0565b815560006003820155600181015460055461068591610ee0565b60055580546040516001600160a01b038416917f2506c43272ded05d095b91dbba876e66e46888157d3e078db5691496e96c5fad916106c49190611563565b60405180910390a25050565b60046020526000908152604090208054600182015460028301546003909301549192909184565b600654600160a01b900460ff1681565b6001546040516370a0823160e01b81526000916001600160a01b0316906370a082319061073890309060040161152b565b60206040518083038186803b15801561075057600080fd5b505afa158015610764573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078891906114f8565b905090565b6006546001600160a01b031633146107b75760405162461bcd60e51b815260040161034e90611665565b6001600160a01b038216600090815260046020526040902080548211156107f05760405162461bcd60e51b815260040161034e906116f9565b6000816002015460001461080857816002015461080c565b6000545b4281016003840155600183018490556040519091506001600160a01b038516907fc8305077b495025ec4c1d977b176a762c350bb18cad4666ce1ee85c32b78698a90610859908690611563565b60405180910390a250505050565b7fe95f353750f192082df064ca5142d3a2d6f0bef0f3ffad66d80d8af86b7a749a81565b600061078860055461089b610707565b90610ee0565b7fc2f8787176b8ac6bf7215b4adcc1e069bf4ab82d9ab1df05a57a91d425935b6e81565b6001600160a01b0381166000908152600460205260408120546108f0906108ea61088b565b90610e7f565b90505b919050565b6006546001600160a01b031633146109225760405162461bcd60e51b815260040161034e906115e2565b610936610930308585610f3d565b82610e30565b6001600160a01b0316836001600160a01b0316146109665760405162461bcd60e51b815260040161034e906117d5565b6001600160a01b03831660008181526004602052604090819020600201849055517f7b816003a769eb718bd9c66bdbd2dd5827da3f92bc6645276876bd7957b08cf090610549908590611563565b60055481565b6001546001600160a01b031681565b6006546001600160a01b03163314610a16576109e9610930308786610f95565b6006546001600160a01b03908116911614610a165760405162461bcd60e51b815260040161034e9061179e565b6001600160a01b038516600090815260026020526040812054610a3a908590610ee0565b90506000610a5082610a4b896108c5565b610fce565b6001600160a01b03881660009081526004602052604081205491925090610a78908390610fce565b905084821015610a9a5760405162461bcd60e51b815260040161034e90611767565b8015610aed576001600160a01b038816600090815260046020526040902054610ac39082610ee0565b6001600160a01b038916600090815260046020526040902055600554610ae99082610ee0565b6005555b6001600160a01b038816600090815260026020526040902054610b109083610e7f565b6001600160a01b038916600090815260026020526040902055600354610b369083610e7f565b600355828214610b7d576006805460ff60a01b1916600160a01b1790556040517f3f4449c047e11092ec54dc0751b6b4817a9162745de856c893a26e611d18ffc490600090a15b8415610ccf5760015460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb90610bb5903390899060040161153f565b602060405180830381600087803b158015610bcf57600080fd5b505af1158015610be3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c0791906114c0565b610c235760405162461bcd60e51b815260040161034e9061180c565b6001546001600160a01b031663a9059cbb88610c3f8589610ee0565b6040518363ffffffff1660e01b8152600401610c5c92919061153f565b602060405180830381600087803b158015610c7657600080fd5b505af1158015610c8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cae91906114c0565b610cca5760405162461bcd60e51b815260040161034e9061180c565b610d6f565b60015460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb90610d01908a90869060040161153f565b602060405180830381600087803b158015610d1b57600080fd5b505af1158015610d2f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d5391906114c0565b610d6f5760405162461bcd60e51b815260040161034e9061180c565b336001600160a01b0316876001600160a01b0316896001600160a01b03167f950494fc3642fae5221b6c32e0e45765c95ebb382a04a71b160db0843e74c99f858a8a604051610dc093929190611835565b60405180910390a45050505050505050565b60007f7d824962dd0f01520922ea1766c987b1db570cd5db90bdba5ccf5e32060795028686868686604051602001610e0f96959493929190611591565b60405160208183030381529060405280519060200120905095945050505050565b600080610e43610e3e610fe4565b61103e565b84604051602001610e55929190611510565b604051602081830303815290604052805190602001209050610e7781846110ae565b949350505050565b600082820183811015610ed9576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b600082821115610f37576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60007fe95f353750f192082df064ca5142d3a2d6f0bef0f3ffad66d80d8af86b7a749a848484604051602001610f76949392919061156c565b6040516020818303038152906040528051906020012090509392505050565b60007f48ebe6deff4a5ee645c01506a026031e2a945d6f41f1f4e5098ad65347492c12848484604051602001610f76949392919061156c565b6000818310610fdd5781610ed9565b5090919050565b610fec6112ac565b506040805160a081018252600a6060820190815269436865717565626f6f6b60b01b608083015281528151808301835260038152620312e360ec1b602082810191909152820152469181019190915290565b60007fc2f8787176b8ac6bf7215b4adcc1e069bf4ab82d9ab1df05a57a91d425935b6e826000015180519060200120836020015180519060200120846040015160405160200161109194939291906115c7565b604051602081830303815290604052805190602001209050919050565b60008151604114611106576040805162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015290519081900360640190fd5b60208201516040830151606084015160001a6111248682858561112e565b9695505050505050565b60007f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a082111561118f5760405162461bcd60e51b815260040180806020018281038252602281526020018061184c6022913960400191505060405180910390fd5b8360ff16601b14806111a457508360ff16601c145b6111df5760405162461bcd60e51b815260040180806020018281038252602281526020018061186e6022913960400191505060405180910390fd5b600060018686868660405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa15801561123b573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166112a3576040805162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e61747572650000000000000000604482015290519081900360640190fd5b95945050505050565b60405180606001604052806060815260200160608152602001600081525090565b80356001600160a01b03811681146108f357600080fd5b600082601f8301126112f4578081fd5b813567ffffffffffffffff8082111561130957fe5b604051601f8301601f19168101602001828111828210171561132757fe5b60405282815284830160200186101561133e578384fd5b82602086016020830137918201602001929092529392505050565b60006020828403121561136a578081fd5b610ed9826112cd565b600080600060608486031215611387578182fd5b611390846112cd565b925061139e602085016112cd565b9150604084013590509250925092565b60008060008060008060c087890312156113c6578182fd5b6113cf876112cd565b95506113dd602088016112cd565b945060408701359350606087013567ffffffffffffffff80821115611400578384fd5b61140c8a838b016112e4565b94506080890135935060a0890135915080821115611428578283fd5b5061143589828a016112e4565b9150509295509295509295565b60008060408385031215611454578182fd5b61145d836112cd565b946020939093013593505050565b60008060006060848603121561147f578283fd5b611488846112cd565b925060208401359150604084013567ffffffffffffffff8111156114aa578182fd5b6114b6868287016112e4565b9150509250925092565b6000602082840312156114d1578081fd5b81518015158114610ed9578182fd5b6000602082840312156114f1578081fd5b5035919050565b600060208284031215611509578081fd5b5051919050565b61190160f01b81526002810192909252602282015260420190565b6001600160a01b0391909116815260200190565b6001600160a01b03929092168252602082015260400190565b901515815260200190565b90815260200190565b9384526001600160a01b03928316602085015291166040830152606082015260800190565b9586526001600160a01b03948516602087015292841660408601526060850191909152909116608083015260a082015260c00190565b93845260208401929092526040830152606082015260800190565b6020808252600a90820152693737ba1034b9b9bab2b960b11b604082015260600190565b60208082526019908201527f6465706f736974206e6f74207965742074696d6564206f757400000000000000604082015260600190565b6020808252600e908201526d34b73b30b634b21034b9b9bab2b960911b604082015260600190565b60208082526016908201527529b4b6b83632a9bbb0b81d103737ba1034b9b9bab2b960511b604082015260600190565b6020808252601c908201527f6c697175696442616c616e6365206e6f742073756666696369656e7400000000604082015260600190565b602080825260139082015272185b1c9958591e481a5b9a5d1a585b1a5e9959606a1b604082015260600190565b6020808252601b908201527f68617264206465706f736974206e6f742073756666696369656e740000000000604082015260600190565b6020808252601c908201527f68617264206465706f73697420657863656564732062616c616e636500000000604082015260600190565b6020808252601d908201527f53696d706c65537761703a2063616e6e6f74207061792063616c6c6572000000604082015260600190565b60208082526018908201527f696e76616c696420697373756572207369676e61747572650000000000000000604082015260600190565b6020808252601d908201527f696e76616c69642062656e6566696369617279207369676e6174757265000000604082015260600190565b6020808252600f908201526e1d1c985b9cd9995c8819985a5b1959608a1b604082015260600190565b928352602083019190915260408201526060019056fe45434453413a20696e76616c6964207369676e6174757265202773272076616c756545434453413a20696e76616c6964207369676e6174757265202776272076616c7565a26469706673582212202e31e31c7c2744ee68a03ed443217b2f9f1a54bafefbc8c8e911e1d5cc01ec0864736f6c63430007060033 --------------------------------------------------------------------------------