Explorar el Código

Merge pull request #93 from Joystream/development

Rome release
Martin hace 5 años
padre
commit
441c04b54d

+ 89 - 0
.travis.yml

@@ -0,0 +1,89 @@
+language: rust
+
+rust:
+  - 1.41.1
+
+matrix:
+  include:
+    - os: linux
+      env: TARGET=x86_64-unknown-linux-gnu
+    - os: linux
+      env: TARGET=arm-unknown-linux-gnueabihf
+      services: docker
+    - os: osx
+      env: TARGET=x86_64-apple-darwin
+
+before_install:
+  - rustup component add rustfmt
+  - cargo fmt --all -- --check
+
+install:
+  - |
+    if [ "$TARGET" = "arm-unknown-linux-gnueabihf" ]
+    then
+      docker pull joystream/rust-raspberry
+    else
+      rustup update nightly
+      rustup target add ${TARGET}
+      rustup target add wasm32-unknown-unknown --toolchain nightly
+      rustup show
+    fi
+
+script:
+  - |
+    if [ "$TARGET" = "arm-unknown-linux-gnueabihf" ]
+    then
+      docker run -u root \
+        --volume ${TRAVIS_BUILD_DIR}:/home/cross/project \
+          joystream/rust-raspberry \
+        build --release
+      sudo chmod a+r ${TRAVIS_BUILD_DIR}/target/${TARGET}/release/joystream-node
+    else
+      cargo build --release --target=${TARGET}
+    fi
+
+before_deploy:
+  - cp ./target/${TARGET}/release/joystream-node .
+  - |
+    if [ "$TARGET" = "arm-unknown-linux-gnueabihf" ]
+    then
+      export FILENAME="joystream-node-armv7-linux-gnueabihf"
+    else
+      export FILENAME=`./joystream-node --version | sed -e "s/ /-/g"`
+    fi
+  - tar -cf ${FILENAME}.tar ./joystream-node
+  - gzip ${FILENAME}.tar
+
+deploy:
+  - provider: releases
+    api_key:
+      secure: QTna4XzKmPrXNA5KnYfLsH8cAKxESLdFbQ5HJ6nvB9reE10SVtg8lZ+ShL+no7TACNBUNt09Qv9HNgs6JcNRJ9QMHEJHKIbMyjplhBtZ+W3l0k+6TL0yeKHZ/OvddDF+vDbpN+y4xBfOf0xqZcNH3lZJTms/NPBn/KT5DpQ3JZ8bibdMP2HSCazfvHLwj38OuLX6VWbFcmN2RAmUR9AXYvk5wWYVw8g1VDzTCxjH1G+dGWH1L9+ZDgFfv7BNSNhPc6V9GghgLVZ+37r/STzTTAQ/gPv+yruglEWUhSAngFfVYUegvTmIeQLi/V+g0tKUS+l7eNX08xz6eZcn0+/32V7P+oEN/dhU84E0kgmiOsiUEGI/KFM+qw9TyX3GtD67UmG5TZrD7OUMIu1qCuPSetsTOK2kvpwlYAn+j5iFB30Uz4hXhOH5dib2zz2I7cYHi1kvzeNQqQOPNDCmaO48bcbRIaeqMAHdsb6scGzh/+CD2V2HOmHlhd+4o1PpX6hAMwmOXAu3bMDi4zlB9Hb1cSZnsYNBHawkD6y45QGepFKpGW/6u5VRPeMK62Gm9wu815C36B4mVg6CVqtZMbk0WYPIk6zfoTft3i04YthKbRO96a5VD9LssVbiSYnudXuZJjSllSZVCi9AKS8JVIS2jC2z+tWkquAesSrwztriRcs=
+    file: ${FILENAME}.tar.gz
+    on:
+      tags: true
+      repo: Joystream/substrate-node-joystream
+    draft: true
+    overwrite: true
+    skip_cleanup: true
+  - provider: releases
+    api_key:
+      secure: QTna4XzKmPrXNA5KnYfLsH8cAKxESLdFbQ5HJ6nvB9reE10SVtg8lZ+ShL+no7TACNBUNt09Qv9HNgs6JcNRJ9QMHEJHKIbMyjplhBtZ+W3l0k+6TL0yeKHZ/OvddDF+vDbpN+y4xBfOf0xqZcNH3lZJTms/NPBn/KT5DpQ3JZ8bibdMP2HSCazfvHLwj38OuLX6VWbFcmN2RAmUR9AXYvk5wWYVw8g1VDzTCxjH1G+dGWH1L9+ZDgFfv7BNSNhPc6V9GghgLVZ+37r/STzTTAQ/gPv+yruglEWUhSAngFfVYUegvTmIeQLi/V+g0tKUS+l7eNX08xz6eZcn0+/32V7P+oEN/dhU84E0kgmiOsiUEGI/KFM+qw9TyX3GtD67UmG5TZrD7OUMIu1qCuPSetsTOK2kvpwlYAn+j5iFB30Uz4hXhOH5dib2zz2I7cYHi1kvzeNQqQOPNDCmaO48bcbRIaeqMAHdsb6scGzh/+CD2V2HOmHlhd+4o1PpX6hAMwmOXAu3bMDi4zlB9Hb1cSZnsYNBHawkD6y45QGepFKpGW/6u5VRPeMK62Gm9wu815C36B4mVg6CVqtZMbk0WYPIk6zfoTft3i04YthKbRO96a5VD9LssVbiSYnudXuZJjSllSZVCi9AKS8JVIS2jC2z+tWkquAesSrwztriRcs=
+    file: ${FILENAME}.tar.gz
+    on:
+      branch: development
+      repo: Joystream/substrate-node-joystream
+    draft: true
+    prerelease: true
+    overwrite: true
+    skip_cleanup: true
+  - provider: releases
+    api_key:
+      secure: ZoEXp8g+yZOEG8JZ1Fg6tWnW3aYDfupFbZflEejYaAdXhj1nw7G9N10ZX5VDdb/O1iFx8BhfFymQxk0ynxNC8c52LzOjKIhXEporxgvEPdnoPS/N1JhfsOUV0ragwZDLv2tFVi2AT0K4w8WJFJDzrK4qHOMMQgVbVQZtFmDL1whHdfBD5FyFyKmMdZdWBtTGy4s7X0LnmxjNB4/3AMa540T3LowZ5H66MYZkQmAbtg8ib93WomVanhS23vbjNaH9x1Kmzxd2B3pCSgI8uaxBrpmzINvAeSusYVJQt0EF/cAPXmq0+JmGoocvcS1ecg/SNZoKUNmeElB4ns/obg/QAyE+fyQtyl+iDYBilhFLm5xRMUnqkpyeUUD3u824i/Z+/tfLvtm5Egg1QAiXtIIJMeAj1nN8OIeSlHR4phnSTA3jl2PZw9QYidtV9WCqHC0qxtpkYSKkC8ItaefScPB1AuvOvVx8xvnIxfR/tXvL8Y3Y2BvhiLgpky9JkbdMln1b0m0E5c4vyGCEVqHqpbxM63VJkpct8sVx0atGvipWEelVjz5XpkxW2PYbgg4EKUzl3FiYcXwf5Y/ykxaZNZt7I4gv9nz2KkVwUCCPqdwWF7ww1shFWW5tCoCmJuUESOdPFx0jQ7LVWz7SDLDsqvvaW2c2OPxG6DIx9BiTeAE4qIQ=
+    file: "${FILENAME}.tar.gz"
+    skip_cleanup: true
+    draft: true
+    prerelease: true
+    overwrite: true
+    on:
+      repo: mnaamani/substrate-node-joystream
+      branch: deploy

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 311 - 281
Cargo.lock


+ 109 - 44
Cargo.toml

@@ -7,86 +7,151 @@ authors = ['Joystream']
 build = 'build.rs'
 edition = '2018'
 name = 'joystream-node'
-version = '1.0.0'
+version = '2.1.2'
 
 [dependencies]
-error-chain = '0.12'
-exit-future = '0.1'
-futures = '0.1'
-hex-literal = '0.1'
-log = '0.4'
-parity-codec = '3.2'
-parking_lot = '0.7.1'
-tokio = '0.1'
-trie-root = '0.12.0'
-
-[dependencies.basic-authorship]
-git = 'https://github.com/joystream/substrate.git'
+hex-literal = '0.2.1'
+derive_more = '0.14.0'
+exit-future = '0.1.4'
+futures = '0.1.29'
+log = '0.4.8'
+parking_lot = '0.9.0'
+tokio = '0.1.22'
+jsonrpc-core = '13.2.0'
+rand = '0.7.2'
+structopt = '=0.3.5'
+serde_json = '1.0'
+serde = '1.0'
+hex = '0.4'
+# https://users.rust-lang.org/t/failure-derive-compilation-error/39062
+# quote = '<=1.0.2'
+
+[dependencies.node-runtime]
+package = 'joystream-node-runtime'
+git = 'https://github.com/joystream/substrate-runtime-joystream'
+tag = 'v6.8.0'
+# rev = '620094ef5f393180284aab2e5516f854694f009b' # development branch: v6.8.0
+# branch = 'development'
+# local development...
+# path = '/Users/mokhtar/joystream/runtime'
+
+[dependencies.substrate-basic-authorship]
+git = 'https://github.com/paritytech/substrate.git'
 package = 'substrate-basic-authorship'
-rev = '6dfc3e8b057bb00322136251a0f10305fbb1ad8f'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
+
+[dependencies.babe]
+git = 'https://github.com/paritytech/substrate.git'
+package = 'substrate-consensus-babe'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
 
-[dependencies.consensus]
-git = 'https://github.com/joystream/substrate.git'
-package = 'substrate-consensus-aura'
-rev = '6dfc3e8b057bb00322136251a0f10305fbb1ad8f'
+[dependencies.babe-primitives]
+git = 'https://github.com/paritytech/substrate.git'
+package = 'substrate-consensus-babe-primitives'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
+
+[dependencies.codec]
+package = 'parity-scale-codec'
+version = '1.0.0'
 
 [dependencies.ctrlc]
 features = ['termination']
 version = '3.0'
 
 [dependencies.inherents]
-git = 'https://github.com/joystream/substrate.git'
+git = 'https://github.com/paritytech/substrate.git'
 package = 'substrate-inherents'
-rev = '6dfc3e8b057bb00322136251a0f10305fbb1ad8f'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
 
 [dependencies.network]
-git = 'https://github.com/joystream/substrate.git'
+git = 'https://github.com/paritytech/substrate.git'
 package = 'substrate-network'
-rev = '6dfc3e8b057bb00322136251a0f10305fbb1ad8f'
-
-[dependencies.joystream-node-runtime]
-# clone https://github.com/joystream/substrate-runtime-joystream to this path:
-path = 'substrate-runtime-joystream'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
 
 [dependencies.primitives]
-git = 'https://github.com/joystream/substrate.git'
+git = 'https://github.com/paritytech/substrate.git'
 package = 'substrate-primitives'
