├── .gitignore ├── configs ├── config4.json ├── config2.json ├── config3.json ├── config1.json ├── config-windows.json └── protocol.json ├── docker-compose.yml ├── scripts ├── start_consensus_node.sh ├── run.sh └── claim_neo_and_gas.py ├── docker_run_and_create_wallet.sh ├── .travis.yml ├── docker_run.sh ├── create_wallet.sh ├── docker_build.sh ├── wallets ├── wallet1.json ├── wallet2.json ├── wallet3.json └── wallet4.json ├── LICENSE ├── Dockerfile └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | neo-privnet.wallet 2 | neo-privnet.wif 3 | *.zip 4 | -------------------------------------------------------------------------------- /configs/config4.json: -------------------------------------------------------------------------------- 1 | { 2 | "ApplicationConfiguration": { 3 | "Paths": { 4 | "Chain": "Chain", 5 | "Notifications": "Notifications" 6 | }, 7 | "P2P": { 8 | "Port": 20336, 9 | "WsPort": 10336 10 | }, 11 | "RPC": { 12 | "Port": 30336, 13 | "SslCert": "", 14 | "SslCertPassword": "" 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /configs/config2.json: -------------------------------------------------------------------------------- 1 | { 2 | "ApplicationConfiguration": { 3 | "Paths": { 4 | "Chain": "Chain", 5 | "Notifications": "Notifications" 6 | }, 7 | "P2P": { 8 | "Port": 20334, 9 | "WsPort": 10334 10 | }, 11 | "RPC": { 12 | "Port": 30334, 13 | "SslCert": "", 14 | "SslCertPassword": "" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /configs/config3.json: -------------------------------------------------------------------------------- 1 | { 2 | "ApplicationConfiguration": { 3 | "Paths": { 4 | "Chain": "Chain", 5 | "Notifications": "Notifications" 6 | }, 7 | "P2P": { 8 | "Port": 20335, 9 | "WsPort": 10335 10 | }, 11 | "RPC": { 12 | "Port": 30335, 13 | "SslCert": "", 14 | "SslCertPassword": "" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /configs/config1.json: -------------------------------------------------------------------------------- 1 | { 2 | "ApplicationConfiguration": { 3 | "Paths": { 4 | "Chain": "Chain", 5 | "Notifications": "Notifications" 6 | }, 7 | "P2P": { 8 | "Port": 20333, 9 | "WsPort": 10333 10 | }, 11 | "RPC": { 12 | "Port": 30333, 13 | "SslCert": "", 14 | "SslCertPassword": "" 15 | } 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | neo-privnet: 4 | build: . 5 | container_name: neo-privnet 6 | ports: 7 | - 10333-10336:10333-10336 8 | - 20333-20336:20333-20336 9 | - 30333-30336:30333-30336 10 | volumes: 11 | - /opt/node1/neo-cli/Chain 12 | - /opt/node2/neo-cli/Chain 13 | - /opt/node3/neo-cli/Chain 14 | - /opt/node4/neo-cli/Chain 15 | -------------------------------------------------------------------------------- /scripts/start_consensus_node.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/expect -f 2 | set dnpath [lindex $argv 0] 3 | set wallet [lindex $argv 1] 4 | set password [lindex $argv 2] 5 | set timeout -1 6 | cd $dnpath 7 | spawn dotnet neo-cli.dll --rpc 8 | expect "neo>" 9 | send "open wallet $wallet\n" 10 | expect "password:" 11 | send "$password\n" 12 | expect "neo>" 13 | send "start consensus\n" 14 | expect "OnStart" 15 | #expect "LIVEFOREVER" 16 | interact 17 | -------------------------------------------------------------------------------- /scripts/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This script starts four consensus and waits forever 4 | # 5 | 6 | screen -dmS node1 expect /opt/start_consensus_node.sh /opt/node1/neo-cli/ wallet1.json one 7 | screen -dmS node2 expect /opt/start_consensus_node.sh /opt/node2/neo-cli/ wallet2.json two 8 | screen -dmS node3 expect /opt/start_consensus_node.sh /opt/node3/neo-cli/ wallet3.json three 9 | screen -dmS node4 expect /opt/start_consensus_node.sh /opt/node4/neo-cli/ wallet4.json four 10 | 11 | sleep infinity 12 | 13 | -------------------------------------------------------------------------------- /docker_run_and_create_wallet.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This script starts the privnet Docker container and: 4 | # 5 | # 1. Create a wallet 6 | # 2. Claim initial NEO and GAS 7 | # 8 | # This will take about 5 minutes. 9 | # 10 | # The output is a wallet file and a WIF key. Both are copied 11 | # into the current directory: 12 | # 13 | # - neo-privnet.wallet (pwd: neo) 14 | # - neo-privnet.wif 15 | # 16 | 17 | ./docker_run.sh 18 | 19 | echo "Waiting 10 seconds to let consensus nodes start..." 20 | sleep 10 21 | 22 | ./create_wallet.sh 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # See also: https://docs.travis-ci.com/user/docker/ 2 | sudo: required 3 | 4 | language: python 5 | python: 6 | - "3.5" 7 | 8 | services: 9 | - docker 10 | 11 | before_install: 12 | - bash ./docker_build.sh 13 | - docker run -d --name neo-privnet -p 20333-20336:20333-20336/tcp -p 30333-30336:30333-30336/tcp -h neo-privnet neo-privnet 14 | - docker ps -a 15 | - sleep 5 16 | - "curl -X POST http://localhost:30333 -d '{ \"jsonrpc\": \"2.0\", \"id\": 1, \"method\": \"getblockcount\", \"params\": [] }'" 17 | 18 | script: 19 | - ./create_wallet.sh 20 | -------------------------------------------------------------------------------- /configs/config-windows.json: -------------------------------------------------------------------------------- 1 | { 2 | "ApplicationConfiguration": { 3 | "Paths": { 4 | "Chain": "Chain", 5 | "Notifications": "Notifications" 6 | }, 7 | "P2P": { 8 | "Port": 20333, 9 | "WsPort": 10333 10 | }, 11 | "RPC": { 12 | "Port": 30333, 13 | "SslCert": "", 14 | "SslCertPassword": "" 15 | }, 16 | "DataDirectoryPath": "ChainTestNet", 17 | "CertCachePath": "Certs", 18 | "NodePort": 20333, 19 | "WsPort": 20334, 20 | "UriPrefix": [ "https://localhost:20332" ], 21 | "SslCert": "", 22 | "SslCertPassword": "", 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /docker_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Start a Docker container which runs the four consensus nodes. If it is 4 | # already running, it will be destroyed first. 5 | # 6 | CONTAINER_NAME="neo-privnet" 7 | CONTAINER=$(docker ps -aqf name=$CONTAINER_NAME) 8 | 9 | if [ -n "$CONTAINER" ]; then 10 | echo "Stopping container named $CONTAINER_NAME" 11 | docker stop $CONTAINER_NAME 1>/dev/null 12 | echo "Removing container named $CONTAINER_NAME" 13 | docker rm $CONTAINER_NAME 1>/dev/null 14 | fi 15 | 16 | echo "Starting container..." 17 | docker run -d --name $CONTAINER_NAME -p 20333-20336:20333-20336/tcp -p 30333-30336:30333-30336/tcp -h $CONTAINER_NAME $CONTAINER_NAME 18 | -------------------------------------------------------------------------------- /configs/protocol.json: -------------------------------------------------------------------------------- 1 | { 2 | "ProtocolConfiguration": { 3 | "Magic": 56753, 4 | "AddressVersion": 23, 5 | "StandbyValidators": [ 6 | "02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2", 7 | "02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e", 8 | "03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699", 9 | "02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62" 10 | ], 11 | "SeedList": [ 12 | "127.0.0.1:20333", 13 | "127.0.0.1:20334", 14 | "127.0.0.1:20335", 15 | "127.0.0.1:20336" 16 | ], 17 | "SystemFee": { 18 | "EnrollmentTransaction": 1000, 19 | "IssueTransaction": 500, 20 | "PublishTransaction": 500, 21 | "RegisterTransaction": 10000 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /create_wallet.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | WALLET_PWD="coz" 4 | 5 | echo "Starting script to claim NEO and GAS..." 6 | CLAIM_CMD="python3.5 /opt/neo-python/claim_neo_and_gas.py -o /tmp/wallet -p ${WALLET_PWD} -w /tmp/wif" 7 | DOCKER_CMD="docker exec -it neo-privnet ${CLAIM_CMD}" 8 | echo $DOCKER_CMD 9 | echo 10 | ($DOCKER_CMD) 11 | 12 | echo 13 | echo "Copying wallet file and wif key out of Docker container..." 14 | docker cp neo-privnet:/tmp/wif ./neo-privnet.wif 15 | docker cp neo-privnet:/tmp/wallet ./neo-privnet.wallet 16 | 17 | echo 18 | echo "--------------------" 19 | echo 20 | echo "All done! You now have 2 files in the current directory:" 21 | echo 22 | echo " neo-privnet.wallet .. a wallet you can use with neo-python (pwd: ${WALLET_PWD})" 23 | echo " neo-privnet.wif ..... a wif private key you can import into other clients" 24 | echo 25 | echo "Enjoy!" 26 | -------------------------------------------------------------------------------- /docker_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # To use a newer neo-cli version, just update this variable: 5 | NEO_CLI_VERSION="2.6.0" 6 | 7 | # Definition of standard neo-cli filenames and URL based on the version 8 | NEO_CLI_ZIPFN="neo-release-${NEO_CLI_VERSION}.zip" 9 | NEO_CLI_URL="https://github.com/neo-project/neo-cli/releases/download/v${NEO_CLI_VERSION}/neo-cli-ubuntu.16.04-x64.zip" 10 | 11 | if [ -z "$1" ]; 12 | then 13 | echo "Using default neo-cli v${NEO_CLI_VERSION}" 14 | 15 | if [ -e "${NEO_CLI_ZIPFN}" ] 16 | then 17 | echo "- release already downloaded: ${NEO_CLI_ZIPFN}" 18 | else 19 | echo "- downloading ${NEO_CLI_URL}..." 20 | wget --no-check-certificate -O $NEO_CLI_ZIPFN $NEO_CLI_URL || (rm -f $NEO_CLI_ZIPFN && exit 1) 21 | fi 22 | cp $NEO_CLI_ZIPFN ./neo-cli.zip 23 | else 24 | echo "Using custom neo-cli.zip: $1" 25 | cp $1 ./neo-cli.zip 26 | fi 27 | 28 | docker build -t neo-privnet . 29 | -------------------------------------------------------------------------------- /wallets/wallet1.json: -------------------------------------------------------------------------------- 1 | {"name":"wallet1","version":"1.0","scrypt":{"n":16384,"r":8,"p":8},"accounts":[{"address":"AKkkumHbBipZ46UMZJoFynJMXzSRnBvKcs","label":null,"isDefault":false,"lock":false,"key":"6PYLmjBYJ4wQTCEfqvnznGJwZeW9pfUcV5m5oreHxqryUgqKpTRAFt9L8Y","contract":{"script":"2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2ac","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"extra":null},{"address":"AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU","label":null,"isDefault":false,"lock":false,"key":"6PYLmjBYJ4wQTCEfqvnznGJwZeW9pfUcV5m5oreHxqryUgqKpTRAFt9L8Y","contract":{"script":"532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae","parameters":[{"name":"parameter0","type":"Signature"},{"name":"parameter1","type":"Signature"},{"name":"parameter2","type":"Signature"}],"deployed":false},"extra":null}],"extra":null} -------------------------------------------------------------------------------- /wallets/wallet2.json: -------------------------------------------------------------------------------- 1 | {"name":"wallet2","version":"1.0","scrypt":{"n":16384,"r":8,"p":8},"accounts":[{"address":"AWLYWXB8C9Lt1nHdDZJnC5cpYJjgRDLk17","label":null,"isDefault":false,"lock":false,"key":"6PYXHjPaNvW8YknSXaKsTWjf9FRxo1s4naV2jdmSQEgzaqKGX368rndN3L","contract":{"script":"2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406eac","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"extra":null},{"address":"AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU","label":null,"isDefault":false,"lock":false,"key":"6PYXHjPaNvW8YknSXaKsTWjf9FRxo1s4naV2jdmSQEgzaqKGX368rndN3L","contract":{"script":"532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae","parameters":[{"name":"parameter0","type":"Signature"},{"name":"parameter1","type":"Signature"},{"name":"parameter2","type":"Signature"}],"deployed":false},"extra":null}],"extra":null} -------------------------------------------------------------------------------- /wallets/wallet3.json: -------------------------------------------------------------------------------- 1 | {"name":"wallet3","version":"1.0","scrypt":{"n":16384,"r":8,"p":8},"accounts":[{"address":"AR3uEnLUdfm1tPMJmiJQurAXGL7h3EXQ2F","label":null,"isDefault":false,"lock":false,"key":"6PYX86vYiHfUbpD95hfN1xgnvcSxy5skxfWYKu3ztjecxk6ikYs2kcWbeh","contract":{"script":"2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699ac","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"extra":null},{"address":"AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU","label":null,"isDefault":false,"lock":false,"key":"6PYX86vYiHfUbpD95hfN1xgnvcSxy5skxfWYKu3ztjecxk6ikYs2kcWbeh","contract":{"script":"532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae","parameters":[{"name":"parameter0","type":"Signature"},{"name":"parameter1","type":"Signature"},{"name":"parameter2","type":"Signature"}],"deployed":false},"extra":null}],"extra":null} -------------------------------------------------------------------------------- /wallets/wallet4.json: -------------------------------------------------------------------------------- 1 | {"name":"wallet4","version":"1.0","scrypt":{"n":16384,"r":8,"p":8},"accounts":[{"address":"AJmjUqf1jDenxYpuNS4i2NxD9FQYieDpBF","label":null,"isDefault":false,"lock":false,"key":"6PYRXVwHSqFSukL3CuXxdQ75VmsKpjeLgQLEjt83FrtHf1gCVphHzdD4nc","contract":{"script":"2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62ac","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"extra":null},{"address":"AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU","label":null,"isDefault":false,"lock":false,"key":"6PYRXVwHSqFSukL3CuXxdQ75VmsKpjeLgQLEjt83FrtHf1gCVphHzdD4nc","contract":{"script":"532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae","parameters":[{"name":"parameter0","type":"Signature"},{"name":"parameter1","type":"Signature"},{"name":"parameter2","type":"Signature"}],"deployed":false},"extra":null}],"extra":null} -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # NEO private network - Dockerfile 2 | 3 | FROM microsoft/dotnet:2.0-runtime 4 | LABEL maintainer="City of Zion" 5 | LABEL authors="hal0x2328, phetter, metachris, ashant" 6 | 7 | ENV DEBIAN_FRONTEND noninteractive 8 | 9 | # Disable dotnet usage information collection 10 | ENV DOTNET_CLI_TELEMETRY_OPTOUT 1 11 | 12 | # Install system dependencies. always should be done in one line 13 | # https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#run 14 | RUN apt-get update && apt-get install -y \ 15 | unzip \ 16 | screen \ 17 | expect \ 18 | libleveldb-dev \ 19 | git-core \ 20 | python3.5-dev python3-pip libssl-dev 21 | 22 | # APT cleanup to reduce image size 23 | RUN rm -rf /var/lib/apt/lists/* 24 | 25 | # neo-python setup 26 | RUN git clone https://github.com/CityOfZion/neo-python.git /opt/neo-python 27 | RUN cd /opt/neo-python && git checkout origin/master 28 | RUN pip3 install -r /opt/neo-python/requirements.txt 29 | 30 | # Add the neo-cli package 31 | ADD ./neo-cli.zip /opt/neo-cli.zip 32 | 33 | # Extract and prepare four consensus nodes 34 | RUN unzip -q -d /opt/node1 /opt/neo-cli.zip 35 | RUN unzip -q -d /opt/node2 /opt/neo-cli.zip 36 | RUN unzip -q -d /opt/node3 /opt/neo-cli.zip 37 | RUN unzip -q -d /opt/node4 /opt/neo-cli.zip 38 | 39 | # Add config files 40 | ADD ./configs/config1.json /opt/node1/neo-cli/config.json 41 | ADD ./configs/protocol.json /opt/node1/neo-cli/protocol.json 42 | ADD ./wallets/wallet1.json /opt/node1/neo-cli/ 43 | 44 | ADD ./configs/config2.json /opt/node2/neo-cli/config.json 45 | ADD ./configs/protocol.json /opt/node2/neo-cli/protocol.json 46 | ADD ./wallets/wallet2.json /opt/node2/neo-cli/ 47 | 48 | ADD ./configs/config3.json /opt/node3/neo-cli/config.json 49 | ADD ./configs/protocol.json /opt/node3/neo-cli/protocol.json 50 | ADD ./wallets/wallet3.json /opt/node3/neo-cli/ 51 | 52 | ADD ./configs/config4.json /opt/node4/neo-cli/config.json 53 | ADD ./configs/protocol.json /opt/node4/neo-cli/protocol.json 54 | ADD ./wallets/wallet4.json /opt/node4/neo-cli/ 55 | 56 | # Add scripts 57 | ADD ./scripts/run.sh /opt/ 58 | ADD ./scripts/start_consensus_node.sh /opt/ 59 | ADD ./scripts/claim_neo_and_gas.py /opt/neo-python/ 60 | 61 | # Inform Docker what ports to expose 62 | EXPOSE 20333 63 | EXPOSE 20334 64 | EXPOSE 20335 65 | EXPOSE 20336 66 | 67 | EXPOSE 30333 68 | EXPOSE 30334 69 | EXPOSE 30335 70 | EXPOSE 30336 71 | 72 | # On docker run, start the consensus nodes 73 | CMD ["/bin/bash", "/opt/run.sh"] 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # neo-privatenet-docker 2 | 3 | [![Build Status](https://travis-ci.org/CityOfZion/neo-privatenet-docker.svg?branch=master)](https://travis-ci.org/CityOfZion/neo-privatenet-docker) 4 | 5 | This is a convenient way to run a private Neo blockchain. The image is based on microsoft/dotnet:2.0-runtime, 6 | please review Dockerfile for details. 7 | 8 | This image is meant to skip the overhead of having to wait to get enough gas for smart contract testing on testnet and to bypass the steps of creating your own private chain. 9 | 10 | See the section below on extracting Neo and Gas as the private chain in this Docker image starts at block height 0. 11 | 12 | You will also need to install and configure your neo client to use this private network, which involves editing the protocol.json file to point the seeds at your docker IP addresses. 13 | 14 | **Note:** There is also a turnkey Docker image with the initial 100m NEO and 16.6k GAS already claimed in a ready-to-use wallet available here: https://hub.docker.com/r/metachris/neo-privnet-with-gas/ 15 | 16 | 17 | ## Instructions 18 | 19 | Clone the repository and build the Docker image: 20 | 21 | git clone https://github.com/CityOfZion/neo-privatenet-docker.git 22 | cd neo-privatenet-docker 23 | ./docker_build.sh 24 | 25 | Just start the private network: 26 | 27 | ./docker_run.sh 28 | 29 | Start the private network, create a wallet and automatically claim the initial NEO and 48 GAS (takes about 5 minutes): 30 | 31 | ./docker_run_and_create_wallet.sh 32 | 33 | _or_, if you prefer `docker-compose`, you can start the nodes with: 34 | 35 | docker-compose up -d 36 | 37 | You can now claim the initial NEO and GAS: 38 | 39 | ./create_wallet.sh 40 | 41 | `./create_wallet` will display several internal error messages, which is expected as long as at the end you still get a success message. 42 | 43 | If you call `./create_wallet.sh` or `./docker_run_and_create_wallet.sh`, it will create 2 files in your current directory: 44 | 45 | - `neo-privnet.wallet`: a wallet you can use with neo-python 46 | - `neo-privnet.wif`: a wif private key you can import into other clients (neo-gui for example). 47 | 48 | Those files will get you access to the wallet containing all the NEO and GAS for your private network. 49 | 50 | ## Install neo-gui or neo-gui-developer 51 | 52 | Install one of the following: 53 | 54 | https://github.com/neo-project/neo-gui 55 | 56 | https://github.com/CityOfZion/neo-gui-developer 57 | 58 | Edit the protocol.json in your respective neo-gui installation to point to the IP of the system running your docker. 59 | Please note the ports listed match the private chain ports in the current docker build. 60 | 61 | If you copy the protocol.json file from the configs directory of this repo and replace your neo-gui protocol.json you will only need to find and edit the section that looks like the following: 62 | 63 | "SeedList": [ 64 | "127.0.0.1:20333", 65 | "127.0.0.1:20334", 66 | "127.0.0.1:20335", 67 | "127.0.0.1:20336" 68 | ], 69 | 70 | Change each occurrence of 127.0.0.1 to the IP of the system or vm running your docker image. 71 | 72 | If you don't copy the protocol.json from the docker configs directory of this repo, in addition to the "SeedList" modifications mentioned above, you will also need to edit the following: 73 | 74 | 1. Change value "Magic" to 56753 75 | 2. Copy the public keys of each of your node wallets into the "StandbyValidators" section 76 | 77 | --- 78 | 79 | ##### For users who use docker machine (i.e Windows Home Edition users without Hyper-V) 80 | 81 | You'll need your docker machine IP. First, get the name of your machine: 82 | 83 | docker-machine ls 84 | 85 | And get the ip with: 86 | 87 | docker-machine ip "Nameofyourmachine" 88 | 89 | (By default, the machine name is "default"). Use this ip to replace each occurence of 127.0.0.1 in the SeedList array. 90 | 91 | ## Copy wallets from docker image to neo-gui 92 | 93 | Note: You won't need this step if you used `./create_wallet.sh` or `./docker_run_and_create_wallet.sh` in the previous step (The multiparty signature and neo/gas extraction should already be done). 94 | 95 | Once your docker image is running, use the following commands to copy each node's wallet to your neo-gui home directory in preparation for multiparty signature and neo/gas extraction. 96 | Note: all four must be copied. 97 | 98 | The following will copy each wallet from the docker image to the current working directory. 99 | 100 | docker cp neo-privnet:/opt/node1/neo-cli/wallet1.json . 101 | docker cp neo-privnet:/opt/node2/neo-cli/wallet2.json . 102 | docker cp neo-privnet:/opt/node3/neo-cli/wallet3.json . 103 | docker cp neo-privnet:/opt/node4/neo-cli/wallet4.json . 104 | 105 | ## Wallet Passwords 106 | 107 | node1: one 108 | 109 | node2: two 110 | 111 | node3: three 112 | 113 | node4: four 114 | 115 | ## Extracting Neo and Gas 116 | Check out the docs at http://docs.neo.org/en-us/node/private-chain.html for instructions on how to claim Neo and Gas 117 | for testing. 118 | -------------------------------------------------------------------------------- /scripts/claim_neo_and_gas.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | privnet-claim-neo-and-gas.py 5 | 6 | With this script you can transfer all NEO from the default contract on a private network 7 | such as the one created by the neo-privatenet-docker image, without having to 8 | use the neo-gui client (or Windows at all). It also waits a little bit to generate 9 | GAS and claims it. 10 | 11 | The output of the script is two things: 12 | 13 | * A wallet with 100,000,000 NEO and 48 GAS (after 1 minute) which you can use with prompt.py 14 | * A WIF private key you can use with any client. 15 | 16 | Run it like this: 17 | 18 | python3 scripts/privnet-claim-neo-and-gas.py 19 | 20 | There are several parameters you can configure with cli args. Take a look at the help: 21 | 22 | python3 scripts/privnet-claim-neo-and-gas.py -h 23 | 24 | This script takes several minutes to complete. Be patient! 25 | """ 26 | 27 | import os 28 | import sys 29 | import json 30 | import time 31 | import datetime 32 | import argparse 33 | 34 | from tempfile import NamedTemporaryFile 35 | from Crypto import Random 36 | 37 | from neo.Implementations.Wallets.peewee.UserWallet import UserWallet 38 | from neo.Implementations.Blockchains.LevelDB.LevelDBBlockchain import LevelDBBlockchain 39 | from neo.Wallets.KeyPair import KeyPair 40 | from neo.Prompt.Commands.LoadSmartContract import ImportMultiSigContractAddr 41 | from neo.Core.Blockchain import Blockchain 42 | from neo.Fixed8 import Fixed8 43 | from neo.Prompt.Commands.Send import construct_and_send 44 | from neo.Prompt.Commands.Wallet import ClaimGas 45 | from neo.Core.TX.Transaction import TransactionOutput, ContractTransaction 46 | from neo.SmartContract.ContractParameterContext import ContractParametersContext 47 | from neo.Network.NodeLeader import NodeLeader 48 | from twisted.internet import reactor, task 49 | from neo.Settings import settings 50 | 51 | WALLET_PATH = "/tmp/privnet1" 52 | WALLET_PWD = "neo" 53 | MINUTES_TO_WAIT_UNTIL_GAS_CLAIM = 1 54 | 55 | # default multi-sig contract from the neo-privatenet-docker image. If you are using a different private net 56 | # configuration, you'll need to add all your node keys and multi-sig address in place of the ones below 57 | 58 | multisig_addr = 'AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU' 59 | nodekeys = {'02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2': 'KxyjQ8eUa4FHt3Gvioyt1Wz29cTUrE4eTqX3yFSk1YFCsPL8uNsY', 60 | '02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e': 'KzfPUYDC9n2yf4fK5ro4C8KMcdeXtFuEnStycbZgX3GomiUsvX6W', 61 | '03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699': 'L2oEXKRAAMiPEZukwR5ho2S6SMeQLhcK9mF71ZnF7GvT8dU4Kkgz', 62 | '02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62': 'KzgWE3u3EDp13XPXXuTKZxeJ3Gi8Bsm8f9ijY3ZsCKKRvZUo1Cdn'} 63 | 64 | 65 | class PrivnetClaimall(object): 66 | start_height = None 67 | start_dt = None 68 | _walletdb_loop = None 69 | 70 | wallet_fn = None 71 | wallet_pwd = None 72 | min_wait = None 73 | wif_fn = None 74 | 75 | def __init__(self, wallet_fn, wallet_pwd, min_wait, wif_fn): 76 | self.wallet_fn = wallet_fn 77 | self.wallet_pwd = wallet_pwd 78 | self.min_wait = min_wait 79 | self.wif_fn = wif_fn 80 | 81 | self.start_height = Blockchain.Default().Height 82 | self.start_dt = datetime.datetime.utcnow() 83 | 84 | def quit(self): 85 | print('Shutting down. This may take a bit...') 86 | self.go_on = False 87 | Blockchain.Default().Dispose() 88 | reactor.stop() 89 | NodeLeader.Instance().Shutdown() 90 | 91 | @staticmethod 92 | def send_neo(wallet, address_from, address_to, amount): 93 | assetId = None 94 | 95 | assetId = Blockchain.Default().SystemShare().Hash 96 | 97 | scripthash_to = wallet.ToScriptHash(address_to) 98 | scripthash_from = wallet.ToScriptHash(address_from) 99 | 100 | f8amount = Fixed8.TryParse(amount) 101 | 102 | if f8amount.value % pow(10, 8 - Blockchain.Default().GetAssetState(assetId.ToBytes()).Precision) != 0: 103 | raise Exception("incorrect amount precision") 104 | 105 | fee = Fixed8.Zero() 106 | 107 | output = TransactionOutput(AssetId=assetId, Value=f8amount, script_hash=scripthash_to) 108 | tx = ContractTransaction(outputs=[output]) 109 | ttx = wallet.MakeTransaction(tx=tx, change_address=None, fee=fee, from_addr=scripthash_from) 110 | 111 | if ttx is None: 112 | raise Exception("insufficient funds, were funds already moved from multi-sig contract?") 113 | 114 | context = ContractParametersContext(tx, isMultiSig=True) 115 | wallet.Sign(context) 116 | 117 | if context.Completed: 118 | raise Exception("Something went wrong, multi-sig transaction failed") 119 | 120 | else: 121 | print("Transaction initiated") 122 | return json.dumps(context.ToJson(), separators=(',', ':')) 123 | 124 | def sign_and_finish(self, wallet, jsn): 125 | 126 | context = ContractParametersContext.FromJson(jsn) 127 | if context is None: 128 | print("Failed to parse JSON") 129 | return None 130 | 131 | wallet.Sign(context) 132 | 133 | if context.Completed: 134 | 135 | print("Signature complete, relaying...") 136 | 137 | tx = context.Verifiable 138 | tx.scripts = context.GetScripts() 139 | 140 | wallet.SaveTransaction(tx) 141 | 142 | print("will send tx: %s " % json.dumps(tx.ToJson(), indent=4)) 143 | 144 | relayed = NodeLeader.Instance().Relay(tx) 145 | 146 | if relayed: 147 | print("Relayed Tx: %s " % tx.Hash.ToString()) 148 | self.wait_for_tx(tx) 149 | return('success') 150 | 151 | else: 152 | print("Could not relay tx %s " % tx.Hash.ToString()) 153 | return('fail') 154 | else: 155 | print("Transaction signed, but the signature is still incomplete") 156 | return(json.dumps(context.ToJson(), separators=(',', ':'))) 157 | 158 | def wait_for_tx(self, tx, max_seconds=300): 159 | """ Wait for tx to show up on blockchain """ 160 | foundtx = False 161 | sec_passed = 0 162 | while not foundtx and sec_passed < max_seconds: 163 | _tx, height = Blockchain.Default().GetTransaction(tx.Hash.ToString()) 164 | if height > -1: 165 | foundtx = True 166 | continue 167 | print("Waiting for tx {} to show up on blockchain...".format(tx.Hash.ToString())) 168 | time.sleep(3) 169 | sec_passed += 3 170 | if foundtx: 171 | return True 172 | else: 173 | print("Transaction was relayed but never accepted by consensus node") 174 | return False 175 | 176 | def run(self): 177 | dbloop = task.LoopingCall(Blockchain.Default().PersistBlocks) 178 | dbloop.start(.1) 179 | Blockchain.Default().PersistBlocks() 180 | 181 | while Blockchain.Default().Height < 2: 182 | print("Waiting for wallet to sync...") 183 | time.sleep(1) 184 | 185 | print("Creating Wallet...") 186 | self.wallet = UserWallet.Create(path=self.wallet_fn, password=self.wallet_pwd) 187 | self.wallet.ProcessBlocks() 188 | 189 | # Extract infos from wallet 190 | contract = self.wallet.GetDefaultContract() 191 | key = self.wallet.GetKey(contract.PublicKeyHash) 192 | address = key.GetAddress() 193 | wif = key.Export() 194 | print("- Address:", address) 195 | print("- WIF key:", wif) 196 | self.wallet = None 197 | 198 | # Claim initial NEO 199 | self.claim_initial_neo(address) 200 | 201 | # Open wallet again 202 | self.wallet = UserWallet.Open(self.wallet_fn, self.wallet_pwd) 203 | self._walletdb_loop = task.LoopingCall(self.wallet.ProcessBlocks) 204 | self._walletdb_loop.start(1) 205 | 206 | print("\nWait %s min before claiming GAS." % self.min_wait) 207 | time.sleep(60 * self.min_wait) 208 | 209 | print("\nSending NEO to own wallet...") 210 | tx = construct_and_send(None, self.wallet, ["neo", address, "100000000"], prompt_password=False) 211 | if not tx: 212 | print("Something went wrong, no tx.") 213 | return 214 | 215 | # Wait until transaction is on blockchain 216 | self.wait_for_tx(tx) 217 | 218 | print("Claiming the GAS...") 219 | claim_tx, relayed = ClaimGas(self.wallet, require_password=False) 220 | self.wait_for_tx(claim_tx) 221 | 222 | # Finally, need to rebuild the wallet 223 | self.wallet.Rebuild() 224 | 225 | print("\nAll done!") 226 | print("- WIF key: %s" % wif) 227 | print("- Wallet file: %s" % self.wallet_fn) 228 | print("- Wallet pwd: %s" % self.wallet_pwd) 229 | 230 | if self.wif_fn: 231 | with open(self.wif_fn, "w") as f: 232 | f.write(wif) 233 | 234 | self.quit() 235 | 236 | def claim_initial_neo(self, target_address): 237 | wallets = [] 238 | i = 0 239 | tx_json = None 240 | dbloops = [] 241 | 242 | print("Signing new transaction with 3 of 4 node keys...") 243 | for pkey, wif in nodekeys.items(): 244 | walletpath = "wallet{}.db3".format(i + 1) 245 | if os.path.exists(walletpath): 246 | os.remove(walletpath) 247 | wallet = UserWallet.Create(path=walletpath, password=self.wallet_pwd) 248 | wallets.append(wallet) 249 | 250 | print("Importing node private key to to {}".format(walletpath)) 251 | prikey = KeyPair.PrivateKeyFromWIF(wif) 252 | wallet.CreateKey(prikey) 253 | 254 | print("Importing multi-sig contract to {}".format(walletpath)) 255 | multisig_args = [pkey, '3'] 256 | multisig_args.extend(list(nodekeys.keys())) 257 | ImportMultiSigContractAddr(wallet, multisig_args) 258 | 259 | dbloop = task.LoopingCall(wallet.ProcessBlocks) 260 | dbloop.start(1) 261 | dbloops.append(dbloop) 262 | 263 | # print("Wallet %s " % json.dumps(wallet.ToJson(), indent=4)) 264 | 265 | if i == 0: 266 | print("Creating spend transaction to {}".format(target_address)) 267 | tx_json = self.send_neo(wallet, multisig_addr, target_address, '100000000') 268 | if tx_json is None: 269 | break 270 | else: 271 | tx_json = self.sign_and_finish(wallet, tx_json) 272 | 273 | if tx_json == 'success': 274 | print("Finished, {} should now own all the NEO on the private network.".format(target_address)) 275 | break 276 | i += 1 277 | 278 | 279 | if __name__ == "__main__": 280 | parser = argparse.ArgumentParser() 281 | parser.add_argument("-o", "--output", action="store", help="Filename of wallet that will be created (default: %s)" % WALLET_PATH, default=WALLET_PATH) 282 | parser.add_argument("-p", "--password", action="store", help="Wallet password (default: %s)" % WALLET_PWD, default=WALLET_PWD) 283 | parser.add_argument("-t", "--time", action="store", help="Minutes to wait for the NEO to generate GAS (default: %s)" % MINUTES_TO_WAIT_UNTIL_GAS_CLAIM, default=MINUTES_TO_WAIT_UNTIL_GAS_CLAIM) 284 | parser.add_argument("-w", "--save-privnet-wif", action="store", help="Filename to store created privnet wif key") 285 | args = parser.parse_args() 286 | 287 | if os.path.isfile(args.output): 288 | print("Error: Wallet file %s already exists" % args.output) 289 | exit(1) 290 | 291 | settings.setup_privnet() 292 | print("Blockchain DB path:", settings.LEVELDB_PATH) 293 | if os.path.exists(settings.LEVELDB_PATH): 294 | print("Warning: Chain database already exists. If this is from a previous private network, you need to delete %s" % settings.LEVELDB_PATH) 295 | 296 | blockchain = LevelDBBlockchain(settings.LEVELDB_PATH) 297 | Blockchain.RegisterBlockchain(blockchain) 298 | 299 | reactor.suggestThreadPoolSize(15) 300 | NodeLeader.Instance().Start() 301 | 302 | pc = PrivnetClaimall(args.output, args.password, args.time, args.save_privnet_wif) 303 | reactor.callInThread(pc.run) 304 | reactor.run() 305 | --------------------------------------------------------------------------------