-rev = '6dfc3e8b057bb00322136251a0f10305fbb1ad8f'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
 
 [dependencies.sr-io]
-git = 'https://github.com/joystream/substrate.git'
-rev = '6dfc3e8b057bb00322136251a0f10305fbb1ad8f'
+git = 'https://github.com/paritytech/substrate.git'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
 
 [dependencies.substrate-cli]
-git = 'https://github.com/joystream/substrate.git'
-rev = '6dfc3e8b057bb00322136251a0f10305fbb1ad8f'
+git = 'https://github.com/paritytech/substrate.git'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
 
 [dependencies.substrate-client]
-git = 'https://github.com/joystream/substrate.git'
-rev = '6dfc3e8b057bb00322136251a0f10305fbb1ad8f'
+git = 'https://github.com/paritytech/substrate.git'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
 
 [dependencies.substrate-executor]
-git = 'https://github.com/joystream/substrate.git'
-rev = '6dfc3e8b057bb00322136251a0f10305fbb1ad8f'
+git = 'https://github.com/paritytech/substrate.git'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
 
 [dependencies.substrate-service]
-git = 'https://github.com/joystream/substrate.git'
-rev = '6dfc3e8b057bb00322136251a0f10305fbb1ad8f'
+git = 'https://github.com/paritytech/substrate.git'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
 
 [dependencies.transaction-pool]
-git = 'https://github.com/joystream/substrate.git'
+git = 'https://github.com/paritytech/substrate.git'
 package = 'substrate-transaction-pool'
-rev = '6dfc3e8b057bb00322136251a0f10305fbb1ad8f'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
 
 [dependencies.substrate-telemetry]
-git = 'https://github.com/joystream/substrate.git'
+git = 'https://github.com/paritytech/substrate.git'
 package = 'substrate-telemetry'
-rev = '6dfc3e8b057bb00322136251a0f10305fbb1ad8f'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
 
 [dependencies.grandpa]
-git = 'https://github.com/joystream/substrate.git'
+git = 'https://github.com/paritytech/substrate.git'
 package = 'substrate-finality-grandpa'
-rev = '6dfc3e8b057bb00322136251a0f10305fbb1ad8f'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
+
+[dependencies.grandpa-primitives]
+git = 'https://github.com/paritytech/substrate.git'
+package = 'substrate-finality-grandpa-primitives'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
+
+[dependencies.im-online]
+default_features = false
+git = 'https://github.com/paritytech/substrate.git'
+package = 'srml-im-online'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
+
+[dependencies.substrate-rpc]
+default_features = false
+git = 'https://github.com/paritytech/substrate.git'
+package = 'substrate-rpc'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
+
+[dependencies.authority-discovery]
+default_features = false
+git = 'https://github.com/paritytech/substrate.git'
+package = 'substrate-authority-discovery'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
+
+[dependencies.client-db]
+default_features = false
+git = 'https://github.com/paritytech/substrate.git'
+package = 'substrate-client-db'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
+
+[dependencies.runtime-primitives]
+default_features = false
+git = 'https://github.com/paritytech/substrate.git'
+package = 'sr-primitives'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
+
+[dependencies.offchain]
+default_features = false
+git = 'https://github.com/paritytech/substrate.git'
+package = 'substrate-offchain'
+rev = 'c37bb08535c49a12320af7facfd555ce05cce2e8'
+
+[dependencies.libp2p]
+version = '0.13.2'
+default-features = false
 
 [profile.release]
 panic = 'unwind'

+ 23 - 14
Dockerfile

@@ -1,17 +1,26 @@
-FROM liuchong/rustup:1.34.0 AS builder
-LABEL description="Joystream substrate node"
+FROM joystream/rust-builder AS builder
+LABEL description="compiles and caches dependencies, artifacts and node"
+WORKDIR /joystream
+COPY . /joystream
 
-WORKDIR /substrate-node-joystream
-COPY . /substrate-node-joystream
-ENV TERM=xterm
+RUN cargo build --release
 
-RUN apt-get update && apt-get install git clang -y \
-    && ./init-wasm.sh \
-    && git clone -b v5.3.0 https://github.com/Joystream/substrate-runtime-joystream.git \
-    && ./build-runtime.sh \
-    && cargo build --release \
-    && cargo install --path ./ \
-    && apt-get remove git clang -y \
-    && rm -rf /var/lib/apt/lists/*
+FROM debian:stretch
+LABEL description="Joystream node"
+WORKDIR /joystream
+COPY --from=builder /joystream/target/release/joystream-node /joystream/node
 
-ENTRYPOINT ["/root/.cargo/bin/joystream-node"]
+# confirm it works
+RUN /joystream/node --version
+
+EXPOSE 30333 9933 9944
+
+# Use these volumes to persits chain state and keystore, eg.:
+# --base-path /data
+# optionally separate keystore (otherwise it will be stored in the base path)
+# --keystore-path /keystore
+# if base-path isn't specified, chain state is stored inside container in ~/.local/share/joystream-node/
+# which is not ideal
+VOLUME ["/data", "/keystore"]
+
+ENTRYPOINT ["/joystream/node"]

+ 30 - 0
Dockerfile_experimental

@@ -0,0 +1,30 @@
+# syntax=docker/dockerfile:experimental
+# must enable experimental features in docker daemon and set DOCKER_BUILDKIT=1 env variable
+# https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md
+FROM joystream/rust-builder AS builder
+LABEL description="compiles and caches dependencies, artifacts and node"
+WORKDIR /joystream
+COPY . /joystream
+RUN mkdir /build-output
+
+RUN --mount=type=cache,target=/joystream/target \
+    --mount=type=cache,target=/root/.cargo/git \
+    --mount=type=cache,target=/root/.cargo/registry \
+    cargo build --release \
+    && cp ./target/release/joystream-node /build-output/joystream-node
+# copy in last part could be done with nightly option --out-dir
+
+FROM debian:stretch
+LABEL description="Joystream node"
+WORKDIR /joystream
+COPY --from=builder /build-output/joystream-node /joystream/node
+
+# Use these volumes to persits chain state and keystore, eg.:
+# --base-path /data
+# optionally separate keystore (otherwise it will be stored in the base path)
+# --keystore-path /keystore
+# if base-path isn't specified, chain state is stored inside container in ~/.local/share/joystream-node/
+# which is not ideal
+VOLUME ["/data", "/keystore"]
+
+ENTRYPOINT ["/joystream/node"]

+ 674 - 0
LICENSE

@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.

+ 38 - 18
README.md

@@ -13,37 +13,24 @@ Downloads are available in [releases](https://github.com/Joystream/substrate-nod
 ## Building from source
 
 ### Initial setup
-If you want to build from source you will need the Rust [toolchain](https://rustup.rs/), openssl and llvm/libclang.
+If you want to build from source you will need the Rust [toolchain](https://rustup.rs/), openssl and llvm/libclang. You can install the required dependencies with:
 
 ```bash
 git clone https://github.com/Joystream/substrate-node-joystream.git
-```
-
-Initialise the WASM build environment:
-
-```bash
 cd substrate-node-joystream/
-./init-wasm.sh
+./setup.sh
 ```
 
-### Building
-Clone the joystream runtime into the substrate-node-joystream directory, release version v5.3.0:
-
-```bash
-git clone -b v5.3.0 https://github.com/Joystream/substrate-runtime-joystream.git
-```
+If you prefer to use docker see [building with docker](Docker).
 
-Build the WASM runtime library:
-```bash
-./build-runtime.sh
-```
+### Building
 
-Build the node (native code):
 ```bash
 cargo build --release
 ```
 
 ### Running a public node
+
 Run the node and connect to the public testnet
 ```bash
 cargo run --release
@@ -77,3 +64,36 @@ When making changes to the runtime library remember to purge the chain after reb
 cargo run --release -- purge-chain --dev
 ```
 
+### Docker
+
+#### Building localy
+
+A joystream-node can be compiled with give [Dockerfile](Dockerfile) file:
+
+```bash
+# Build and tag a new image, which will compile joystream-node from source
+docker build . -t joystream-node
+
+# run a development chain with the image just created publishing the websocket port
+docker run -p 9944:9944 joystream-node --dev --ws-external
+```
+
+#### Downloading joystream pre-built images from Docker Hub
+
+```bash
+docker pull joystream/node
+```
+
+#### Running a public node as a service
+
+```bash
+docker run -d -p 30333:30333 --name my-node joystream/node
+
+# check status
+docker ps
+
+# monitor logs
+docker logs --tail 100 -f my-node
+```
+
+[More advanced guide]()

+ 0 - 1
build-clean-start.sh

@@ -1,6 +1,5 @@
 #!/usr/bin/env bash
 
-./build-runtime.sh
 cargo build
 cargo run -- purge-chain --dev
 cargo run -- --dev

+ 0 - 32
build-runtime.sh

@@ -1,32 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-
-PROJECT_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
-
-export CARGO_INCREMENTAL=0
-
-bold=$(tput bold)
-normal=$(tput sgr0)
-
-# Save current directory.
-pushd . >/dev/null
-
-RUNTIME_DIR="substrate-runtime-joystream"
-if [ ! -d "$RUNTIME_DIR" ]; then
-    echo "The '$RUNTIME_DIR' directory is missing. Did you git clone or symlink it yet?"
-    exit 1
-fi
-
-for SRC in "$RUNTIME_DIR/"
-do
-  echo "${bold}Building webassembly binary in $SRC...${normal}"
-  cd "$PROJECT_ROOT/$SRC"
-
-  ./build.sh
-
-  cd - >> /dev/null
-done
-
-# Restore initial directory.
-popd >/dev/null

+ 23 - 4
build.rs

@@ -1,8 +1,27 @@
-use vergen::{ConstantsFlags, generate_cargo_keys};
+use std::{env, path::PathBuf};
 
-const ERROR_MSG: &'static str = "Failed to generate metadata files";
+use vergen::{generate_cargo_keys, ConstantsFlags};
+
+const ERROR_MSG: &str = "Failed to generate metadata files";
 
 fn main() {
-	generate_cargo_keys(ConstantsFlags::all()).expect(ERROR_MSG);
-	println!("cargo:rerun-if-changed=.git/HEAD");
+    generate_cargo_keys(ConstantsFlags::SHA_SHORT).expect(ERROR_MSG);
+
+    let mut manifest_dir = PathBuf::from(
+        env::var("CARGO_MANIFEST_DIR").expect("`CARGO_MANIFEST_DIR` is always set by cargo."),
+    );
+
+    while manifest_dir.parent().is_some() {
+        if manifest_dir.join(".git/HEAD").exists() {
+            println!(
+                "cargo:rerun-if-changed={}",
+                manifest_dir.join(".git/HEAD").display()
+            );
+            return;
+        }
+
+        manifest_dir.pop();
+    }
+
+    println!("cargo:warning=Could not find `.git/HEAD` from manifest dir!");
 }

+ 13 - 0
raspberry-cross-build.sh

@@ -0,0 +1,13 @@
+#!/bin/sh
+
+### Cross build for Raspberry Pi - using docker ###
+docker pull joystream/rust-raspberry
+
+docker run \
+    --volume ${PWD}/:/home/cross/project \
+    --volume ${HOME}/.cargo/registry:/home/cross/.cargo/registry \
+    joystream/rust-raspberry \
+    build --release
+
+# output will be in project folder:
+# target/arm-unknown-linux-gnueabihf/joystream-node

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
res/acropolis_members.json


+ 1 - 0
res/dummy.json

@@ -0,0 +1 @@
+{}

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
res/forum_data_acropolis_encoded.json


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
res/forum_data_acropolis_serialized.json


+ 1 - 0
res/forum_data_empty.json

@@ -0,0 +1 @@
+{ "categories":[], "posts":[], "threads":[] }

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 77
res/joy_testnet_2.json


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 22 - 0
res/rome-experimental.json


+ 4 - 1
init-wasm.sh → setup.sh

@@ -2,6 +2,9 @@
 
 set -e
 
+# Install OS dependencies
+curl https://getsubstrate.io -sSf | bash -s -- --fast
+
 echo "*** Initialising WASM build environment"
 
 if [ -z $CI_PROJECT_NAME ] ; then
@@ -13,4 +16,4 @@ rustup target add wasm32-unknown-unknown --toolchain nightly
 
 # Install wasm-gc. It's useful for stripping slimming down wasm binaries.
 command -v wasm-gc || \
-	cargo +nightly install --git https://github.com/alexcrichton/wasm-gc --force
+	cargo +nightly install --git https://github.com/alexcrichton/wasm-gc --force

+ 401 - 261
src/chain_spec.rs

@@ -1,21 +1,44 @@
-use hex_literal::{hex, hex_impl};
-use joystream_node_runtime::{
-    AccountId, BalancesConfig, ConsensusConfig, CouncilConfig,
-    CouncilElectionConfig, DataObjectStorageRegistryConfig, DataObjectTypeRegistryConfig,
-    DownloadSessionsConfig, GenesisConfig, GrandpaConfig, IndicesConfig, MembersConfig, Perbill,
-    ProposalsConfig, SessionConfig, StakerStatus, StakingConfig, SudoConfig, TimestampConfig,
-	ActorsConfig,
+// Copyright 2019 Joystream Contributors
+// This file is part of Joystream node.
+
+// Joystream node is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Joystream node is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Joystream node.  If not, see <http://www.gnu.org/licenses/>.
+
+use hex_literal::hex;
+use node_runtime::{
+    versioned_store::InputValidationLengthConstraint as VsInputValidation, AccountId, ActorsConfig,
+    AuthorityDiscoveryConfig, BabeConfig, Balance, BalancesConfig, ContentWorkingGroupConfig,
+    CouncilConfig, CouncilElectionConfig, DataObjectStorageRegistryConfig,
+    DataObjectTypeRegistryConfig, GenesisConfig, GrandpaConfig, ImOnlineConfig, IndicesConfig,
+    MembersConfig, Perbill, ProposalsConfig, SessionConfig, SessionKeys, Signature, StakerStatus,
+    StakingConfig, SudoConfig, SystemConfig, VersionedStoreConfig, DAYS, WASM_BINARY,
 };
-use primitives::{crypto::UncheckedInto, ed25519, sr25519, Pair};
+use primitives::{crypto::UncheckedInto, sr25519, Pair, Public};
+use runtime_primitives::traits::{IdentifyAccount, Verify};
+
+use babe_primitives::AuthorityId as BabeId;
+use grandpa_primitives::AuthorityId as GrandpaId;
+use im_online::sr25519::AuthorityId as ImOnlineId;
+use serde_json as json;
 use substrate_service;
 use substrate_telemetry::TelemetryEndpoints;
 
-use ed25519::Public as AuthorityId;
+type AccountPublic = <Signature as Verify>::Signer;
 
 // Note this is the URL for the telemetry server
 const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
 
-/// Specialised `ChainSpec`. This is a specialisation of the general Substrate ChainSpec type.
+/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type.
 pub type ChainSpec = substrate_service::ChainSpec<GenesisConfig>;
 
 /// The chain specification option. This is expected to come in from the CLI and
@@ -33,16 +56,40 @@ pub enum Alternative {
     LiveTestnet,
 }
 
-fn authority_key(s: &str) -> AuthorityId {
-    ed25519::Pair::from_string(&format!("//{}", s), None)
+/// Helper function to generate a crypto pair from seed
+pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
+    TPublic::Pair::from_string(&format!("//{}", seed), None)
         .expect("static values are valid; qed")
         .public()
 }
 
-fn account_key(s: &str) -> AccountId {
-	sr25519::Pair::from_string(&format!("//{}", s), None)
-		.expect("static values are valid; qed")
-		.public()
+/// Helper function to generate an account ID from seed
+pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId
+where
+    AccountPublic: From<<TPublic::Pair as Pair>::Public>,
+{
+    AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()
+}
+
+/// Helper function to generate stash, controller and session key from seed
+pub fn get_authority_keys_from_seed(
+    seed: &str,
+) -> (AccountId, AccountId, GrandpaId, BabeId, ImOnlineId) {
+    (
+        get_account_id_from_seed::<sr25519::Public>(&format!("{}//stash", seed)),
+        get_account_id_from_seed::<sr25519::Public>(seed),
+        get_from_seed::<GrandpaId>(seed),
+        get_from_seed::<BabeId>(seed),
+        get_from_seed::<ImOnlineId>(seed),
+    )
+}
+
+fn session_keys(grandpa: GrandpaId, babe: BabeId, im_online: ImOnlineId) -> SessionKeys {
+    SessionKeys {
+        grandpa,
+        babe,
+        im_online,
+    }
 }
 
 impl Alternative {
@@ -54,26 +101,20 @@ impl Alternative {
                 "dev",
                 || {
                     testnet_genesis(
+                        vec![get_authority_keys_from_seed("Alice")],
+                        get_account_id_from_seed::<sr25519::Public>("Alice"),
                         vec![
-                            // stash, controller, authority
-                            (
-                                account_key("Alice//stash"),
-                                account_key("Alice"),
-                                authority_key("Alice"),
-                            ),
-                        ],
-                        vec![
-                            // endowed account
-                            account_key("Alice"),
+                            get_account_id_from_seed::<sr25519::Public>("Alice"),
+                            get_account_id_from_seed::<sr25519::Public>("Bob"),
+                            get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
+                            get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
                         ],
-                        // sudo key
-                        account_key("Alice"),
                     )
                 },
                 vec![],
                 None,
                 None,
-                None,
+                Some(chain_spec_properties()),
                 None,
             ),
             Alternative::LocalTestnet => ChainSpec::from_genesis(
@@ -82,32 +123,30 @@ impl Alternative {
                 || {
                     testnet_genesis(
                         vec![
-                            (
-                                account_key("Alice//stash"),
-                                account_key("Alice"),
-                                authority_key("Alice"),
-                            ),
-                            (
-                                account_key("Bob//stash"),
-                                account_key("Bob"),
-                                authority_key("Bob"),
-                            ),
+                            get_authority_keys_from_seed("Alice"),
+                            get_authority_keys_from_seed("Bob"),
                         ],
+                        get_account_id_from_seed::<sr25519::Public>("Alice"),
                         vec![
-                            account_key("Alice"),
-                            account_key("Bob"),
-                            account_key("Charlie"),
-                            account_key("Dave"),
-                            account_key("Eve"),
-                            account_key("Ferdie"),
+                            get_account_id_from_seed::<sr25519::Public>("Alice"),
+                            get_account_id_from_seed::<sr25519::Public>("Bob"),
+                            get_account_id_from_seed::<sr25519::Public>("Charlie"),
+                            get_account_id_from_seed::<sr25519::Public>("Dave"),
+                            get_account_id_from_seed::<sr25519::Public>("Eve"),
+                            get_account_id_from_seed::<sr25519::Public>("Ferdie"),
+                            get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
+                            get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
+                            get_account_id_from_seed::<sr25519::Public>("Charlie//stash"),
+                            get_account_id_from_seed::<sr25519::Public>("Dave//stash"),
+                            get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
+                            get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
                         ],
-                        account_key("Alice"),
                     )
                 },
                 vec![],
                 None,
                 None,
-                None,
+                Some(chain_spec_properties()),
                 None,
             ),
             Alternative::StagingTestnet => staging_testnet_config(),
@@ -120,251 +159,352 @@ impl Alternative {
             "dev" => Some(Alternative::Development),
             "local" => Some(Alternative::LocalTestnet),
             "staging" => Some(Alternative::StagingTestnet),
-            "" | "testnet" => Some(Alternative::LiveTestnet),
+            "rome-experimental" => Some(Alternative::LiveTestnet),
+            // "" | "tesnet" => Some(Alternative::LiveTestnet),
             _ => None,
         }
     }
 }
 
-/// LiveTestnet generator
+fn new_vs_validation(min: u16, max_min_diff: u16) -> VsInputValidation {
+    return VsInputValidation { min, max_min_diff };
+}
+
+/// Joystream LiveTestnet generator
 pub fn live_testnet_config() -> Result<ChainSpec, String> {
-    ChainSpec::from_embedded(include_bytes!("../res/joy_testnet_2.json"))
+    ChainSpec::from_json_bytes(&include_bytes!("../res/rome-experimental.json")[..])
+}
+
+pub fn chain_spec_properties() -> json::map::Map<String, json::Value> {
+    let mut properties: json::map::Map<String, json::Value> = json::map::Map::new();
+    properties.insert(
+        String::from("tokenDecimals"),
+        json::Value::Number(json::Number::from(0)),
+    );
+    properties.insert(
+        String::from("tokenSymbol"),
+        json::Value::String(String::from("JOY")),
+    );
+    properties
 }
 
 /// Staging testnet config
 pub fn staging_testnet_config() -> ChainSpec {
     let boot_nodes = vec![
-		String::from("/dns4/bootnode1.joystream.org/tcp/30333/p2p/QmeDa8jASqMRpTh4YCkeVEuHo6nbMcFDzD9pkUxTr3WxhM"),
-		String::from("/dns4/bootnode2.joystream.org/tcp/30333/p2p/QmbjzmNMjzQUMHpzqcPHW5DnFeUjM3x4hbiDSMkYv1McD3"),
-	];
+        String::from("/dns4/rome-reckless.joystream.org/tcp/30333/p2p/QmaTTdEF6YVCtynSjsXmGPSGcEesAahoZ8pmcCmmBwSE7S")
+    ];
+
     ChainSpec::from_genesis(
-        "Joystream Staging Testnet",
-        "joy_staging_5",
+        "Joystream Rome Reckless Testnet",
+        "joy_rome_reckless_N",
         staging_testnet_config_genesis,
         boot_nodes,
         Some(TelemetryEndpoints::new(vec![(
             STAGING_TELEMETRY_URL.to_string(),
             0,
         )])),
-        None,
-        None,
+        // protocol_id
+        Some(&*"/joy/rome/reckless/N"),
+        // Properties
+        Some(chain_spec_properties()),
+        // Extensions
         None,
     )
 }
 
 fn staging_testnet_config_genesis() -> GenesisConfig {
-    let initial_authorities: Vec<(AccountId, AccountId, AuthorityId)> = vec![(
-        hex!["0610d1a2b1d704723e588c842a934737491688b18b052baae1286f12e96adb65"].unchecked_into(), // stash
-        hex!["609cee3edd9900e69be44bcbf7a1892cad10408840a2d72d563811d72d9bb339"].unchecked_into(), // controller
-        hex!["65179fd9c39ec301457d1ee47a13f3bb0fef65812a57b6c93212e609b10d35d2"].unchecked_into(), // session key
+    let initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId)> = vec![(
+        hex!["4430a31121fc174b1c361b365580c54ef393813171e59542f5d2ce3d8b171a2d"].into(), // stash
+        hex!["58a743f1bab2f472fb99af98b6591e23a56fd84bc9c2a62037ed8867caae7c21"].into(), // controller
+        hex!["af5286fb1e403afd44d92ae3fb0b371a0f4f8faf3e6b2ff50ea91fb426b0015f"].unchecked_into(), // session - grandpa
+        hex!["d69529ed1549644977cec8dc027e71e1e2ae7aab99833a7f7dc08677a8d36307"].unchecked_into(), // session - babe
+        hex!["56bfd27715ce6c76e4d884c31374b9928391e461727ffaf27b94b6ce48570d39"].unchecked_into(), // session - im-online
     )];
-    let endowed_accounts = vec![hex![
-        "0ae55282e669fc55cb9529c0b12b989f2c5bf636d0de7630b5a4850055ed9c30"
-    ]
-    .unchecked_into()];
+    let endowed_accounts =
+        vec![hex!["00680fb81473784017291ef0afd968b986966daa7842d5b5063c8427c2b62577"].into()];
 
-    const CENTS: u128 = 1;
-    const DOLLARS: u128 = 100 * CENTS;
-
-    const SECS_PER_BLOCK: u64 = 6;
-    const MINUTES: u64 = 60 / SECS_PER_BLOCK;
-    const HOURS: u64 = MINUTES * 60;
-    const DAYS: u64 = HOURS * 24;
-    const STASH: u128 = 50 * DOLLARS;
-    const ENDOWMENT: u128 = 100_000_000 * DOLLARS;
+    const CENTS: Balance = 1;
+    const DOLLARS: Balance = 100 * CENTS;
+    const STASH: Balance = 20 * DOLLARS;
+    const ENDOWMENT: Balance = 100_000 * DOLLARS;
 
     GenesisConfig {
-		consensus: Some(ConsensusConfig {
-			code: include_bytes!("../substrate-runtime-joystream/wasm/target/wasm32-unknown-unknown/release/joystream_node_runtime_wasm.compact.wasm").to_vec(),
-			authorities: initial_authorities.iter().map(|x| x.2.clone()).collect(),
-		}),
-		system: None,
-		timestamp: Some(TimestampConfig {
-			minimum_period: SECS_PER_BLOCK / 2, // due to the nature of aura the slots are 2*period
-		}),
-		indices: Some(IndicesConfig {
-			ids: vec![],
-		}),
-		balances: Some(BalancesConfig {
-			balances: endowed_accounts.iter().cloned()
-				.map(|k| (k, ENDOWMENT))
-				.chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH)))
-				.collect(),
-			existential_deposit: 0,
-			transfer_fee: 0,
-			creation_fee: 0,
-			vesting: vec![],
-			transaction_base_fee: 1,
-			transaction_byte_fee: 0,
-		}),
-		sudo: Some(SudoConfig {
-			key: endowed_accounts[0].clone(),
-		}),
-		session: Some(SessionConfig {
-			validators: initial_authorities.iter().map(|x| x.1.clone()).collect(),
-			session_length: 10 * MINUTES,
-			keys: initial_authorities.iter().map(|x| (x.1.clone(), x.2.clone())).collect::<Vec<_>>(),
-		}),
-		staking: Some(StakingConfig {
-			current_era: 0,
-			offline_slash: Perbill::from_millionths(10_000),  // 1/ 100 => 1%
-			session_reward: Perbill::from_millionths(1_000),  // 1/1000 => 0.1% (min stake -> 1000 units for reward to be GT 0)
-			current_session_reward: 0,
-			validator_count: 20,
-			sessions_per_era: 6,
-			bonding_duration: 1, // Number of ERAs
-			offline_slash_grace: 4,
-			minimum_validator_count: 1,
-			stakers: initial_authorities.iter().map(|x| (x.0.clone(), x.1.clone(), STASH, StakerStatus::Validator)).collect(),
-			invulnerables: initial_authorities.iter().map(|x| x.1.clone()).collect(),
-		}),
-		grandpa: Some(GrandpaConfig {
-			authorities: initial_authorities.iter().map(|x| (x.2.clone(), 1)).collect(),
-		}),
-		council: Some(CouncilConfig {
-			active_council: vec![],
-			term_ends_at: 1,
-		}),
-		election: Some(CouncilElectionConfig {
-			auto_start: true,
-			announcing_period: 3 * DAYS,
-			voting_period: 1 * DAYS,
-			revealing_period: 1 * DAYS,
-			council_size: 12,
-			candidacy_limit: 25,
-			min_council_stake: 10 * DOLLARS,
-			new_term_duration: 14 * DAYS,
-			min_voting_stake: 1 * DOLLARS,
-		}),
-		proposals: Some(ProposalsConfig {
-			approval_quorum: 66,
-			min_stake: 2 * DOLLARS,
-			cancellation_fee: 10 * CENTS,
-			rejection_fee: 1 * DOLLARS,
-			voting_period: 2 * DAYS,
-			name_max_len: 512,
-			description_max_len: 10_000,
-			wasm_code_max_len: 2_000_000,
-		}),
-		members: Some(MembersConfig {
-			default_paid_membership_fee: 100u128,
-			first_member_id: 1,
-		}),
-		data_object_type_registry: Some(DataObjectTypeRegistryConfig {
-			first_data_object_type_id: 1,
-		}),
-		data_object_storage_registry: Some(DataObjectStorageRegistryConfig{
-			first_relationship_id: 1,
-		}),
-		downloads: Some(DownloadSessionsConfig{
-			first_download_session_id: 1,
-		}),
-		actors: Some(ActorsConfig{
-			enable_storage_role: true,
-			request_life_time: 300,
-			_genesis_phantom_data: Default::default(),
-		})
-	}
+        system: Some(SystemConfig {
+            code: WASM_BINARY.to_vec(),
+            changes_trie_config: Default::default(),
+        }),
+        balances: Some(BalancesConfig {
+            balances: endowed_accounts
+                .iter()
+                .cloned()
+                .map(|k| (k, ENDOWMENT))
+                .chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH)))
+                .collect(),
+            vesting: vec![],
+        }),
+        indices: Some(IndicesConfig { ids: vec![] }),
+        session: Some(SessionConfig {
+            keys: initial_authorities
+                .iter()
+                .map(|x| {
+                    (
+                        x.0.clone(),
+                        session_keys(x.2.clone(), x.3.clone(), x.4.clone()),
+                    )
+                })
+                .collect::<Vec<_>>(),
+        }),
+        staking: Some(StakingConfig {
+            current_era: 0,
+            validator_count: 20,
+            minimum_validator_count: 1,
+            stakers: initial_authorities
+                .iter()
+                .map(|x| (x.0.clone(), x.1.clone(), STASH, StakerStatus::Validator))
+                .collect(),
+            invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(),
+            slash_reward_fraction: Perbill::from_percent(10),
+            ..Default::default()
+        }),
+        sudo: Some(SudoConfig {
+            key: endowed_accounts[0].clone(),
+        }),
+        babe: Some(BabeConfig {
+            authorities: vec![],
+        }),
+        im_online: Some(ImOnlineConfig { keys: vec![] }),
+        authority_discovery: Some(AuthorityDiscoveryConfig { keys: vec![] }),
+        grandpa: Some(GrandpaConfig {
+            authorities: vec![],
+        }),
+        council: Some(CouncilConfig {
+            active_council: vec![],
+            term_ends_at: 1,
+        }),
+        election: Some(CouncilElectionConfig {
+            auto_start: true,
+            announcing_period: 3 * DAYS,
+            voting_period: 1 * DAYS,
+            revealing_period: 1 * DAYS,
+            council_size: 12,
+            candidacy_limit: 25,
+            min_council_stake: 10 * DOLLARS,
+            new_term_duration: 14 * DAYS,
+            min_voting_stake: 1 * DOLLARS,
+        }),
+        proposals: Some(ProposalsConfig {
+            approval_quorum: 66,
+            min_stake: 2 * DOLLARS,
+            cancellation_fee: 10 * CENTS,
+            rejection_fee: 1 * DOLLARS,
+            voting_period: 2 * DAYS,
+            name_max_len: 512,
+            description_max_len: 10_000,
+            wasm_code_max_len: 2_000_000,
+        }),
+        members: Some(MembersConfig {
+            default_paid_membership_fee: 100u128,
+            members: crate::members_config::initial_members(),
+        }),
+        forum: Some(crate::forum_config::from_serialized::create(
+            endowed_accounts[0].clone(),
+        )),
+        data_object_type_registry: Some(DataObjectTypeRegistryConfig {
+            first_data_object_type_id: 1,
+        }),
+        data_object_storage_registry: Some(DataObjectStorageRegistryConfig {
+            first_relationship_id: 1,
+        }),
+        actors: Some(ActorsConfig {
+            enable_storage_role: true,
+            request_life_time: 300,
+        }),
+        versioned_store: Some(VersionedStoreConfig {
+            class_by_id: vec![],
+            entity_by_id: vec![],
+            next_class_id: 1,
+            next_entity_id: 1,
+            property_name_constraint: new_vs_validation(1, 99),
+            property_description_constraint: new_vs_validation(1, 999),
+            class_name_constraint: new_vs_validation(1, 99),
+            class_description_constraint: new_vs_validation(1, 999),
+        }),
+        content_wg: Some(ContentWorkingGroupConfig {
+            mint_capacity: 100000,
+            curator_opening_by_id: vec![],
+            next_curator_opening_id: 0,
+            curator_application_by_id: vec![],
+            next_curator_application_id: 0,
+            channel_by_id: vec![],
+            next_channel_id: 1,
+            channel_id_by_handle: vec![],
+            curator_by_id: vec![],
+            next_curator_id: 0,
+            principal_by_id: vec![],
+            next_principal_id: 0,
+            channel_creation_enabled: true, // there is no extrinsic to change it so enabling at genesis
+            unstaker_by_stake_id: vec![],
+            channel_handle_constraint: crate::forum_config::new_validation(5, 20),
+            channel_description_constraint: crate::forum_config::new_validation(1, 1024),
+            opening_human_readable_text: crate::forum_config::new_validation(1, 2048),
+            curator_application_human_readable_text: crate::forum_config::new_validation(1, 2048),
+            curator_exit_rationale_text: crate::forum_config::new_validation(1, 2048),
+            channel_avatar_constraint: crate::forum_config::new_validation(5, 1024),
+            channel_banner_constraint: crate::forum_config::new_validation(5, 1024),
+            channel_title_constraint: crate::forum_config::new_validation(5, 1024),
+        }),
+    }
 }
 
-fn testnet_genesis(
-    initial_authorities: Vec<(AccountId, AccountId, AuthorityId)>,
-    endowed_accounts: Vec<AccountId>,
+/// Helper function to create GenesisConfig for testing
+pub fn testnet_genesis(
+    initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId)>,
     root_key: AccountId,
+    endowed_accounts: Vec<AccountId>,
 ) -> GenesisConfig {
-    const STASH: u128 = 100;
-    const ENDOWMENT: u128 = 100_000_000;
+    const STASH: Balance = 2000;
+    const ENDOWMENT: Balance = 10_000_000;
+
+    // Static members
+    let initial_members = crate::members_config::initial_members();
+
+    // let mut additional_members = vec![
+    //     // Make Root a member
+    //     (
+    //         root_key.clone(),
+    //         String::from("system"),
+    //         String::from("http://joystream.org/avatar.png"),
+    //         String::from("I am root!"),
+    //     ),
+    // ];
+
+    // // Additional members
+    // initial_members.append(&mut additional_members);
 
     GenesisConfig {
-		consensus: Some(ConsensusConfig {
-			code: include_bytes!("../substrate-runtime-joystream/wasm/target/wasm32-unknown-unknown/release/joystream_node_runtime_wasm.compact.wasm").to_vec(),
-			authorities: initial_authorities.iter().map(|x| x.2.clone()).collect(),
-		}),
-		system: None,
-		timestamp: Some(TimestampConfig {
-			minimum_period: 3,                    // 3*2=6 second block time.
-		}),
-		indices: Some(IndicesConfig {
-			ids: vec![]
-		}),
-		balances: Some(BalancesConfig {
-			existential_deposit: 0,
-			transfer_fee: 0,
-			creation_fee: 0,
-			balances: endowed_accounts.iter().cloned()
-				.map(|k| (k, ENDOWMENT))
-				.chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH)))
-				.collect(),
-			vesting: vec![],
-			transaction_base_fee: 1,
-			transaction_byte_fee: 0,
-		}),
-		sudo: Some(SudoConfig {
-			key: root_key,
-		}),
-		session: Some(SessionConfig {
-			validators: initial_authorities.iter().map(|x| x.1.clone()).collect(),
-			session_length: 10,
-			keys: initial_authorities.iter().map(|x| (x.1.clone(), x.2.clone())).collect::<Vec<_>>(),
-		}),
-		staking: Some(StakingConfig {
-			current_era: 0,
-			minimum_validator_count: 1,
-			validator_count: 2,
-			sessions_per_era: 5,
-			bonding_duration: 1, // Number of Eras
-			offline_slash: Perbill::zero(),
-			session_reward: Perbill::zero(),
-			current_session_reward: 0,
-			offline_slash_grace: 0,
-			stakers: initial_authorities.iter().map(|x| (x.0.clone(), x.1.clone(), STASH, StakerStatus::Validator)).collect(),
-			invulnerables: initial_authorities.iter().map(|x| x.1.clone()).collect(),
-		}),
-		grandpa: Some(GrandpaConfig {
-			authorities: initial_authorities.iter().map(|x| (x.2.clone(), 1)).collect(),
-		}),
-		council: Some(CouncilConfig {
-			active_council: vec![],
-			term_ends_at: 1,
-		}),
-		election: Some(CouncilElectionConfig {
-			auto_start: true,
-			announcing_period: 50,
-			voting_period: 50,
-			revealing_period: 50,
-			council_size: 2,
-			candidacy_limit: 25,
-			min_council_stake: 100,
-			new_term_duration: 1000,
-			min_voting_stake: 10,
-		}),
-		proposals: Some(ProposalsConfig {
-			approval_quorum: 66,
-			min_stake: 100,
-			cancellation_fee: 5,
-			rejection_fee: 10,
-			voting_period: 100,
-			name_max_len: 100,
-			description_max_len: 10_000,
-			wasm_code_max_len: 2_000_000,
-		}),
-		members: Some(MembersConfig {
-			default_paid_membership_fee: 100u128,
-			first_member_id: 1,
-		}),
-		data_object_type_registry: Some(DataObjectTypeRegistryConfig {
-			first_data_object_type_id: 1,
-		}),
-		data_object_storage_registry: Some(DataObjectStorageRegistryConfig{
-			first_relationship_id: 1,
-		}),
-		downloads: Some(DownloadSessionsConfig{
-			first_download_session_id: 1,
-		}),
-		actors: Some(ActorsConfig{
-			enable_storage_role: true,
-			request_life_time: 300,
-			_genesis_phantom_data: Default::default(),
-		})
-	}
+        //substrate modules
+        system: Some(SystemConfig {
+            code: WASM_BINARY.to_vec(),
+            changes_trie_config: Default::default(),
+        }),
+        balances: Some(BalancesConfig {
+            balances: endowed_accounts
+                .iter()
+                .map(|k| (k.clone(), ENDOWMENT))
+                .collect(),
+            vesting: vec![],
+        }),
+        indices: Some(IndicesConfig { ids: vec![] }),
+        staking: Some(StakingConfig {
+            current_era: 0,
+            minimum_validator_count: 1,
+            validator_count: 2,
+            stakers: initial_authorities
+                .iter()
+                .map(|x| (x.0.clone(), x.1.clone(), STASH, StakerStatus::Validator))
+                .collect(),
+            invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(),
+            slash_reward_fraction: Perbill::from_percent(10),
+            ..Default::default()
+        }),
+        session: Some(SessionConfig {
+            keys: initial_authorities
+                .iter()
+                .map(|x| {
+                    (
+                        x.0.clone(),
+                        session_keys(x.2.clone(), x.3.clone(), x.4.clone()),
+                    )
+                })
+                .collect::<Vec<_>>(),
+        }),
+        sudo: Some(SudoConfig {
+            key: root_key.clone(),
+        }),
+        babe: Some(BabeConfig {
+            authorities: vec![],
+        }),
+        im_online: Some(ImOnlineConfig { keys: vec![] }),
+        authority_discovery: Some(AuthorityDiscoveryConfig { keys: vec![] }),
+        grandpa: Some(GrandpaConfig {
+            authorities: vec![],
+        }),
+        // joystream modules
+        council: Some(CouncilConfig {
+            active_council: vec![],
+            term_ends_at: 1,
+        }),
+        election: Some(CouncilElectionConfig {
+            auto_start: true,
+            announcing_period: 50,
+            voting_period: 50,
+            revealing_period: 50,
+            council_size: 2,
+            candidacy_limit: 25,
+            min_council_stake: 100,
+            new_term_duration: 1000,
+            min_voting_stake: 10,
+        }),
+        proposals: Some(ProposalsConfig {
+            approval_quorum: 66,
+            min_stake: 100,
+            cancellation_fee: 5,
+            rejection_fee: 10,
+            voting_period: 100,
+            name_max_len: 100,
+            description_max_len: 10_000,
+            wasm_code_max_len: 2_000_000,
+        }),
+        members: Some(MembersConfig {
+            default_paid_membership_fee: 100u128,
+            members: initial_members,
+        }),
+        forum: Some(crate::forum_config::from_serialized::create(
+            root_key.clone(),
+        )),
+        data_object_type_registry: Some(DataObjectTypeRegistryConfig {
+            first_data_object_type_id: 1,
+        }),
+        data_object_storage_registry: Some(DataObjectStorageRegistryConfig {
+            first_relationship_id: 1,
+        }),
+        actors: Some(ActorsConfig {
+            enable_storage_role: true,
+            request_life_time: 300,
+        }),
+        versioned_store: Some(VersionedStoreConfig {
+            class_by_id: vec![],
+            entity_by_id: vec![],
+            next_class_id: 1,
+            next_entity_id: 1,
+            property_name_constraint: new_vs_validation(1, 99),
+            property_description_constraint: new_vs_validation(1, 999),
+            class_name_constraint: new_vs_validation(1, 99),
+            class_description_constraint: new_vs_validation(1, 999),
+        }),
+        content_wg: Some(ContentWorkingGroupConfig {
+            mint_capacity: 100000,
+            curator_opening_by_id: vec![],
+            next_curator_opening_id: 0,
+            curator_application_by_id: vec![],
+            next_curator_application_id: 0,
+            channel_by_id: vec![],
+            next_channel_id: 1,
+            channel_id_by_handle: vec![],
+            curator_by_id: vec![],
+            next_curator_id: 0,
+            principal_by_id: vec![],
+            next_principal_id: 0,
+            channel_creation_enabled: true, // there is no extrinsic to change it so enabling at genesis
+            unstaker_by_stake_id: vec![],
+            channel_handle_constraint: crate::forum_config::new_validation(5, 20),
+            channel_description_constraint: crate::forum_config::new_validation(1, 1024),
+            opening_human_readable_text: crate::forum_config::new_validation(1, 2048),
+            curator_application_human_readable_text: crate::forum_config::new_validation(1, 2048),
+            curator_exit_rationale_text: crate::forum_config::new_validation(1, 2048),
+            channel_avatar_constraint: crate::forum_config::new_validation(5, 1024),
+            channel_banner_constraint: crate::forum_config::new_validation(5, 1024),
+            channel_title_constraint: crate::forum_config::new_validation(5, 1024),
+        }),
+    }
 }

+ 73 - 49
src/cli.rs

@@ -1,12 +1,12 @@
 use crate::chain_spec;
+use crate::new_full_start;
 use crate::service;
 use futures::{future, sync::oneshot, Future};
 use log::info;
 use std::cell::RefCell;
-use std::ops::Deref;
 pub use substrate_cli::{error, IntoExit, VersionInfo};
-use substrate_cli::{informant, parse_and_execute, NoCustom};
-use substrate_service::{Roles as ServiceRoles, ServiceFactory};
+use substrate_cli::{informant, parse_and_prepare, NoCustom, ParseAndPrepare};
+use substrate_service::{AbstractService, Configuration, Roles as ServiceRoles};
 use tokio::runtime::Runtime;
 
 /// Parse command line arguments into service configuration.
@@ -16,39 +16,53 @@ where
     T: Into<std::ffi::OsString> + Clone,
     E: IntoExit,
 {
-    parse_and_execute::<service::Factory, NoCustom, NoCustom, _, _, _, _, _>(
-        load_spec,
-        &version,
-        "joystream-node",
-        args,
-        exit,
-        |exit, _custom_args, config| {
-            info!("{}", version.name);
-            info!("  version {}", config.full_version());
-            info!("  by {}, 2019", version.author);
-            info!("Chain specification: {}", config.chain_spec.name());
-            info!("Node name: {}", config.name);
-            info!("Roles: {:?}", config.roles);
-            let runtime = Runtime::new().map_err(|e| format!("{:?}", e))?;
-            let executor = runtime.executor();
-            match config.roles {
-                ServiceRoles::LIGHT => run_until_exit(
-                    runtime,
-                    service::Factory::new_light(config, executor)
-                        .map_err(|e| format!("{:?}", e))?,
-                    exit,
-                ),
-                _ => run_until_exit(
-                    runtime,
-                    service::Factory::new_full(config, executor).map_err(|e| format!("{:?}", e))?,
-                    exit,
-                ),
-            }
-            .map_err(|e| format!("{:?}", e))
-        },
-    )
-    .map_err(Into::into)
-    .map(|_| ())
+    type Config<T> = Configuration<(), T>;
+    match parse_and_prepare::<NoCustom, NoCustom, _>(&version, "joystream-node", args) {
+        ParseAndPrepare::Run(cmd) => cmd.run(
+            load_spec,
+            exit,
+            |exit, _cli_args, _custom_args, config: Config<_>| {
+                info!("{}", version.name);
+                info!("  version {}", config.full_version());
+                info!("  by {}, 2019", version.author);
+                info!("Chain specification: {}", config.chain_spec.name());
+                info!("Node name: {}", config.name);
+                info!("Roles: {:?}", config.roles);
+                let runtime = Runtime::new().map_err(|e| format!("{:?}", e))?;
+                match config.roles {
+                    ServiceRoles::LIGHT => run_until_exit(
+                        runtime,
+                        service::new_light(config).map_err(|e| format!("{:?}", e))?,
+                        exit,
+                    ),
+                    _ => run_until_exit(
+                        runtime,
+                        service::new_full(config).map_err(|e| format!("{:?}", e))?,
+                        exit,
+                    ),
+                }
+                .map_err(|e| format!("{:?}", e))
+            },
+        ),
+        ParseAndPrepare::BuildSpec(cmd) => cmd.run::<NoCustom, _, _, _>(load_spec),
+        ParseAndPrepare::ExportBlocks(cmd) => cmd.run_with_builder(
+            |config: Config<_>| Ok(new_full_start!(config).0),
+            load_spec,
+            exit,
+        ),
+        ParseAndPrepare::ImportBlocks(cmd) => cmd.run_with_builder(
+            |config: Config<_>| Ok(new_full_start!(config).0),
+            load_spec,
+            exit,
+        ),
+        ParseAndPrepare::PurgeChain(cmd) => cmd.run(load_spec),
+        ParseAndPrepare::RevertChain(cmd) => {
+            cmd.run_with_builder(|config: Config<_>| Ok(new_full_start!(config).0), load_spec)
+        }
+        ParseAndPrepare::CustomCommand(_) => Ok(()),
+    }?;
+
+    Ok(())
 }
 
 fn load_spec(id: &str) -> Result<Option<chain_spec::ChainSpec>, String> {
@@ -58,25 +72,35 @@ fn load_spec(id: &str) -> Result<Option<chain_spec::ChainSpec>, String> {
     })
 }
 
-fn run_until_exit<T, C, E>(mut runtime: Runtime, service: T, e: E) -> error::Result<()>
+fn run_until_exit<T, E>(mut runtime: Runtime, service: T, e: E) -> error::Result<()>
 where
-    T: Deref<Target = substrate_service::Service<C>>,
-    C: substrate_service::Components,
+    T: AbstractService,
     E: IntoExit,
 {
     let (exit_send, exit) = exit_future::signal();
 
-    let executor = runtime.executor();
-    informant::start(&service, exit.clone(), executor.clone());
-
-    let _ = runtime.block_on(e.into_exit());
-    exit_send.fire();
+    let informant = informant::build(&service);
+    runtime.executor().spawn(exit.until(informant).map(|_| ()));
 
     // we eagerly drop the service so that the internal exit future is fired,
     // but we need to keep holding a reference to the global telemetry guard
     let _telemetry = service.telemetry();
-    drop(service);
-    Ok(())
+
+    let service_res = {
+        let exit = e
+            .into_exit()
+            .map_err(|_| error::Error::Other("Exit future failed.".into()));
+        let service = service.map_err(|err| error::Error::Service(err));
+        let select = service.select(exit).map(|_| ()).map_err(|(err, _)| err);
+        runtime.block_on(select)
+    };
+
+    exit_send.fire();
+
+    // TODO [andre]: timeout this future #1318
+    let _ = runtime.shutdown_on_idle().wait();
+
+    service_res
 }
 
 // handles ctrl-c
@@ -89,11 +113,11 @@ impl IntoExit for Exit {
 
         let exit_send_cell = RefCell::new(Some(exit_send));
         ctrlc::set_handler(move || {
-            if let Some(exit_send) = exit_send_cell
+            let exit_send = exit_send_cell
                 .try_borrow_mut()
                 .expect("signal handler not reentrant; qed")
-                .take()
-            {
+                .take();
+            if let Some(exit_send) = exit_send {
                 exit_send.send(()).expect("Error sending exit notification");
             }
         })

+ 0 - 13
src/error.rs

@@ -1,13 +0,0 @@
-//! Initialization errors.
-
-use client;
-
-error_chain! {
-    foreign_links {
-        Io(::std::io::Error) #[doc="IO error"];
-        Cli(::clap::Error) #[doc="CLI error"];
-    }
-    links {
-        Client(client::error::Error, client::error::ErrorKind) #[doc="Client error"];
-    }
-}

+ 90 - 0
src/forum_config/from_encoded.rs

@@ -0,0 +1,90 @@
+// This module is not used but included as sample code
+// and highlights some pitfalls.
+
+use node_runtime::{
+    forum::{
+        Category, CategoryId, Post, PostId, Thread, ThreadId,
+    },
+    AccountId, BlockNumber, ForumConfig, Moment,
+};
+use serde::Deserialize;
+use serde_json::Result;
+use super::new_validation;
+
+use codec::Decode;
+
+#[derive(Deserialize)]
+struct ForumData {
+    /// hex encoded categories
+    categories: Vec<(CategoryId, String)>,
+    /// hex encoded posts
+    posts: Vec<(PostId, String)>,
+    /// hex encoded threads
+    threads: Vec<(ThreadId, String)>,
+}
+
+fn decode_post(encoded: String) -> Post<BlockNumber, Moment, AccountId> {
+    // hex string must not include '0x' prefix!
+    let encoded = hex::decode(encoded.as_bytes()).expect("failed to parse hex string");
+    Decode::decode(&mut encoded.as_slice()).unwrap()
+}
+
+fn decode_category(encoded: String) -> Category<BlockNumber, Moment, AccountId> {
+    // hex string must not include '0x' prefix!
+    let encoded = hex::decode(encoded.as_bytes()).expect("failed to parse hex string");
+    Decode::decode(&mut encoded.as_slice()).unwrap()
+}
+
+fn decode_thread(encoded: String) -> Thread<BlockNumber, Moment, AccountId> {
+    // hex string must not include '0x' prefix!
+    let encoded = hex::decode(encoded.as_bytes()).expect("failed to parse hex string");
+    Decode::decode(&mut encoded.as_slice()).unwrap()
+}
+
+fn parse_forum_json() -> Result<ForumData> {
+    let data = include_str!("../../res/forum_data_acropolis_encoded.json");
+    serde_json::from_str(data)
+}
+
+pub fn create(forum_sudo: AccountId) -> ForumConfig {
+    let forum_data = parse_forum_json().expect("failed loading forum data");
+
+    let next_category_id: CategoryId = forum_data
+        .categories
+        .last()
+        .map_or(1, |category| category.0 + 1);
+    let next_thread_id: ThreadId = forum_data.threads.last().map_or(1, |thread| thread.0 + 1);
+    let next_post_id: PostId = forum_data.posts.last().map_or(1, |post| post.0 + 1);
+
+    ForumConfig {
+        // Decoding will fail because of differnt type used for
+        // BlockNumber between Acropolis (u64) and Rome (u32)
+        // As long as types between chains are identical this approach works nicely
+        // since we don't need to use an intermediate format or do any transformation on source data.
+        category_by_id: forum_data
+            .categories
+            .into_iter()
+            .map(|category| (category.0, decode_category(category.1)))
+            .collect(),
+        thread_by_id: forum_data
+            .threads
+            .into_iter()
+            .map(|thread| (thread.0, decode_thread(thread.1)))
+            .collect(),
+        post_by_id: forum_data
+            .posts
+            .into_iter()
+            .map(|post| (post.0, decode_post(post.1)))
+            .collect(),
+        next_category_id,
+        next_thread_id,
+        next_post_id,
+        forum_sudo,
+        category_title_constraint: new_validation(10, 90),
+        category_description_constraint: new_validation(10, 490),
+        thread_title_constraint: new_validation(10, 90),
+        post_text_constraint: new_validation(10, 990),
+        thread_moderation_rationale_constraint: new_validation(10, 290),
+        post_moderation_rationale_constraint: new_validation(10, 290),
+    }
+}

+ 46 - 0
src/forum_config/from_serialized.rs

@@ -0,0 +1,46 @@
+use super::new_validation;
+use node_runtime::{
+    forum::{Category, CategoryId, Post, PostId, Thread, ThreadId},
+    AccountId, BlockNumber, ForumConfig, Moment,
+};
+use serde::Deserialize;
+use serde_json::Result;
+
+#[derive(Deserialize)]
+struct ForumData {
+    categories: Vec<(CategoryId, Category<BlockNumber, Moment, AccountId>)>,
+    posts: Vec<(PostId, Post<BlockNumber, Moment, AccountId>)>,
+    threads: Vec<(ThreadId, Thread<BlockNumber, Moment, AccountId>)>,
+}
+
+fn parse_forum_json() -> Result<ForumData> {
+    let data = include_str!("../../res/forum_data_acropolis_serialized.json");
+    serde_json::from_str(data)
+}
+
+pub fn create(forum_sudo: AccountId) -> ForumConfig {
+    let forum_data = parse_forum_json().expect("failed loading forum data");
+
+    let next_category_id: CategoryId = forum_data
+        .categories
+        .last()
+        .map_or(1, |category| category.0 + 1);
+    let next_thread_id: ThreadId = forum_data.threads.last().map_or(1, |thread| thread.0 + 1);
+    let next_post_id: PostId = forum_data.posts.last().map_or(1, |post| post.0 + 1);
+
+    ForumConfig {
+        category_by_id: forum_data.categories,
+        thread_by_id: forum_data.threads,
+        post_by_id: forum_data.posts,
+        next_category_id,
+        next_thread_id,
+        next_post_id,
+        forum_sudo,
+        category_title_constraint: new_validation(10, 90),
+        category_description_constraint: new_validation(10, 490),
+        thread_title_constraint: new_validation(10, 90),
+        post_text_constraint: new_validation(10, 990),
+        thread_moderation_rationale_constraint: new_validation(10, 290),
+        post_moderation_rationale_constraint: new_validation(10, 290),
+    }
+}

+ 10 - 0
src/forum_config/mod.rs

@@ -0,0 +1,10 @@
+pub mod from_serialized;
+
+// Not exported - only here as sample code
+// mod from_encoded;
+
+use node_runtime::forum::InputValidationLengthConstraint;
+
+pub fn new_validation(min: u16, max_min_diff: u16) -> InputValidationLengthConstraint {
+    return InputValidationLengthConstraint { min, max_min_diff };
+}

+ 24 - 4
src/main.rs

@@ -1,3 +1,19 @@
+// Copyright 2019 Joystream Contributors
+// This file is part of Joystream node.
+
+// Joystream node is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Joystream node is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Joystream node.  If not, see <http://www.gnu.org/licenses/>.
+
 //! Substrate Node Template CLI library.
 
 #![warn(missing_docs)]
@@ -5,11 +21,13 @@
 
 mod chain_spec;
 mod cli;
+mod forum_config;
+mod members_config;
 mod service;
 
 pub use substrate_cli::{error, IntoExit, VersionInfo};
 
-fn run() -> cli::error::Result<()> {
+fn main() {
     let version = VersionInfo {
         name: "Joystream Node",
         commit: env!("VERGEN_SHA_SHORT"),
@@ -19,7 +37,9 @@ fn run() -> cli::error::Result<()> {
         description: "Joystream substrate node",
         support_url: "https://www.joystream.org/",
     };
-    cli::run(::std::env::args(), cli::Exit, version)
-}
 
-error_chain::quick_main!(run);
+    if let Err(e) = cli::run(::std::env::args(), cli::Exit, version) {
+        eprintln!("Fatal error: {}\n\n{:?}", e, e);
+        std::process::exit(1)
+    }
+}

+ 50 - 0
src/members_config.rs

@@ -0,0 +1,50 @@
+use serde::Deserialize;
+use serde_json::Result;
+
+use primitives::crypto::{AccountId32, Ss58Codec};
+
+#[derive(Deserialize)]
+struct Member {
+    /// SS58 Encoded public key
+    address: String,
+    handle: String,
+    avatar_uri: String,
+    about: String,
+}
+
+// fn test_load_members() -> Result<Vec<Member>> {
+//     let data = r#"
+//         [{
+//             "address": "5Gn9n7SDJ7VgHqHQWYzkSA4vX6DCmS5TFWdHxikTXp9b4L32",
+//             "handle": "mokhtar",
+//             "avatar_uri": "http://mokhtar.net/avatar.png",
+//             "about": "Mokhtar"
+//         }]"#;
+
+//     serde_json::from_str(data)
+// }
+
+fn parse_members_json() -> Result<Vec<Member>> {
+    let data = include_str!("../res/acropolis_members.json");
+    serde_json::from_str(data)
+}
+
+pub fn decode_address(address: String) -> AccountId32 {
+    AccountId32::from_ss58check(address.as_ref()).expect("failed to decode account id")
+}
+
+pub fn initial_members() -> Vec<(AccountId32, String, String, String)> {
+    let members = parse_members_json().expect("failed parsing members data");
+
+    members
+        .into_iter()
+        .map(|member| {
+            (
+                decode_address(member.address),
+                member.handle,
+                member.avatar_uri,
+                member.about,
+            )
+        })
+        .collect()
+}

+ 323 - 145
src/service.rs

@@ -1,173 +1,351 @@
-//! Service and ServiceFactory implementation. Specialized wrapper over Substrate service.
+// Copyright 2019 Joystream Contributors
+// This file is part of Joystream node.
+
+// Joystream node is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Joystream node is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Joystream node.  If not, see <http://www.gnu.org/licenses/>.
 
 #![warn(unused_extern_crates)]
 
-use basic_authorship::ProposerFactory;
-use consensus::{import_queue, start_aura, AuraImportQueue, NothingExtra, SlotDuration};
-use grandpa;
+//! Service and ServiceFactory implementation. Specialized wrapper over substrate service.
+
+use client_db::Backend;
+use grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider};
 use inherents::InherentDataProviders;
-use joystream_node_runtime::{self, opaque::Block, GenesisConfig, RuntimeApi};
-use log::info;
-use network::construct_simple_protocol;
-use primitives::{ed25519::Pair, Pair as PairT, sr25519::Public as SrPublic, crypto::Ss58Codec};
+use network::{construct_simple_protocol, NetworkService};
+use node_runtime::{self, opaque::Block, GenesisConfig, RuntimeApi};
+use offchain::OffchainWorkers;
+use primitives::Blake2Hasher;
+use runtime_primitives::traits::Block as BlockT;
 use std::sync::Arc;
-use std::time::Duration;
-use substrate_client as client;
-use substrate_executor::native_executor_instance;
-use substrate_service::construct_service_factory;
+use substrate_client::{Client, LocalCallExecutor, LongestChain};
+pub use substrate_executor::{native_executor_instance, NativeExecutor};
 use substrate_service::{
-    FactoryFullConfiguration, FullBackend, FullClient, FullComponents, FullExecutor, LightBackend,
-    LightClient, LightComponents, LightExecutor, TaskExecutor,
+    error::Error as ServiceError, AbstractService, Configuration, NetworkStatus, Service,
+    ServiceBuilder,
 };
 use transaction_pool::{self, txpool::Pool as TransactionPool};
 
-// Get new prefixed ss58 address encoding for ed25519 public keys
-fn ed_ss58check(public_key: &primitives::ed25519::Public) -> String {
-    let raw_bytes: &[u8; 32] = public_key.as_ref();
-    // Interpret bytes as sr25519 public key
-    let v : SrPublic = SrPublic::from_raw(raw_bytes.clone());
-    v.to_ss58check()
+construct_simple_protocol! {
+    /// Demo protocol attachment for substrate.
+    pub struct NodeProtocol where Block = Block { }
 }
 
-pub use substrate_executor::NativeExecutor;
-// Our native executor instance.
+// Declare an instance of the native executor named `Executor`. Include the wasm binary as the
+// equivalent wasm code.
 native_executor_instance!(
 	pub Executor,
-	joystream_node_runtime::api::dispatch,
-	joystream_node_runtime::native_version,
-	include_bytes!("../substrate-runtime-joystream/wasm/target/wasm32-unknown-unknown/release/joystream_node_runtime_wasm.compact.wasm")
+	node_runtime::api::dispatch,
+	node_runtime::native_version
 );
 
-pub struct NodeConfig<F: substrate_service::ServiceFactory> {
-    pub grandpa_import_setup: Option<(
-        Arc<grandpa::BlockImportForService<F>>,
-        grandpa::LinkHalfForService<F>,
-    )>,
-    inherent_data_providers: InherentDataProviders,
+/// Starts a `ServiceBuilder` for a full service.
+///
+/// Use this macro if you don't actually need the full service, but just the builder in order to
+/// be able to perform chain operations.
+#[macro_export]
+macro_rules! new_full_start {
+    ($config:expr) => {{
+        // type RpcExtension = jsonrpc_core::IoHandler<substrate_rpc::Metadata>;
+        let mut import_setup = None;
+        let inherent_data_providers = inherents::InherentDataProviders::new();
+
+        let builder = substrate_service::ServiceBuilder::new_full::<
+            node_runtime::opaque::Block,
+            node_runtime::RuntimeApi,
+            crate::service::Executor,
+        >($config)?
+        .with_select_chain(|_config, backend| {
+            Ok(substrate_client::LongestChain::new(backend.clone()))
+        })?
+        .with_transaction_pool(|config, client| {
+            Ok(transaction_pool::txpool::Pool::new(
+                config,
+                transaction_pool::FullChainApi::new(client),
+            ))
+        })?
+        .with_import_queue(|_config, client, mut select_chain, _transaction_pool| {
+            let select_chain = select_chain
+                .take()
+                .ok_or_else(|| substrate_service::Error::SelectChainRequired)?;
+            let (grandpa_block_import, grandpa_link) =
+                grandpa::block_import::<_, _, _, node_runtime::RuntimeApi, _>(
+                    client.clone(),
+                    &*client,
+                    select_chain,
+                )?;
+            let justification_import = grandpa_block_import.clone();
+
+            let (block_import, babe_link) = babe::block_import(
+                babe::Config::get_or_compute(&*client)?,
+                grandpa_block_import,
+                client.clone(),
+                client.clone(),
+            )?;
+
+            let import_queue = babe::import_queue(
+                babe_link.clone(),
+                block_import.clone(),
+                Some(Box::new(justification_import)),
+                None,
+                client.clone(),
+                client,
+                inherent_data_providers.clone(),
+            )?;
+
+            import_setup = Some((block_import, grandpa_link, babe_link));
+            Ok(import_queue)
+        })?;
+        // We don't have any custom rpc commands...
+        // .with_rpc_extensions(|client, pool| -> RpcExtension {
+        // 	node_rpc::create(client, pool)
+        // })?;
+
+        (builder, import_setup, inherent_data_providers)
+    }};
 }
 
-impl<F> Default for NodeConfig<F>
-where
-    F: substrate_service::ServiceFactory,
-{
-    fn default() -> NodeConfig<F> {
-        NodeConfig {
-            grandpa_import_setup: None,
-            inherent_data_providers: InherentDataProviders::new(),
+/// Creates a full service from the configuration.
+///
+/// We need to use a macro because the test suit doesn't work with an opaque service. It expects
+/// concrete types instead.
+macro_rules! new_full {
+	($config:expr, $with_startup_data: expr) => {{
+		use futures::sync::mpsc;
+		use network::DhtEvent;
+
+		let (
+			is_authority,
+			force_authoring,
+			name,
+			disable_grandpa
+		) = (
+			$config.roles.is_authority(),
+			$config.force_authoring,
+			$config.name.clone(),
+			$config.disable_grandpa
+		);
+
+        // sentry nodes announce themselves as authorities to the network
+		// and should run the same protocols authorities do, but it should
+		// never actively participate in any consensus process.
+        let participates_in_consensus = is_authority && !$config.sentry_mode;
+
+		let (builder, mut import_setup, inherent_data_providers) = new_full_start!($config);
+
+		// Dht event channel from the network to the authority discovery module. Use bounded channel to ensure
+		// back-pressure. Authority discovery is triggering one event per authority within the current authority set.
+		// This estimates the authority set size to be somewhere below 10 000 thereby setting the channel buffer size to
+		// 10 000.
+		let (dht_event_tx, _dht_event_rx) =
+			mpsc::channel::<DhtEvent>(10_000);
+
+		let service = builder.with_network_protocol(|_| Ok(crate::service::NodeProtocol::new()))?
+			.with_finality_proof_provider(|client, backend|
+				Ok(Arc::new(grandpa::FinalityProofProvider::new(backend, client)) as _)
+			)?
+			.with_dht_event_tx(dht_event_tx)?
+			.build()?;
+
+		let (block_import, grandpa_link, babe_link) = import_setup.take()
+				.expect("Link Half and Block Import are present for Full Services or setup failed before. qed");
+
+		($with_startup_data)(&block_import, &babe_link);
+
+		if participates_in_consensus {
+			let proposer = substrate_basic_authorship::ProposerFactory {
+				client: service.client(),
+				transaction_pool: service.transaction_pool(),
+			};
+
+			let client = service.client();
+			let select_chain = service.select_chain()
+				.ok_or(substrate_service::Error::SelectChainRequired)?;
+
+			let babe_config = babe::BabeParams {
+				keystore: service.keystore(),
+				client,
+				select_chain,
+				env: proposer,
+				block_import,
+				sync_oracle: service.network(),
+				inherent_data_providers: inherent_data_providers.clone(),
+				force_authoring,
+				babe_link,
+			};
+
+			let babe = babe::start_babe(babe_config)?;
+			service.spawn_essential_task(babe);
         }
-    }
+
+        // if the node isn't actively participating in consensus then it doesn't
+		// need a keystore, regardless of which protocol we use below.
+		let keystore = if participates_in_consensus {
+			Some(service.keystore())
+		} else {
+			None
+        };
+
+        let config = grandpa::Config {
+            // FIXME #1578 make this available through chainspec
+            gossip_duration: std::time::Duration::from_millis(333),
+            justification_period: 512,
+            name: Some(name),
+            observer_enabled: true,
+            keystore,
+            is_authority,
+        };
+
+		match (is_authority, disable_grandpa) {
+			(false, false) => {
+				// start the lightweight GRANDPA observer
+				service.spawn_task(Box::new(grandpa::run_grandpa_observer(
+					config,
+					grandpa_link,
+					service.network(),
+					service.on_exit(),
+				)?));
+			},
+			(true, false) => {
+				// start the full GRANDPA voter
+				let grandpa_config = grandpa::GrandpaParams {
+					config: config,
+					link: grandpa_link,
+					network: service.network(),
+					inherent_data_providers: inherent_data_providers.clone(),
+					on_exit: service.on_exit(),
+					telemetry_on_connect: Some(service.telemetry_on_connect_stream()),
+					voting_rule: grandpa::VotingRulesBuilder::default().build(),
+                };
+                // the GRANDPA voter task is considered infallible, i.e.
+				// if it fails we take down the service with it.
+				service.spawn_essential_task(grandpa::run_grandpa_voter(grandpa_config)?);
+			},
+			(_, true) => {
+				grandpa::setup_disabled_grandpa(
+					service.client(),
+					&inherent_data_providers,
+					service.network(),
+				)?;
+			},
+		}
+
+		Ok((service, inherent_data_providers))
+	}};
+	($config:expr) => {{
+		new_full!($config, |_, _| {})
+	}}
 }
 
-construct_simple_protocol! {
-    /// Demo protocol attachment for substrate.
-    pub struct NodeProtocol where Block = Block { }
+#[allow(dead_code)]
+type ConcreteBlock = node_runtime::opaque::Block;
+#[allow(dead_code)]
+type ConcreteClient = Client<
+    Backend<ConcreteBlock>,
+    LocalCallExecutor<Backend<ConcreteBlock>, NativeExecutor<Executor>>,
+    ConcreteBlock,
+    node_runtime::RuntimeApi,
+>;
+#[allow(dead_code)]
+type ConcreteBackend = Backend<ConcreteBlock>;
+
+/// A specialized configuration object for setting up the node..
+pub type NodeConfiguration<C> =
+    Configuration<C, GenesisConfig /*, crate::chain_spec::Extensions*/>;
+
+/// Builds a new service for a full client.
+pub fn new_full<C: Send + Default + 'static>(config: NodeConfiguration<C>)
+-> Result<
+	Service<
+		ConcreteBlock,
+		ConcreteClient,
+		LongestChain<ConcreteBackend, ConcreteBlock>,
+		NetworkStatus<ConcreteBlock>,
+		NetworkService<ConcreteBlock, crate::service::NodeProtocol, <ConcreteBlock as BlockT>::Hash>,
+		TransactionPool<transaction_pool::FullChainApi<ConcreteClient, ConcreteBlock>>,
+		OffchainWorkers<
+			ConcreteClient,
+			<ConcreteBackend as substrate_client::backend::Backend<Block, Blake2Hasher>>::OffchainStorage,
+			ConcreteBlock,
+		>
+	>,
+	ServiceError,
+>
+{
+    new_full!(config).map(|(service, _)| service)
 }
 
-construct_service_factory! {
-    struct Factory {
-        Block = Block,
-        RuntimeApi = RuntimeApi,
-        NetworkProtocol = NodeProtocol { |config| Ok(NodeProtocol::new()) },
-        RuntimeDispatch = Executor,
-        FullTransactionPoolApi = transaction_pool::ChainApi<client::Client<FullBackend<Self>, FullExecutor<Self>, Block, RuntimeApi>, Block>
-            { |config, client| Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client))) },
-        LightTransactionPoolApi = transaction_pool::ChainApi<client::Client<LightBackend<Self>, LightExecutor<Self>, Block, RuntimeApi>, Block>
-            { |config, client| Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client))) },
-        Genesis = GenesisConfig,
-        Configuration = NodeConfig<Self>,
-        FullService = FullComponents<Self>
-            { |config: FactoryFullConfiguration<Self>, executor: TaskExecutor|
-                FullComponents::<Factory>::new(config, executor)
-            },
-        AuthoritySetup = {
-            |mut service: Self::FullService, executor: TaskExecutor, key: Option<Arc<Pair>>| {
-                let (block_import, link_half) = service.config.custom.grandpa_import_setup.take()
-                    .expect("Link Half and Block Import are present for Full Services or setup failed before. qed");
-
-                if let Some(ref key) = key {
-                    info!("Using authority key {}", ed_ss58check(&key.public()));
-                    let proposer = Arc::new(ProposerFactory {
-                        client: service.client(),
-                        transaction_pool: service.transaction_pool(),
-                        inherents_pool: service.inherents_pool(),
-                    });
-
-                    let client = service.client();
-                    executor.spawn(start_aura(
-                        SlotDuration::get_or_compute(&*client)?,
-                        key.clone(),
-                        client,
-                        block_import.clone(),
-                        proposer,
-                        service.network(),
-                        service.on_exit(),
-                        service.config.custom.inherent_data_providers.clone(),
-                        service.config.force_authoring,
-                    )?);
-
-                    info!("Running Grandpa session as Authority {}", ed_ss58check(&key.public()));
-                }
-
-                let key = if service.config.disable_grandpa {
-                    None
-                } else {
-                    key
-                };
+/// Builds a new service for a light client.
+pub fn new_light<C: Send + Default + 'static>(
+    config: NodeConfiguration<C>,
+) -> Result<impl AbstractService, ServiceError> {
+    // type RpcExtension = jsonrpc_core::IoHandler<substrate_rpc::Metadata>;
+    let inherent_data_providers = InherentDataProviders::new();
 
-                executor.spawn(grandpa::run_grandpa(
-                    grandpa::Config {
-                        local_key: key,
-                        // FIXME #1578 make this available through chainspec
-                        gossip_duration: Duration::from_millis(333),
-                        justification_period: 4096,
-                        name: Some(service.config.name.clone())
-                    },
-                    link_half,
-                    grandpa::NetworkBridge::new(service.network()),
-                    service.config.custom.inherent_data_providers.clone(),
-                    service.on_exit(),
-                )?);
-
-                Ok(service)
-            }
-        },
-        LightService = LightComponents<Self>
-            { |config, executor| <LightComponents<Factory>>::new(config, executor) },
-        FullImportQueue = AuraImportQueue<
-            Self::Block,
-        >
-            { |config: &mut FactoryFullConfiguration<Self> , client: Arc<FullClient<Self>>| {
-                let slot_duration = SlotDuration::get_or_compute(&*client)?;
-                let (block_import, link_half) =
-                    grandpa::block_import::<_, _, _, RuntimeApi, FullClient<Self>>(
-                        client.clone(), client.clone()
-                    )?;
-                let block_import = Arc::new(block_import);
-                let justification_import = block_import.clone();
-
-                config.custom.grandpa_import_setup = Some((block_import.clone(), link_half));
-
-                import_queue::<_, _, _, Pair>(
-                    slot_duration,
-                    block_import,
-                    Some(justification_import),
-                    client,
-                    NothingExtra,
-                    config.custom.inherent_data_providers.clone(),
-                ).map_err(Into::into)
-            }},
-        LightImportQueue = AuraImportQueue<
-            Self::Block,
-        >
-            { |config: &mut FactoryFullConfiguration<Self>, client: Arc<LightClient<Self>>|
-                import_queue::<_, _, _, Pair>(
-                    SlotDuration::get_or_compute(&*client)?,
+    let service = ServiceBuilder::new_light::<Block, RuntimeApi, Executor>(config)?
+        .with_select_chain(|_config, backend| Ok(LongestChain::new(backend.clone())))?
+        .with_transaction_pool(|config, client| {
+            Ok(TransactionPool::new(
+                config,
+                transaction_pool::FullChainApi::new(client),
+            ))
+        })?
+        .with_import_queue_and_fprb(
+            |_config, client, backend, fetcher, _select_chain, _tx_pool| {
+                let fetch_checker = fetcher
+                    .map(|fetcher| fetcher.checker().clone())
+                    .ok_or_else(|| {
+                        "Trying to start light import queue without active fetch checker"
+                    })?;
+                let grandpa_block_import = grandpa::light_block_import::<_, _, _, RuntimeApi>(
+                    client.clone(),
+                    backend,
+                    &*client,
+                    Arc::new(fetch_checker),
+                )?;
+
+                let finality_proof_import = grandpa_block_import.clone();
+                let finality_proof_request_builder =
+                    finality_proof_import.create_finality_proof_request_builder();
+
+                let (babe_block_import, babe_link) = babe::block_import(
+                    babe::Config::get_or_compute(&*client)?,
+                    grandpa_block_import,
                     client.clone(),
+                    client.clone(),
+                )?;
+
+                let import_queue = babe::import_queue(
+                    babe_link,
+                    babe_block_import,
                     None,
+                    Some(Box::new(finality_proof_import)),
+                    client.clone(),
                     client,
-                    NothingExtra,
-                    config.custom.inherent_data_providers.clone(),
-                ).map_err(Into::into)
+                    inherent_data_providers.clone(),
+                )?;
+
+                Ok((import_queue, finality_proof_request_builder))
             },
-    }
+        )?
+        .with_network_protocol(|_| Ok(NodeProtocol::new()))?
+        .with_finality_proof_provider(|client, backend| {
+            Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, client)) as _)
+        })?
+        // We don't have any custom rpc extensions
+        // .with_rpc_extensions(|client, pool| -> RpcExtension {
+        // 	node_rpc::create(client, pool)
+        // })?
+        .build()?;
+
+    Ok(service)
 }

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio