Merge branch 'master' into auth-bft
This commit is contained in:
commit
1692c07ba6
165
.gitlab-ci.yml
165
.gitlab-ci.yml
@ -31,10 +31,12 @@ linux-stable:
|
|||||||
- md5sum "parity_"$VER"_amd64.deb" > "parity_"$VER"_amd64.deb.md5"
|
- md5sum "parity_"$VER"_amd64.deb" > "parity_"$VER"_amd64.deb.md5"
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_access_key_id $s3_key
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
- aws configure set aws_secret_access_key $s3_secret
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity --body target/release/parity
|
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity.md5 --body parity.md5
|
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb" --body "parity_"$VER"_amd64.deb"
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity --body target/release/parity
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb.md5" --body "parity_"$VER"_amd64.deb.md5"
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/parity.md5 --body parity.md5
|
||||||
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb" --body "parity_"$VER"_amd64.deb"
|
||||||
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-unknown-linux-gnu/"parity_"$VER"_amd64.deb.md5" --body "parity_"$VER"_amd64.deb.md5"
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-stable
|
- rust-stable
|
||||||
@ -96,6 +98,8 @@ linux-centos:
|
|||||||
- md5sum target/release/parity > parity.md5
|
- md5sum target/release/parity > parity.md5
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_access_key_id $s3_key
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
- aws configure set aws_secret_access_key $s3_secret
|
||||||
|
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||||
|
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity --body target/release/parity
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity --body target/release/parity
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity.md5 --body parity.md5
|
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-unknown-centos-gnu/parity.md5 --body parity.md5
|
||||||
tags:
|
tags:
|
||||||
@ -126,10 +130,12 @@ linux-i686:
|
|||||||
- md5sum "parity_"$VER"_i386.deb" > "parity_"$VER"_i386.deb.md5"
|
- md5sum "parity_"$VER"_i386.deb" > "parity_"$VER"_i386.deb.md5"
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_access_key_id $s3_key
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
- aws configure set aws_secret_access_key $s3_secret
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/i686-unknown-linux-gnu/parity --body target/i686-unknown-linux-gnu/release/parity
|
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/i686-unknown-linux-gnu/parity.md5 --body parity.md5
|
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/i686-unknown-linux-gnu
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/i686-unknown-linux-gnu/"parity_"$VER"_i386.deb" --body "parity_"$VER"_i386.deb"
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/i686-unknown-linux-gnu/parity --body target/i686-unknown-linux-gnu/release/parity
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/i686-unknown-linux-gnu/"parity_"$VER"_i386.deb.md5" --body "parity_"$VER"_i386.deb.md5"
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/i686-unknown-linux-gnu/parity.md5 --body parity.md5
|
||||||
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/i686-unknown-linux-gnu/"parity_"$VER"_i386.deb" --body "parity_"$VER"_i386.deb"
|
||||||
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/i686-unknown-linux-gnu/"parity_"$VER"_i386.deb.md5" --body "parity_"$VER"_i386.deb.md5"
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-i686
|
- rust-i686
|
||||||
@ -166,10 +172,12 @@ linux-armv7:
|
|||||||
- md5sum "parity_"$VER"_armhf.deb" > "parity_"$VER"_armhf.deb.md5"
|
- md5sum "parity_"$VER"_armhf.deb" > "parity_"$VER"_armhf.deb.md5"
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_access_key_id $s3_key
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
- aws configure set aws_secret_access_key $s3_secret
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/parity --body target/armv7-unknown-linux-gnueabihf/release/parity
|
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/parity.md5 --body parity.md5
|
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb"
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/parity --body target/armv7-unknown-linux-gnueabihf/release/parity
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5"
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/parity.md5 --body parity.md5
|
||||||
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb"
|
||||||
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/armv7-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5"
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-arm
|
- rust-arm
|
||||||
@ -206,10 +214,12 @@ linux-arm:
|
|||||||
- md5sum "parity_"$VER"_armhf.deb" > "parity_"$VER"_armhf.deb.md5"
|
- md5sum "parity_"$VER"_armhf.deb" > "parity_"$VER"_armhf.deb.md5"
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_access_key_id $s3_key
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
- aws configure set aws_secret_access_key $s3_secret
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/parity --body target/arm-unknown-linux-gnueabihf/release/parity
|
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/parity.md5 --body parity.md5
|
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb"
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/parity --body target/arm-unknown-linux-gnueabihf/release/parity
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5"
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/parity.md5 --body parity.md5
|
||||||
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb" --body "parity_"$VER"_armhf.deb"
|
||||||
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabihf/"parity_"$VER"_armhf.deb.md5" --body "parity_"$VER"_armhf.deb.md5"
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-arm
|
- rust-arm
|
||||||
@ -241,8 +251,10 @@ linux-armv6:
|
|||||||
- md5sum target/arm-unknown-linux-gnueabi/release/parity > parity.md5
|
- md5sum target/arm-unknown-linux-gnueabi/release/parity > parity.md5
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_access_key_id $s3_key
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
- aws configure set aws_secret_access_key $s3_secret
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabi/parity --body target/arm-unknown-linux-gnueabi/release/parity
|
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabi/parity.md5 --body parity.md5
|
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/arm-unknown-linux-gnueabi
|
||||||
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabi/parity --body target/arm-unknown-linux-gnueabi/release/parity
|
||||||
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/arm-unknown-linux-gnueabi/parity.md5 --body parity.md5
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-arm
|
- rust-arm
|
||||||
@ -279,10 +291,12 @@ linux-aarch64:
|
|||||||
- md5sum "parity_"$VER"_arm64.deb" > "parity_"$VER"_arm64.deb.md5"
|
- md5sum "parity_"$VER"_arm64.deb" > "parity_"$VER"_arm64.deb.md5"
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_access_key_id $s3_key
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
- aws configure set aws_secret_access_key $s3_secret
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity --body target/aarch64-unknown-linux-gnu/release/parity
|
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity.md5 --body parity.md5
|
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/"parity_"$VER"_arm64.deb" --body "parity_"$VER"_arm64.deb"
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity --body target/aarch64-unknown-linux-gnu/release/parity
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/"parity_"$VER"_arm64.deb.md5" --body "parity_"$VER"_arm64.deb.md5"
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity.md5 --body parity.md5
|
||||||
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/"parity_"$VER"_arm64.deb" --body "parity_"$VER"_arm64.deb"
|
||||||
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/"parity_"$VER"_arm64.deb.md5" --body "parity_"$VER"_arm64.deb.md5"
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-arm
|
- rust-arm
|
||||||
@ -291,6 +305,41 @@ linux-aarch64:
|
|||||||
- target/aarch64-unknown-linux-gnu/release/parity
|
- target/aarch64-unknown-linux-gnu/release/parity
|
||||||
name: "aarch64-unknown-linux-gnu_parity"
|
name: "aarch64-unknown-linux-gnu_parity"
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
|
#linux-alpine:
|
||||||
|
# stage: build
|
||||||
|
# image: ethcore/rust-alpine:latest
|
||||||
|
# only:
|
||||||
|
# - beta
|
||||||
|
# - tags
|
||||||
|
# - stable
|
||||||
|
# - triggers
|
||||||
|
# script:
|
||||||
|
# - export HOST_CC=gcc
|
||||||
|
# - export HOST_CXX=g++
|
||||||
|
# - cargo build --release $CARGOFLAGS
|
||||||
|
# - strip target/release/parity
|
||||||
|
# - md5sum target/release/parity > parity.md5
|
||||||
|
# - sh scripts/deb-build.sh arm64
|
||||||
|
# - cp target/aarch64-unknown-linux-gnu/release/parity deb/usr/bin/parity
|
||||||
|
# - export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||||
|
# - dpkg-deb -b deb "parity_"$VER"_arm64.deb"
|
||||||
|
# - md5sum "parity_"$VER"_arm64.deb" > "parity_"$VER"_arm64.deb.md5"
|
||||||
|
# - aws configure set aws_access_key_id $s3_key
|
||||||
|
# - aws configure set aws_secret_access_key $s3_secret
|
||||||
|
# - if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||||
|
# - aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu
|
||||||
|
# - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity --body target/aarch64-unknown-linux-gnu/release/parity
|
||||||
|
# - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/parity.md5 --body parity.md5
|
||||||
|
# - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/"parity_"$VER"_arm64.deb" --body "parity_"$VER"_arm64.deb"
|
||||||
|
# - aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/aarch64-unknown-linux-gnu/"parity_"$VER"_arm64.deb.md5" --body "parity_"$VER"_arm64.deb.md5"
|
||||||
|
# tags:
|
||||||
|
# - rust
|
||||||
|
# - rust-alpine
|
||||||
|
# artifacts:
|
||||||
|
# paths:
|
||||||
|
# - target/aarch64-unknown-linux-gnu/release/parity
|
||||||
|
# name: "aarch64-unknown-linux-gnu_parity"
|
||||||
|
# allow_failure: true
|
||||||
darwin:
|
darwin:
|
||||||
stage: build
|
stage: build
|
||||||
only:
|
only:
|
||||||
@ -299,13 +348,22 @@ darwin:
|
|||||||
- stable
|
- stable
|
||||||
- triggers
|
- triggers
|
||||||
script:
|
script:
|
||||||
|
- cargo build --release -p ethstore $CARGOFLAGS
|
||||||
- cargo build --release $CARGOFLAGS
|
- cargo build --release $CARGOFLAGS
|
||||||
- rm -rf parity.md5
|
- rm -rf parity.md5
|
||||||
- md5sum target/release/parity > parity.md5
|
- md5sum target/release/parity > parity.md5
|
||||||
|
- packagesbuild -v mac/Parity.pkgproj
|
||||||
|
- export VER=$(grep -m 1 version Cargo.toml | awk '{print $3}' | tr -d '"' | tr -d "\n")
|
||||||
|
- mv target/release/Parity\ Ethereum.pkg "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg"
|
||||||
|
- md5sum "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg" >> "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5"
|
||||||
- aws configure set aws_access_key_id $s3_key
|
- aws configure set aws_access_key_id $s3_key
|
||||||
- aws configure set aws_secret_access_key $s3_secret
|
- aws configure set aws_secret_access_key $s3_secret
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/parity --body target/release/parity
|
- if [[ $CI_BUILD_REF_NAME =~ ^(master|beta|stable)$ ]]; then export S3_BUCKET=builds-parity-published; else export S3_BUCKET=builds-parity; fi
|
||||||
- aws s3api put-object --bucket builds-parity --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/parity.md5 --body parity.md5
|
- aws s3 rm --recursive s3://$S3_BUCKET/$CI_BUILD_REF_NAME/x86_64-apple-darwin
|
||||||
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/parity --body target/release/parity
|
||||||
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/parity.md5 --body parity.md5
|
||||||
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/"parity-"$VER"-osx-installer-EXPERIMENTAL.pkg" --body "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg"
|
||||||
|
- aws s3api put-object --bucket $S3_BUCKET --key $CI_BUILD_REF_NAME/x86_64-apple-darwin/"parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5" --body "parity-"$VER"-osx-installer-EXPERIMENTAL.pkg.md5"
|
||||||
tags:
|
tags:
|
||||||
- osx
|
- osx
|
||||||
artifacts:
|
artifacts:
|
||||||
@ -349,14 +407,20 @@ windows:
|
|||||||
- cd ..\..
|
- cd ..\..
|
||||||
- aws configure set aws_access_key_id %s3_key%
|
- aws configure set aws_access_key_id %s3_key%
|
||||||
- aws configure set aws_secret_access_key %s3_secret%
|
- aws configure set aws_secret_access_key %s3_secret%
|
||||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.exe --body target\release\parity.exe
|
- echo %CI_BUILD_REF_NAME%
|
||||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.exe.md5 --body target\release\parity.exe.md5
|
- echo %CI_BUILD_REF_NAME% | findstr /R "master" >nul 2>&1 && set S3_BUCKET=builds-parity-published || set S3_BUCKET=builds-parity
|
||||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.zip --body target\release\parity.zip
|
- echo %CI_BUILD_REF_NAME% | findstr /R "beta" >nul 2>&1 && set S3_BUCKET=builds-parity-published || set S3_BUCKET=builds-parity
|
||||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.zip.md5 --body target\release\parity.zip.md5
|
- echo %CI_BUILD_REF_NAME% | findstr /R "stable" >nul 2>&1 && set S3_BUCKET=builds-parity-published || set S3_BUCKET=builds-parity
|
||||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/InstallParity.exe --body nsis\InstallParity.exe
|
- echo %S3_BUCKET%
|
||||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/InstallParity.exe.md5 --body nsis\InstallParity.exe.md5
|
- aws s3 rm --recursive s3://%S3_BUCKET%/%CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc
|
||||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/win-installer.zip --body nsis\win-installer.zip
|
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.exe --body target\release\parity.exe
|
||||||
- aws s3api put-object --bucket builds-parity --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/win-installer.zip.md5 --body nsis\win-installer.zip.md5
|
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.exe.md5 --body target\release\parity.exe.md5
|
||||||
|
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.zip --body target\release\parity.zip
|
||||||
|
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/parity.zip.md5 --body target\release\parity.zip.md5
|
||||||
|
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/InstallParity.exe --body nsis\InstallParity.exe
|
||||||
|
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/InstallParity.exe.md5 --body nsis\InstallParity.exe.md5
|
||||||
|
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/win-installer.zip --body nsis\win-installer.zip
|
||||||
|
- aws s3api put-object --bucket %S3_BUCKET% --key %CI_BUILD_REF_NAME%/x86_64-pc-windows-msvc/win-installer.zip.md5 --body nsis\win-installer.zip.md5
|
||||||
tags:
|
tags:
|
||||||
- rust-windows
|
- rust-windows
|
||||||
artifacts:
|
artifacts:
|
||||||
@ -394,13 +458,13 @@ test-rust-stable:
|
|||||||
image: ethcore/rust:stable
|
image: ethcore/rust:stable
|
||||||
before_script:
|
before_script:
|
||||||
- git submodule update --init --recursive
|
- git submodule update --init --recursive
|
||||||
- export JS_FILES_MODIFIED=$(git --no-pager diff --name-only CI_BUILD_REF CI_BUILD_REF@{1} | grep \.js | wc -l)
|
- export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep \.js | wc -l)
|
||||||
- echo $JS_FILES_MODIFIED
|
- echo $JS_FILES_MODIFIED
|
||||||
- if [ -z $JS_FILES_MODIFIED ]; then echo "skip js test"; fi
|
- if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js test"; else ./js/scripts/install-deps.sh;fi
|
||||||
script:
|
script:
|
||||||
- export RUST_BACKTRACE=1
|
- export RUST_BACKTRACE=1
|
||||||
- echo $JS_FILES_MODIFIED
|
- echo $JS_FILES_MODIFIED
|
||||||
- if [ -z $JS_FILES_MODIFIED ]; then echo "skip js test"; else ./test.sh $CARGOFLAGS --no-release; fi
|
- if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js test"&./test.sh $CARGOFLAGS --no-release; else echo "skip rust test"&./js/scripts/lint.sh&./js/scripts/test.sh&./js/scripts/build.sh; fi
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-stable
|
- rust-stable
|
||||||
@ -411,13 +475,13 @@ test-rust-beta:
|
|||||||
image: ethcore/rust:beta
|
image: ethcore/rust:beta
|
||||||
before_script:
|
before_script:
|
||||||
- git submodule update --init --recursive
|
- git submodule update --init --recursive
|
||||||
- export JS_FILES_MODIFIED=$(git --no-pager diff --name-only CI_BUILD_REF CI_BUILD_REF@{1} | grep \.js | wc -l)
|
- export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep \.js | wc -l)
|
||||||
- echo $JS_FILES_MODIFIED
|
- echo $JS_FILES_MODIFIED
|
||||||
- if [ -z $JS_FILES_MODIFIED ]; then echo "skip js test"; fi
|
- if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js test"; else ./js/scripts/install-deps.sh;fi
|
||||||
script:
|
script:
|
||||||
- export RUST_BACKTRACE=1
|
- export RUST_BACKTRACE=1
|
||||||
- echo $JS_FILES_MODIFIED
|
- echo $JS_FILES_MODIFIED
|
||||||
- if [ -z $JS_FILES_MODIFIED ]; then echo "skip js test"; else ./test.sh $CARGOFLAGS --no-release; fi
|
- if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js test"&./test.sh $CARGOFLAGS --no-release; else echo "skip rust test"&./js/scripts/lint.sh&./js/scripts/test.sh&./js/scripts/build.sh; fi
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-beta
|
- rust-beta
|
||||||
@ -429,28 +493,17 @@ test-rust-nightly:
|
|||||||
image: ethcore/rust:nightly
|
image: ethcore/rust:nightly
|
||||||
before_script:
|
before_script:
|
||||||
- git submodule update --init --recursive
|
- git submodule update --init --recursive
|
||||||
- export JS_FILES_MODIFIED=$(git --no-pager diff --name-only CI_BUILD_REF CI_BUILD_REF@{1} | grep \.js | wc -l)
|
- export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep \.js | wc -l)
|
||||||
- echo $JS_FILES_MODIFIED
|
- echo $JS_FILES_MODIFIED
|
||||||
- if [ -z $JS_FILES_MODIFIED ]; then echo "skip js test"; fi
|
- if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js test"; else ./js/scripts/install-deps.sh;fi
|
||||||
script:
|
script:
|
||||||
- export RUST_BACKTRACE=1
|
- export RUST_BACKTRACE=1
|
||||||
- echo $JS_FILES_MODIFIED
|
- echo $JS_FILES_MODIFIED
|
||||||
- if [ -z $JS_FILES_MODIFIED ]; then echo "skip js test"; else ./test.sh $CARGOFLAGS --no-release; fi
|
- if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js test"&./test.sh $CARGOFLAGS --no-release; else echo "skip rust test"&./js/scripts/lint.sh&./js/scripts/test.sh&./js/scripts/build.sh; fi
|
||||||
tags:
|
tags:
|
||||||
- rust
|
- rust
|
||||||
- rust-nightly
|
- rust-nightly
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
js-tests:
|
|
||||||
stage: test
|
|
||||||
image: ethcore/rust:stable
|
|
||||||
before_script:
|
|
||||||
- ./js/scripts/install-deps.sh
|
|
||||||
script:
|
|
||||||
- ./js/scripts/lint.sh
|
|
||||||
- ./js/scripts/test.sh
|
|
||||||
- ./js/scripts/build.sh
|
|
||||||
tags:
|
|
||||||
- javascript-test
|
|
||||||
js-release:
|
js-release:
|
||||||
stage: js-build
|
stage: js-build
|
||||||
only:
|
only:
|
||||||
@ -459,9 +512,11 @@ js-release:
|
|||||||
- stable
|
- stable
|
||||||
image: ethcore/rust:stable
|
image: ethcore/rust:stable
|
||||||
before_script:
|
before_script:
|
||||||
- if [[ $NIGHTLY != "master" ]]; then ./js/scripts/install-deps.sh; fi
|
- export JS_FILES_MODIFIED=$(git --no-pager diff --name-only $CI_BUILD_REF^ $CI_BUILD_REF | grep \.js | wc -l)
|
||||||
|
- echo $JS_FILES_MODIFIED
|
||||||
|
- if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js build"; else ./js/scripts/install-deps.sh;fi
|
||||||
script:
|
script:
|
||||||
- if [[ $NIGHTLY != "master" ]]; then ./js/scripts/build.sh; fi
|
- echo $JS_FILES_MODIFIED
|
||||||
- if [[ $NIGHTLY != "master" ]]; then ./js/scripts/release.sh; fi
|
- if [ "$JS_FILES_MODIFIED" = 0 ]; then echo "skip js build"; else ./js/scripts/build.sh&./js/scripts/release.sh; fi
|
||||||
tags:
|
tags:
|
||||||
- javascript
|
- javascript
|
||||||
|
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -385,7 +385,7 @@ version = "1.5.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"mio 0.6.1 (git+https://github.com/carllerche/mio)",
|
"mio 0.6.1 (git+https://github.com/ethcore/mio)",
|
||||||
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@ -475,7 +475,7 @@ dependencies = [
|
|||||||
"igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"mio 0.6.1 (git+https://github.com/carllerche/mio)",
|
"mio 0.6.1 (git+https://github.com/ethcore/mio)",
|
||||||
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rlp 0.1.0",
|
"rlp 0.1.0",
|
||||||
@ -1026,7 +1026,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
source = "git+https://github.com/carllerche/mio#56f8663510196fdca04bdf7c5f4d60b24297826f"
|
source = "git+https://github.com/ethcore/mio#ef182bae193a9c7457cd2cf661fcaffb226e3eef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1263,7 +1263,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-ui-precompiled"
|
name = "parity-ui-precompiled"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "git+https://github.com/ethcore/js-precompiled.git#587684374a12bf715151dd987a552a3d61e42972"
|
source = "git+https://github.com/ethcore/js-precompiled.git#f1de5e5612d8237143b37aebf237a49475c2c4e6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@ -2053,7 +2053,7 @@ dependencies = [
|
|||||||
"checksum mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)" = "<none>"
|
"checksum mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)" = "<none>"
|
||||||
"checksum mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a637d1ca14eacae06296a008fa7ad955347e34efcb5891cfd8ba05491a37907e"
|
"checksum mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a637d1ca14eacae06296a008fa7ad955347e34efcb5891cfd8ba05491a37907e"
|
||||||
"checksum mio 0.6.0-dev (git+https://github.com/ethcore/mio?branch=timer-fix)" = "<none>"
|
"checksum mio 0.6.0-dev (git+https://github.com/ethcore/mio?branch=timer-fix)" = "<none>"
|
||||||
"checksum mio 0.6.1 (git+https://github.com/carllerche/mio)" = "<none>"
|
"checksum mio 0.6.1 (git+https://github.com/ethcore/mio)" = "<none>"
|
||||||
"checksum miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d5bfc6782530ac8ace97af10a540054a37126b63b0702ddaaa243b73b5745b9a"
|
"checksum miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d5bfc6782530ac8ace97af10a540054a37126b63b0702ddaaa243b73b5745b9a"
|
||||||
"checksum msdos_time 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c04b68cc63a8480fb2550343695f7be72effdec953a9d4508161c3e69041c7d8"
|
"checksum msdos_time 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c04b68cc63a8480fb2550343695f7be72effdec953a9d4508161c3e69041c7d8"
|
||||||
"checksum nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)" = "<none>"
|
"checksum nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)" = "<none>"
|
||||||
|
@ -21,8 +21,8 @@
|
|||||||
"genesis": {
|
"genesis": {
|
||||||
"seal": {
|
"seal": {
|
||||||
"generic": {
|
"generic": {
|
||||||
"fields": 1,
|
"fields": 2,
|
||||||
"rlp": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"
|
"rlp": "0x200"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"difficulty": "0x20000",
|
"difficulty": "0x20000",
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"difficultyBoundDivisor": "0x0800",
|
"difficultyBoundDivisor": "0x0800",
|
||||||
"durationLimit": "0x0d",
|
"durationLimit": "0x0d",
|
||||||
"blockReward": "0x4563918244F40000",
|
"blockReward": "0x4563918244F40000",
|
||||||
"registrar": "0x52dff57a8a1532e6afb3dc07e2af58bb9eb05b3d",
|
"registrar": "0x81a4b044831c4f12ba601adb9274516939e9b8a2",
|
||||||
"homesteadTransition": 0,
|
"homesteadTransition": 0,
|
||||||
"eip150Transition": 0,
|
"eip150Transition": 0,
|
||||||
"eip155Transition": 10,
|
"eip155Transition": 10,
|
||||||
|
@ -11,9 +11,9 @@
|
|||||||
},
|
},
|
||||||
"genesis": {
|
"genesis": {
|
||||||
"seal": {
|
"seal": {
|
||||||
"ethereum": {
|
"generic": {
|
||||||
"nonce": "0x00006d6f7264656e",
|
"fields": 0,
|
||||||
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578"
|
"rlp": "0x0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"difficulty": "0x20000",
|
"difficulty": "0x20000",
|
||||||
|
@ -276,14 +276,20 @@ impl AccountProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the password for `account` is `password`. `false` if not.
|
/// Returns `true` if the password for `account` is `password`. `false` if not.
|
||||||
pub fn test_password(&self, account: &Address, password: String) -> Result<bool, Error> {
|
pub fn test_password(&self, account: &Address, password: &str) -> Result<bool, Error> {
|
||||||
match self.sstore.sign(account, &password, &Default::default()) {
|
match self.sstore.sign(account, password, &Default::default()) {
|
||||||
Ok(_) => Ok(true),
|
Ok(_) => Ok(true),
|
||||||
Err(SSError::InvalidPassword) => Ok(false),
|
Err(SSError::InvalidPassword) => Ok(false),
|
||||||
Err(e) => Err(Error::SStore(e)),
|
Err(e) => Err(Error::SStore(e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Permanently removes an account.
|
||||||
|
pub fn kill_account(&self, account: &Address, password: &str) -> Result<(), Error> {
|
||||||
|
try!(self.sstore.remove_account(account, &password));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Changes the password of `account` from `password` to `new_password`. Fails if incorrect `password` given.
|
/// Changes the password of `account` from `password` to `new_password`. Fails if incorrect `password` given.
|
||||||
pub fn change_password(&self, account: &Address, password: String, new_password: String) -> Result<(), Error> {
|
pub fn change_password(&self, account: &Address, password: String, new_password: String) -> Result<(), Error> {
|
||||||
self.sstore.change_password(account, &password, &new_password).map_err(Error::SStore)
|
self.sstore.change_password(account, &password, &new_password).map_err(Error::SStore)
|
||||||
|
@ -141,7 +141,7 @@ pub trait BlockProvider {
|
|||||||
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockNumber, to_block: BlockNumber) -> Vec<BlockNumber>;
|
fn blocks_with_bloom(&self, bloom: &H2048, from_block: BlockNumber, to_block: BlockNumber) -> Vec<BlockNumber>;
|
||||||
|
|
||||||
/// Returns logs matching given filter.
|
/// Returns logs matching given filter.
|
||||||
fn logs<F>(&self, mut blocks: Vec<BlockNumber>, matches: F, limit: Option<usize>) -> Vec<LocalizedLogEntry>
|
fn logs<F>(&self, blocks: Vec<BlockNumber>, matches: F, limit: Option<usize>) -> Vec<LocalizedLogEntry>
|
||||||
where F: Fn(&LogEntry) -> bool, Self: Sized;
|
where F: Fn(&LogEntry) -> bool, Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,8 +256,8 @@ impl Engine for AuthorityRound {
|
|||||||
|
|
||||||
/// Check if the signature belongs to the correct proposer.
|
/// Check if the signature belongs to the correct proposer.
|
||||||
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
fn verify_block_unordered(&self, header: &Header, _block: Option<&[u8]>) -> Result<(), Error> {
|
||||||
let header_step = try!(header_step(header));
|
let header_step = try!(header_step(header));
|
||||||
// Give one step slack if step is lagging, double vote is still not possible.
|
// Give one step slack if step is lagging, double vote is still not possible.
|
||||||
if header_step <= self.step() + 1 {
|
if header_step <= self.step() + 1 {
|
||||||
let proposer_signature = try!(header_signature(header));
|
let proposer_signature = try!(header_signature(header));
|
||||||
let ok_sig = try!(verify_address(self.step_proposer(header_step), &proposer_signature, &header.bare_hash()));
|
let ok_sig = try!(verify_address(self.step_proposer(header_step), &proposer_signature, &header.bare_hash()));
|
||||||
@ -423,13 +423,13 @@ mod tests {
|
|||||||
let engine = Spec::new_test_round().engine;
|
let engine = Spec::new_test_round().engine;
|
||||||
|
|
||||||
let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
|
let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
|
||||||
let mut step = UNIX_EPOCH.elapsed().unwrap().as_secs();
|
let time = UNIX_EPOCH.elapsed().unwrap().as_secs();
|
||||||
|
// Two authorities.
|
||||||
|
let mut step = time - time % 2;
|
||||||
header.set_seal(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
header.set_seal(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||||
let first_ok = engine.verify_block_seal(&header).is_ok();
|
assert!(engine.verify_block_seal(&header).is_err());
|
||||||
step = step + 1;
|
step = step + 1;
|
||||||
header.set_seal(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
header.set_seal(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
|
||||||
let second_ok = engine.verify_block_seal(&header).is_ok();
|
assert!(engine.verify_block_seal(&header).is_ok());
|
||||||
|
|
||||||
assert!(first_ok ^ second_ok);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -577,29 +577,29 @@ impl Miner {
|
|||||||
let gas_required = |tx: &SignedTransaction| tx.gas_required(&schedule).into();
|
let gas_required = |tx: &SignedTransaction| tx.gas_required(&schedule).into();
|
||||||
let best_block_header: Header = ::rlp::decode(&chain.best_block_header());
|
let best_block_header: Header = ::rlp::decode(&chain.best_block_header());
|
||||||
transactions.into_iter()
|
transactions.into_iter()
|
||||||
.filter(|tx| match self.engine.verify_transaction_basic(tx, &best_block_header) {
|
.map(|tx| {
|
||||||
Ok(()) => true,
|
match self.engine.verify_transaction_basic(&tx, &best_block_header) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug!(target: "miner", "Rejected tx {:?} with invalid signature: {:?}", tx.hash(), e);
|
debug!(target: "miner", "Rejected tx {:?} with invalid signature: {:?}", tx.hash(), e);
|
||||||
false
|
Err(e)
|
||||||
}
|
},
|
||||||
}
|
Ok(()) => {
|
||||||
)
|
let origin = accounts.as_ref().and_then(|accounts| {
|
||||||
.map(|tx| {
|
tx.sender().ok().and_then(|sender| match accounts.contains(&sender) {
|
||||||
let origin = accounts.as_ref().and_then(|accounts| {
|
true => Some(TransactionOrigin::Local),
|
||||||
tx.sender().ok().and_then(|sender| match accounts.contains(&sender) {
|
false => None,
|
||||||
true => Some(TransactionOrigin::Local),
|
})
|
||||||
false => None,
|
}).unwrap_or(default_origin);
|
||||||
})
|
|
||||||
}).unwrap_or(default_origin);
|
match origin {
|
||||||
|
TransactionOrigin::Local | TransactionOrigin::RetractedBlock => {
|
||||||
match origin {
|
transaction_queue.add(tx, origin, &fetch_account, &gas_required)
|
||||||
TransactionOrigin::Local | TransactionOrigin::RetractedBlock => {
|
},
|
||||||
transaction_queue.add(tx, origin, &fetch_account, &gas_required)
|
TransactionOrigin::External => {
|
||||||
|
transaction_queue.add_with_banlist(tx, &fetch_account, &gas_required)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
TransactionOrigin::External => {
|
|
||||||
transaction_queue.add_with_banlist(tx, &fetch_account, &gas_required)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -128,6 +128,9 @@ impl<T> TraceDB<T> where T: DatabaseExtras {
|
|||||||
/// Creates new instance of `TraceDB`.
|
/// Creates new instance of `TraceDB`.
|
||||||
pub fn new(config: Config, tracesdb: Arc<Database>, extras: Arc<T>) -> Self {
|
pub fn new(config: Config, tracesdb: Arc<Database>, extras: Arc<T>) -> Self {
|
||||||
let mut batch = DBTransaction::new(&tracesdb);
|
let mut batch = DBTransaction::new(&tracesdb);
|
||||||
|
let genesis = extras.block_hash(0)
|
||||||
|
.expect("Genesis block is always inserted upon extras db creation qed");
|
||||||
|
batch.write(db::COL_TRACE, &genesis, &FlatBlockTraces::default());
|
||||||
batch.put(db::COL_TRACE, b"version", TRACE_DB_VER);
|
batch.put(db::COL_TRACE, b"version", TRACE_DB_VER);
|
||||||
tracesdb.write(batch).expect("failed to update version");
|
tracesdb.write(batch).expect("failed to update version");
|
||||||
|
|
||||||
@ -413,8 +416,12 @@ mod tests {
|
|||||||
struct NoopExtras;
|
struct NoopExtras;
|
||||||
|
|
||||||
impl DatabaseExtras for NoopExtras {
|
impl DatabaseExtras for NoopExtras {
|
||||||
fn block_hash(&self, _block_number: BlockNumber) -> Option<H256> {
|
fn block_hash(&self, block_number: BlockNumber) -> Option<H256> {
|
||||||
unimplemented!();
|
if block_number == 0 {
|
||||||
|
Some(H256::default())
|
||||||
|
} else {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transaction_hash(&self, _block_number: BlockNumber, _tx_position: usize) -> Option<H256> {
|
fn transaction_hash(&self, _block_number: BlockNumber, _tx_position: usize) -> Option<H256> {
|
||||||
@ -581,35 +588,21 @@ mod tests {
|
|||||||
let db = Arc::new(Database::open(&DatabaseConfig::with_columns(::db::NUM_COLUMNS), temp.as_str()).unwrap());
|
let db = Arc::new(Database::open(&DatabaseConfig::with_columns(::db::NUM_COLUMNS), temp.as_str()).unwrap());
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
config.enabled = true;
|
config.enabled = true;
|
||||||
let block_0 = H256::from(0xa1);
|
let block_1 = H256::from(0xa1);
|
||||||
let block_1 = H256::from(0xa2);
|
let block_2 = H256::from(0xa2);
|
||||||
let tx_0 = H256::from(0xff);
|
let tx_1 = H256::from(0xff);
|
||||||
let tx_1 = H256::from(0xaf);
|
let tx_2 = H256::from(0xaf);
|
||||||
|
|
||||||
let mut extras = Extras::default();
|
let mut extras = Extras::default();
|
||||||
extras.block_hashes.insert(0, block_0.clone());
|
extras.block_hashes.insert(0, H256::default());
|
||||||
|
|
||||||
extras.block_hashes.insert(1, block_1.clone());
|
extras.block_hashes.insert(1, block_1.clone());
|
||||||
extras.transaction_hashes.insert(0, vec![tx_0.clone()]);
|
extras.block_hashes.insert(2, block_2.clone());
|
||||||
extras.transaction_hashes.insert(1, vec![tx_1.clone()]);
|
extras.transaction_hashes.insert(1, vec![tx_1.clone()]);
|
||||||
|
extras.transaction_hashes.insert(2, vec![tx_2.clone()]);
|
||||||
|
|
||||||
let tracedb = TraceDB::new(config, db.clone(), Arc::new(extras));
|
let tracedb = TraceDB::new(config, db.clone(), Arc::new(extras));
|
||||||
|
|
||||||
// import block 0
|
|
||||||
let request = create_simple_import_request(0, block_0.clone());
|
|
||||||
let mut batch = DBTransaction::new(&db);
|
|
||||||
tracedb.import(&mut batch, request);
|
|
||||||
db.write(batch).unwrap();
|
|
||||||
|
|
||||||
let filter = Filter {
|
|
||||||
range: (0..0),
|
|
||||||
from_address: AddressesFilter::from(vec![Address::from(1)]),
|
|
||||||
to_address: AddressesFilter::from(vec![]),
|
|
||||||
};
|
|
||||||
|
|
||||||
let traces = tracedb.filter(&filter);
|
|
||||||
assert_eq!(traces.len(), 1);
|
|
||||||
assert_eq!(traces[0], create_simple_localized_trace(0, block_0.clone(), tx_0.clone()));
|
|
||||||
|
|
||||||
// import block 1
|
// import block 1
|
||||||
let request = create_simple_import_request(1, block_1.clone());
|
let request = create_simple_import_request(1, block_1.clone());
|
||||||
let mut batch = DBTransaction::new(&db);
|
let mut batch = DBTransaction::new(&db);
|
||||||
@ -617,38 +610,56 @@ mod tests {
|
|||||||
db.write(batch).unwrap();
|
db.write(batch).unwrap();
|
||||||
|
|
||||||
let filter = Filter {
|
let filter = Filter {
|
||||||
range: (0..1),
|
range: (1..1),
|
||||||
|
from_address: AddressesFilter::from(vec![Address::from(1)]),
|
||||||
|
to_address: AddressesFilter::from(vec![]),
|
||||||
|
};
|
||||||
|
|
||||||
|
let traces = tracedb.filter(&filter);
|
||||||
|
assert_eq!(traces.len(), 1);
|
||||||
|
assert_eq!(traces[0], create_simple_localized_trace(1, block_1.clone(), tx_1.clone()));
|
||||||
|
|
||||||
|
// import block 2
|
||||||
|
let request = create_simple_import_request(2, block_2.clone());
|
||||||
|
let mut batch = DBTransaction::new(&db);
|
||||||
|
tracedb.import(&mut batch, request);
|
||||||
|
db.write(batch).unwrap();
|
||||||
|
|
||||||
|
let filter = Filter {
|
||||||
|
range: (1..2),
|
||||||
from_address: AddressesFilter::from(vec![Address::from(1)]),
|
from_address: AddressesFilter::from(vec![Address::from(1)]),
|
||||||
to_address: AddressesFilter::from(vec![]),
|
to_address: AddressesFilter::from(vec![]),
|
||||||
};
|
};
|
||||||
|
|
||||||
let traces = tracedb.filter(&filter);
|
let traces = tracedb.filter(&filter);
|
||||||
assert_eq!(traces.len(), 2);
|
assert_eq!(traces.len(), 2);
|
||||||
assert_eq!(traces[0], create_simple_localized_trace(0, block_0.clone(), tx_0.clone()));
|
assert_eq!(traces[0], create_simple_localized_trace(1, block_1.clone(), tx_1.clone()));
|
||||||
assert_eq!(traces[1], create_simple_localized_trace(1, block_1.clone(), tx_1.clone()));
|
assert_eq!(traces[1], create_simple_localized_trace(2, block_2.clone(), tx_2.clone()));
|
||||||
|
|
||||||
let traces = tracedb.block_traces(0).unwrap();
|
assert!(tracedb.block_traces(0).is_some(), "Genesis trace should be always present.");
|
||||||
assert_eq!(traces.len(), 1);
|
|
||||||
assert_eq!(traces[0], create_simple_localized_trace(0, block_0.clone(), tx_0.clone()));
|
|
||||||
|
|
||||||
let traces = tracedb.block_traces(1).unwrap();
|
let traces = tracedb.block_traces(1).unwrap();
|
||||||
assert_eq!(traces.len(), 1);
|
assert_eq!(traces.len(), 1);
|
||||||
assert_eq!(traces[0], create_simple_localized_trace(1, block_1.clone(), tx_1.clone()));
|
assert_eq!(traces[0], create_simple_localized_trace(1, block_1.clone(), tx_1.clone()));
|
||||||
|
|
||||||
assert_eq!(None, tracedb.block_traces(2));
|
let traces = tracedb.block_traces(2).unwrap();
|
||||||
|
|
||||||
let traces = tracedb.transaction_traces(0, 0).unwrap();
|
|
||||||
assert_eq!(traces.len(), 1);
|
assert_eq!(traces.len(), 1);
|
||||||
assert_eq!(traces[0], create_simple_localized_trace(0, block_0.clone(), tx_0.clone()));
|
assert_eq!(traces[0], create_simple_localized_trace(2, block_2.clone(), tx_2.clone()));
|
||||||
|
|
||||||
|
assert_eq!(None, tracedb.block_traces(3));
|
||||||
|
|
||||||
let traces = tracedb.transaction_traces(1, 0).unwrap();
|
let traces = tracedb.transaction_traces(1, 0).unwrap();
|
||||||
assert_eq!(traces.len(), 1);
|
assert_eq!(traces.len(), 1);
|
||||||
assert_eq!(traces[0], create_simple_localized_trace(1, block_1.clone(), tx_1.clone()));
|
assert_eq!(traces[0], create_simple_localized_trace(1, block_1.clone(), tx_1.clone()));
|
||||||
|
|
||||||
assert_eq!(None, tracedb.transaction_traces(1, 1));
|
let traces = tracedb.transaction_traces(2, 0).unwrap();
|
||||||
|
assert_eq!(traces.len(), 1);
|
||||||
|
assert_eq!(traces[0], create_simple_localized_trace(2, block_2.clone(), tx_2.clone()));
|
||||||
|
|
||||||
|
assert_eq!(None, tracedb.transaction_traces(2, 1));
|
||||||
|
|
||||||
assert_eq!(tracedb.trace(0, 0, vec![]).unwrap(), create_simple_localized_trace(0, block_0.clone(), tx_0.clone()));
|
|
||||||
assert_eq!(tracedb.trace(1, 0, vec![]).unwrap(), create_simple_localized_trace(1, block_1.clone(), tx_1.clone()));
|
assert_eq!(tracedb.trace(1, 0, vec![]).unwrap(), create_simple_localized_trace(1, block_1.clone(), tx_1.clone()));
|
||||||
|
assert_eq!(tracedb.trace(2, 0, vec![]).unwrap(), create_simple_localized_trace(2, block_2.clone(), tx_2.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -660,8 +671,10 @@ mod tests {
|
|||||||
let block_0 = H256::from(0xa1);
|
let block_0 = H256::from(0xa1);
|
||||||
let tx_0 = H256::from(0xff);
|
let tx_0 = H256::from(0xff);
|
||||||
|
|
||||||
extras.block_hashes.insert(0, block_0.clone());
|
extras.block_hashes.insert(0, H256::default());
|
||||||
extras.transaction_hashes.insert(0, vec![tx_0.clone()]);
|
extras.transaction_hashes.insert(0, vec![]);
|
||||||
|
extras.block_hashes.insert(1, block_0.clone());
|
||||||
|
extras.transaction_hashes.insert(1, vec![tx_0.clone()]);
|
||||||
|
|
||||||
// set tracing on
|
// set tracing on
|
||||||
config.enabled = true;
|
config.enabled = true;
|
||||||
@ -669,8 +682,8 @@ mod tests {
|
|||||||
{
|
{
|
||||||
let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(extras.clone()));
|
let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(extras.clone()));
|
||||||
|
|
||||||
// import block 0
|
// import block 1
|
||||||
let request = create_simple_import_request(0, block_0.clone());
|
let request = create_simple_import_request(1, block_0.clone());
|
||||||
let mut batch = DBTransaction::new(&db);
|
let mut batch = DBTransaction::new(&db);
|
||||||
tracedb.import(&mut batch, request);
|
tracedb.import(&mut batch, request);
|
||||||
db.write(batch).unwrap();
|
db.write(batch).unwrap();
|
||||||
@ -678,8 +691,28 @@ mod tests {
|
|||||||
|
|
||||||
{
|
{
|
||||||
let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(extras));
|
let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(extras));
|
||||||
let traces = tracedb.transaction_traces(0, 0);
|
let traces = tracedb.transaction_traces(1, 0);
|
||||||
assert_eq!(traces.unwrap(), vec![create_simple_localized_trace(0, block_0, tx_0)]);
|
assert_eq!(traces.unwrap(), vec![create_simple_localized_trace(1, block_0, tx_0)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn query_genesis() {
|
||||||
|
let temp = RandomTempPath::new();
|
||||||
|
let db = new_db(temp.as_str());
|
||||||
|
let mut config = Config::default();
|
||||||
|
let mut extras = Extras::default();
|
||||||
|
let block_0 = H256::from(0xa1);
|
||||||
|
|
||||||
|
extras.block_hashes.insert(0, block_0.clone());
|
||||||
|
extras.transaction_hashes.insert(0, vec![]);
|
||||||
|
|
||||||
|
// set tracing on
|
||||||
|
config.enabled = true;
|
||||||
|
|
||||||
|
let tracedb = TraceDB::new(config.clone(), db.clone(), Arc::new(extras.clone()));
|
||||||
|
let traces = tracedb.block_traces(0).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(traces.len(), 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ impl Into<Vec<FlatTrace>> for FlatTransactionTraces {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Represents all traces produced by transactions in a single block.
|
/// Represents all traces produced by transactions in a single block.
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone, Default)]
|
||||||
pub struct FlatBlockTraces(Vec<FlatTransactionTraces>);
|
pub struct FlatBlockTraces(Vec<FlatTransactionTraces>);
|
||||||
|
|
||||||
impl HeapSizeOf for FlatBlockTraces {
|
impl HeapSizeOf for FlatBlockTraces {
|
||||||
|
@ -35,6 +35,9 @@ pub mod kind;
|
|||||||
const MIN_MEM_LIMIT: usize = 16384;
|
const MIN_MEM_LIMIT: usize = 16384;
|
||||||
const MIN_QUEUE_LIMIT: usize = 512;
|
const MIN_QUEUE_LIMIT: usize = 512;
|
||||||
|
|
||||||
|
// maximum possible number of verification threads.
|
||||||
|
const MAX_VERIFIERS: usize = 8;
|
||||||
|
|
||||||
/// Type alias for block queue convenience.
|
/// Type alias for block queue convenience.
|
||||||
pub type BlockQueue = VerificationQueue<self::kind::Blocks>;
|
pub type BlockQueue = VerificationQueue<self::kind::Blocks>;
|
||||||
|
|
||||||
@ -61,6 +64,37 @@ impl Default for Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct VerifierHandle {
|
||||||
|
deleting: Arc<AtomicBool>,
|
||||||
|
sleep: Arc<AtomicBool>,
|
||||||
|
thread: JoinHandle<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VerifierHandle {
|
||||||
|
// signal to the verifier thread that it should sleep.
|
||||||
|
fn sleep(&self) {
|
||||||
|
self.sleep.store(true, AtomicOrdering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
// signal to the verifier thread that it should wake up.
|
||||||
|
fn wake_up(&self) {
|
||||||
|
self.sleep.store(false, AtomicOrdering::SeqCst);
|
||||||
|
self.thread.thread().unpark();
|
||||||
|
}
|
||||||
|
|
||||||
|
// signal to the verifier thread that it should conclude its
|
||||||
|
// operations.
|
||||||
|
fn conclude(&self) {
|
||||||
|
self.wake_up();
|
||||||
|
self.deleting.store(true, AtomicOrdering::Release);
|
||||||
|
}
|
||||||
|
|
||||||
|
// join the verifier thread.
|
||||||
|
fn join(self) {
|
||||||
|
self.thread.join().expect("Verifier thread panicked");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An item which is in the process of being verified.
|
/// An item which is in the process of being verified.
|
||||||
pub struct Verifying<K: Kind> {
|
pub struct Verifying<K: Kind> {
|
||||||
hash: H256,
|
hash: H256,
|
||||||
@ -97,11 +131,12 @@ pub struct VerificationQueue<K: Kind> {
|
|||||||
engine: Arc<Engine>,
|
engine: Arc<Engine>,
|
||||||
more_to_verify: Arc<SCondvar>,
|
more_to_verify: Arc<SCondvar>,
|
||||||
verification: Arc<Verification<K>>,
|
verification: Arc<Verification<K>>,
|
||||||
verifiers: Vec<JoinHandle<()>>,
|
verifiers: Mutex<(Vec<VerifierHandle>, usize)>,
|
||||||
deleting: Arc<AtomicBool>,
|
deleting: Arc<AtomicBool>,
|
||||||
ready_signal: Arc<QueueSignal>,
|
ready_signal: Arc<QueueSignal>,
|
||||||
empty: Arc<SCondvar>,
|
empty: Arc<SCondvar>,
|
||||||
processing: RwLock<HashSet<H256>>,
|
processing: RwLock<HashSet<H256>>,
|
||||||
|
ticks_since_adjustment: AtomicUsize,
|
||||||
max_queue_size: usize,
|
max_queue_size: usize,
|
||||||
max_mem_use: usize,
|
max_mem_use: usize,
|
||||||
}
|
}
|
||||||
@ -157,6 +192,7 @@ struct Verification<K: Kind> {
|
|||||||
more_to_verify: SMutex<()>,
|
more_to_verify: SMutex<()>,
|
||||||
empty: SMutex<()>,
|
empty: SMutex<()>,
|
||||||
sizes: Sizes,
|
sizes: Sizes,
|
||||||
|
check_seal: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: Kind> VerificationQueue<K> {
|
impl<K: Kind> VerificationQueue<K> {
|
||||||
@ -173,7 +209,8 @@ impl<K: Kind> VerificationQueue<K> {
|
|||||||
unverified: AtomicUsize::new(0),
|
unverified: AtomicUsize::new(0),
|
||||||
verifying: AtomicUsize::new(0),
|
verifying: AtomicUsize::new(0),
|
||||||
verified: AtomicUsize::new(0),
|
verified: AtomicUsize::new(0),
|
||||||
}
|
},
|
||||||
|
check_seal: check_seal,
|
||||||
});
|
});
|
||||||
let more_to_verify = Arc::new(SCondvar::new());
|
let more_to_verify = Arc::new(SCondvar::new());
|
||||||
let deleting = Arc::new(AtomicBool::new(false));
|
let deleting = Arc::new(AtomicBool::new(false));
|
||||||
@ -185,44 +222,82 @@ impl<K: Kind> VerificationQueue<K> {
|
|||||||
let empty = Arc::new(SCondvar::new());
|
let empty = Arc::new(SCondvar::new());
|
||||||
let panic_handler = PanicHandler::new_in_arc();
|
let panic_handler = PanicHandler::new_in_arc();
|
||||||
|
|
||||||
let mut verifiers: Vec<JoinHandle<()>> = Vec::new();
|
let max_verifiers = min(::num_cpus::get(), MAX_VERIFIERS);
|
||||||
let thread_count = max(::num_cpus::get(), 3) - 2;
|
let default_amount = max(::num_cpus::get(), 3) - 2;
|
||||||
for i in 0..thread_count {
|
let mut verifiers = Vec::with_capacity(max_verifiers);
|
||||||
let verification = verification.clone();
|
|
||||||
let engine = engine.clone();
|
debug!(target: "verification", "Allocating {} verifiers, {} initially active", max_verifiers, default_amount);
|
||||||
let more_to_verify = more_to_verify.clone();
|
|
||||||
let ready_signal = ready_signal.clone();
|
for i in 0..max_verifiers {
|
||||||
let empty = empty.clone();
|
debug!(target: "verification", "Adding verification thread #{}", i);
|
||||||
|
|
||||||
let deleting = deleting.clone();
|
let deleting = deleting.clone();
|
||||||
let panic_handler = panic_handler.clone();
|
let panic_handler = panic_handler.clone();
|
||||||
verifiers.push(
|
let verification = verification.clone();
|
||||||
thread::Builder::new()
|
let engine = engine.clone();
|
||||||
.name(format!("Verifier #{}", i))
|
let wait = more_to_verify.clone();
|
||||||
.spawn(move || {
|
let ready = ready_signal.clone();
|
||||||
panic_handler.catch_panic(move || {
|
let empty = empty.clone();
|
||||||
VerificationQueue::verify(verification, engine, more_to_verify, ready_signal, deleting, empty, check_seal)
|
|
||||||
}).unwrap()
|
// enable only the first few verifiers.
|
||||||
})
|
let sleep = if i < default_amount {
|
||||||
.expect("Error starting block verification thread")
|
Arc::new(AtomicBool::new(false))
|
||||||
);
|
} else {
|
||||||
|
Arc::new(AtomicBool::new(true))
|
||||||
|
};
|
||||||
|
|
||||||
|
verifiers.push(VerifierHandle {
|
||||||
|
deleting: deleting.clone(),
|
||||||
|
sleep: sleep.clone(),
|
||||||
|
thread: thread::Builder::new()
|
||||||
|
.name(format!("Verifier #{}", i))
|
||||||
|
.spawn(move || {
|
||||||
|
panic_handler.catch_panic(move || {
|
||||||
|
VerificationQueue::verify(verification, engine, wait, ready, deleting, empty, sleep)
|
||||||
|
}).unwrap()
|
||||||
|
})
|
||||||
|
.expect("Failed to create verifier thread.")
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
VerificationQueue {
|
VerificationQueue {
|
||||||
engine: engine,
|
engine: engine,
|
||||||
panic_handler: panic_handler,
|
panic_handler: panic_handler,
|
||||||
ready_signal: ready_signal.clone(),
|
ready_signal: ready_signal,
|
||||||
more_to_verify: more_to_verify.clone(),
|
more_to_verify: more_to_verify,
|
||||||
verification: verification.clone(),
|
verification: verification,
|
||||||
verifiers: verifiers,
|
verifiers: Mutex::new((verifiers, default_amount)),
|
||||||
deleting: deleting.clone(),
|
deleting: deleting,
|
||||||
processing: RwLock::new(HashSet::new()),
|
processing: RwLock::new(HashSet::new()),
|
||||||
empty: empty.clone(),
|
empty: empty,
|
||||||
|
ticks_since_adjustment: AtomicUsize::new(0),
|
||||||
max_queue_size: max(config.max_queue_size, MIN_QUEUE_LIMIT),
|
max_queue_size: max(config.max_queue_size, MIN_QUEUE_LIMIT),
|
||||||
max_mem_use: max(config.max_mem_use, MIN_MEM_LIMIT),
|
max_mem_use: max(config.max_mem_use, MIN_MEM_LIMIT),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify(verification: Arc<Verification<K>>, engine: Arc<Engine>, wait: Arc<SCondvar>, ready: Arc<QueueSignal>, deleting: Arc<AtomicBool>, empty: Arc<SCondvar>, check_seal: bool) {
|
fn verify(
|
||||||
|
verification: Arc<Verification<K>>,
|
||||||
|
engine: Arc<Engine>,
|
||||||
|
wait: Arc<SCondvar>,
|
||||||
|
ready: Arc<QueueSignal>,
|
||||||
|
deleting: Arc<AtomicBool>,
|
||||||
|
empty: Arc<SCondvar>,
|
||||||
|
sleep: Arc<AtomicBool>,
|
||||||
|
) {
|
||||||
while !deleting.load(AtomicOrdering::Acquire) {
|
while !deleting.load(AtomicOrdering::Acquire) {
|
||||||
|
{
|
||||||
|
while sleep.load(AtomicOrdering::SeqCst) {
|
||||||
|
trace!(target: "verification", "Verifier sleeping");
|
||||||
|
::std::thread::park();
|
||||||
|
trace!(target: "verification", "Verifier waking up");
|
||||||
|
|
||||||
|
if deleting.load(AtomicOrdering::Acquire) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut more_to_verify = verification.more_to_verify.lock().unwrap();
|
let mut more_to_verify = verification.more_to_verify.lock().unwrap();
|
||||||
|
|
||||||
@ -255,7 +330,7 @@ impl<K: Kind> VerificationQueue<K> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let hash = item.hash();
|
let hash = item.hash();
|
||||||
let is_ready = match K::verify(item, &*engine, check_seal) {
|
let is_ready = match K::verify(item, &*engine, verification.check_seal) {
|
||||||
Ok(verified) => {
|
Ok(verified) => {
|
||||||
let mut verifying = verification.verifying.lock();
|
let mut verifying = verification.verifying.lock();
|
||||||
let mut idx = None;
|
let mut idx = None;
|
||||||
@ -302,9 +377,15 @@ impl<K: Kind> VerificationQueue<K> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drain_verifying(verifying: &mut VecDeque<Verifying<K>>, verified: &mut VecDeque<K::Verified>, bad: &mut HashSet<H256>, sizes: &Sizes) {
|
fn drain_verifying(
|
||||||
|
verifying: &mut VecDeque<Verifying<K>>,
|
||||||
|
verified: &mut VecDeque<K::Verified>,
|
||||||
|
bad: &mut HashSet<H256>,
|
||||||
|
sizes: &Sizes,
|
||||||
|
) {
|
||||||
let mut removed_size = 0;
|
let mut removed_size = 0;
|
||||||
let mut inserted_size = 0;
|
let mut inserted_size = 0;
|
||||||
|
|
||||||
while let Some(output) = verifying.front_mut().and_then(|x| x.output.take()) {
|
while let Some(output) = verifying.front_mut().and_then(|x| x.output.take()) {
|
||||||
assert!(verifying.pop_front().is_some());
|
assert!(verifying.pop_front().is_some());
|
||||||
let size = output.heap_size_of_children();
|
let size = output.heap_size_of_children();
|
||||||
@ -487,14 +568,85 @@ impl<K: Kind> VerificationQueue<K> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optimise memory footprint of the heap fields.
|
/// Optimise memory footprint of the heap fields, and adjust the number of threads
|
||||||
|
/// to better suit the workload.
|
||||||
pub fn collect_garbage(&self) {
|
pub fn collect_garbage(&self) {
|
||||||
{
|
// number of ticks to average queue stats over
|
||||||
self.verification.unverified.lock().shrink_to_fit();
|
// when deciding whether to change the number of verifiers.
|
||||||
|
#[cfg(not(test))]
|
||||||
|
const READJUSTMENT_PERIOD: usize = 12;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
const READJUSTMENT_PERIOD: usize = 1;
|
||||||
|
|
||||||
|
let (u_len, v_len) = {
|
||||||
|
let u_len = {
|
||||||
|
let mut q = self.verification.unverified.lock();
|
||||||
|
q.shrink_to_fit();
|
||||||
|
q.len()
|
||||||
|
};
|
||||||
self.verification.verifying.lock().shrink_to_fit();
|
self.verification.verifying.lock().shrink_to_fit();
|
||||||
self.verification.verified.lock().shrink_to_fit();
|
|
||||||
}
|
let v_len = {
|
||||||
|
let mut q = self.verification.verified.lock();
|
||||||
|
q.shrink_to_fit();
|
||||||
|
q.len()
|
||||||
|
};
|
||||||
|
|
||||||
|
(u_len as isize, v_len as isize)
|
||||||
|
};
|
||||||
|
|
||||||
self.processing.write().shrink_to_fit();
|
self.processing.write().shrink_to_fit();
|
||||||
|
|
||||||
|
if self.ticks_since_adjustment.fetch_add(1, AtomicOrdering::SeqCst) + 1 >= READJUSTMENT_PERIOD {
|
||||||
|
self.ticks_since_adjustment.store(0, AtomicOrdering::SeqCst);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let current = self.verifiers.lock().1;
|
||||||
|
|
||||||
|
let diff = (v_len - u_len).abs();
|
||||||
|
let total = v_len + u_len;
|
||||||
|
|
||||||
|
self.scale_verifiers(
|
||||||
|
if u_len < 20 {
|
||||||
|
1
|
||||||
|
} else if diff <= total / 10 {
|
||||||
|
current
|
||||||
|
} else if v_len > u_len {
|
||||||
|
current - 1
|
||||||
|
} else {
|
||||||
|
current + 1
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// wake up or sleep verifiers to get as close to the target as
|
||||||
|
// possible, never going over the amount of initially allocated threads
|
||||||
|
// or below 1.
|
||||||
|
fn scale_verifiers(&self, target: usize) {
|
||||||
|
let mut verifiers = self.verifiers.lock();
|
||||||
|
let &mut (ref mut verifiers, ref mut verifier_count) = &mut *verifiers;
|
||||||
|
|
||||||
|
let target = min(verifiers.len(), target);
|
||||||
|
let target = max(1, target);
|
||||||
|
|
||||||
|
debug!(target: "verification", "Scaling from {} to {} verifiers", verifier_count, target);
|
||||||
|
|
||||||
|
// scaling up
|
||||||
|
for i in *verifier_count..target {
|
||||||
|
debug!(target: "verification", "Waking up verifier {}", i);
|
||||||
|
verifiers[i].wake_up();
|
||||||
|
}
|
||||||
|
|
||||||
|
// scaling down.
|
||||||
|
for i in target..*verifier_count {
|
||||||
|
debug!(target: "verification", "Putting verifier {} to sleep", i);
|
||||||
|
verifiers[i].sleep();
|
||||||
|
}
|
||||||
|
|
||||||
|
*verifier_count = target;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,10 +661,23 @@ impl<K: Kind> Drop for VerificationQueue<K> {
|
|||||||
trace!(target: "shutdown", "[VerificationQueue] Closing...");
|
trace!(target: "shutdown", "[VerificationQueue] Closing...");
|
||||||
self.clear();
|
self.clear();
|
||||||
self.deleting.store(true, AtomicOrdering::Release);
|
self.deleting.store(true, AtomicOrdering::Release);
|
||||||
self.more_to_verify.notify_all();
|
|
||||||
for t in self.verifiers.drain(..) {
|
let mut verifiers = self.verifiers.get_mut();
|
||||||
t.join().unwrap();
|
let mut verifiers = &mut verifiers.0;
|
||||||
|
|
||||||
|
// first pass to signal conclusion. must be done before
|
||||||
|
// notify or deadlock possible.
|
||||||
|
for handle in verifiers.iter() {
|
||||||
|
handle.conclude();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.more_to_verify.notify_all();
|
||||||
|
|
||||||
|
// second pass to join.
|
||||||
|
for handle in verifiers.drain(..) {
|
||||||
|
handle.join();
|
||||||
|
}
|
||||||
|
|
||||||
trace!(target: "shutdown", "[VerificationQueue] Closed.");
|
trace!(target: "shutdown", "[VerificationQueue] Closed.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -611,4 +776,56 @@ mod tests {
|
|||||||
}
|
}
|
||||||
assert!(queue.queue_info().is_full());
|
assert!(queue.queue_info().is_full());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scaling_limits() {
|
||||||
|
use super::MAX_VERIFIERS;
|
||||||
|
|
||||||
|
let queue = get_test_queue();
|
||||||
|
queue.scale_verifiers(MAX_VERIFIERS + 1);
|
||||||
|
|
||||||
|
assert!(queue.verifiers.lock().1 < MAX_VERIFIERS + 1);
|
||||||
|
|
||||||
|
queue.scale_verifiers(0);
|
||||||
|
|
||||||
|
assert!(queue.verifiers.lock().1 == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn readjust_verifiers() {
|
||||||
|
let queue = get_test_queue();
|
||||||
|
|
||||||
|
// put all the verifiers to sleep to ensure
|
||||||
|
// the test isn't timing sensitive.
|
||||||
|
let num_verifiers = {
|
||||||
|
let verifiers = queue.verifiers.lock();
|
||||||
|
for i in 0..verifiers.1 {
|
||||||
|
verifiers.0[i].sleep();
|
||||||
|
}
|
||||||
|
|
||||||
|
verifiers.1
|
||||||
|
};
|
||||||
|
|
||||||
|
for block in get_good_dummy_block_seq(5000) {
|
||||||
|
queue.import(Unverified::new(block)).expect("Block good by definition; qed");
|
||||||
|
}
|
||||||
|
|
||||||
|
// almost all unverified == bump verifier count.
|
||||||
|
queue.collect_garbage();
|
||||||
|
assert_eq!(queue.verifiers.lock().1, num_verifiers + 1);
|
||||||
|
|
||||||
|
// wake them up again and verify everything.
|
||||||
|
{
|
||||||
|
let verifiers = queue.verifiers.lock();
|
||||||
|
for i in 0..verifiers.1 {
|
||||||
|
verifiers.0[i].wake_up();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.flush();
|
||||||
|
|
||||||
|
// nothing to verify == use minimum number of verifiers.
|
||||||
|
queue.collect_garbage();
|
||||||
|
assert_eq!(queue.verifiers.lock().1, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "parity.js",
|
"name": "parity.js",
|
||||||
"version": "0.2.58",
|
"version": "0.2.73",
|
||||||
"main": "release/index.js",
|
"main": "release/index.js",
|
||||||
"jsnext:main": "src/index.js",
|
"jsnext:main": "src/index.js",
|
||||||
"author": "Parity Team <admin@parity.io>",
|
"author": "Parity Team <admin@parity.io>",
|
||||||
@ -102,6 +102,7 @@
|
|||||||
"postcss-nested": "^1.0.0",
|
"postcss-nested": "^1.0.0",
|
||||||
"postcss-simple-vars": "^3.0.0",
|
"postcss-simple-vars": "^3.0.0",
|
||||||
"raw-loader": "^0.5.1",
|
"raw-loader": "^0.5.1",
|
||||||
|
"react-addons-perf": "~15.3.2",
|
||||||
"react-addons-test-utils": "~15.3.2",
|
"react-addons-test-utils": "~15.3.2",
|
||||||
"react-copy-to-clipboard": "^4.2.3",
|
"react-copy-to-clipboard": "^4.2.3",
|
||||||
"react-dom": "~15.3.2",
|
"react-dom": "~15.3.2",
|
||||||
|
26
js/src/3rdparty/shapeshift/shapeshift.js
vendored
26
js/src/3rdparty/shapeshift/shapeshift.js
vendored
@ -15,7 +15,8 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
export default function (rpc) {
|
export default function (rpc) {
|
||||||
const subscriptions = [];
|
let subscriptions = [];
|
||||||
|
let pollStatusIntervalId = null;
|
||||||
|
|
||||||
function getCoins () {
|
function getCoins () {
|
||||||
return rpc.get('getcoins');
|
return rpc.get('getcoins');
|
||||||
@ -45,6 +46,24 @@ export default function (rpc) {
|
|||||||
callback,
|
callback,
|
||||||
idx
|
idx
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Only poll if there are subscriptions...
|
||||||
|
if (!pollStatusIntervalId) {
|
||||||
|
pollStatusIntervalId = setInterval(_pollStatus, 2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function unsubscribe (depositAddress) {
|
||||||
|
const newSubscriptions = []
|
||||||
|
.concat(subscriptions)
|
||||||
|
.filter((sub) => sub.depositAddress !== depositAddress);
|
||||||
|
|
||||||
|
subscriptions = newSubscriptions;
|
||||||
|
|
||||||
|
if (subscriptions.length === 0) {
|
||||||
|
clearInterval(pollStatusIntervalId);
|
||||||
|
pollStatusIntervalId = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _getSubscriptionStatus (subscription) {
|
function _getSubscriptionStatus (subscription) {
|
||||||
@ -81,13 +100,12 @@ export default function (rpc) {
|
|||||||
subscriptions.forEach(_getSubscriptionStatus);
|
subscriptions.forEach(_getSubscriptionStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
setInterval(_pollStatus, 2000);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getCoins,
|
getCoins,
|
||||||
getMarketInfo,
|
getMarketInfo,
|
||||||
getStatus,
|
getStatus,
|
||||||
shift,
|
shift,
|
||||||
subscribe
|
subscribe,
|
||||||
|
unsubscribe
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,10 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { stringify } from 'querystring';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export default (
|
export const termsOfService = (
|
||||||
<ul>
|
<ul>
|
||||||
<li>This privacy notice relates to your use of the Parity SMS verification service. We take your privacy seriously and deal in an honest, direct and transparent way when it comes to your data.</li>
|
<li>This privacy notice relates to your use of the Parity SMS verification service. We take your privacy seriously and deal in an honest, direct and transparent way when it comes to your data.</li>
|
||||||
<li>We collect your phone number when you use this service. This is temporarily kept in memory, and then encrypted and stored in our EU servers. We only retain the cryptographic hash of the number to prevent duplicated accounts. You consent to this use.</li>
|
<li>We collect your phone number when you use this service. This is temporarily kept in memory, and then encrypted and stored in our EU servers. We only retain the cryptographic hash of the number to prevent duplicated accounts. You consent to this use.</li>
|
||||||
@ -25,3 +26,18 @@ export default (
|
|||||||
<li><i>Parity Technology Limited</i> is registered in England and Wales under company number <code>09760015</code> and complies with the Data Protection Act 1998 (UK). You may contact us via email at <a href={ 'mailto:admin@parity.io' }>admin@parity.io</a>. Our general privacy policy can be found here: <a href={ 'https://ethcore.io/legal.html' }>https://ethcore.io/legal.html</a>.</li>
|
<li><i>Parity Technology Limited</i> is registered in England and Wales under company number <code>09760015</code> and complies with the Data Protection Act 1998 (UK). You may contact us via email at <a href={ 'mailto:admin@parity.io' }>admin@parity.io</a>. Our general privacy policy can be found here: <a href={ 'https://ethcore.io/legal.html' }>https://ethcore.io/legal.html</a>.</li>
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const postToServer = (query) => {
|
||||||
|
query = stringify(query);
|
||||||
|
return fetch('https://sms-verification.parity.io/?' + query, {
|
||||||
|
method: 'POST', mode: 'cors', cache: 'no-store'
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
return res.json().then((data) => {
|
||||||
|
if (res.ok) {
|
||||||
|
return data.message;
|
||||||
|
}
|
||||||
|
throw new Error(data.message || 'unknown error');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
@ -48,7 +48,11 @@ export default class Contract {
|
|||||||
this._instance[fn.signature] = fn;
|
this._instance[fn.signature] = fn;
|
||||||
});
|
});
|
||||||
|
|
||||||
this._sendSubscriptionChanges();
|
this._subscribedToPendings = false;
|
||||||
|
this._pendingsSubscriptionId = null;
|
||||||
|
|
||||||
|
this._subscribedToBlock = false;
|
||||||
|
this._blockSubscriptionId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get address () {
|
get address () {
|
||||||
@ -239,44 +243,71 @@ export default class Contract {
|
|||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe (eventName = null, options = {}, callback) {
|
_findEvent (eventName = null) {
|
||||||
return new Promise((resolve, reject) => {
|
const event = eventName
|
||||||
let event = null;
|
? this._events.find((evt) => evt.name === eventName)
|
||||||
|
: null;
|
||||||
|
|
||||||
if (eventName) {
|
if (eventName && !event) {
|
||||||
event = this._events.find((evt) => evt.name === eventName);
|
const events = this._events.map((evt) => evt.name).join(', ');
|
||||||
|
throw new Error(`${eventName} is not a valid eventName, subscribe using one of ${events} (or null to include all)`);
|
||||||
|
}
|
||||||
|
|
||||||
if (!event) {
|
return event;
|
||||||
const events = this._events.map((evt) => evt.name).join(', ');
|
}
|
||||||
reject(new Error(`${eventName} is not a valid eventName, subscribe using one of ${events} (or null to include all)`));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._subscribe(event, options, callback).then(resolve).catch(reject);
|
_createEthFilter (event = null, _options) {
|
||||||
|
const optionTopics = _options.topics || [];
|
||||||
|
const signature = event && event.signature || null;
|
||||||
|
|
||||||
|
// If event provided, remove the potential event signature
|
||||||
|
// as the first element of the topics
|
||||||
|
const topics = signature
|
||||||
|
? [ signature ].concat(optionTopics.filter((t, idx) => idx > 0 || t !== signature))
|
||||||
|
: optionTopics;
|
||||||
|
|
||||||
|
const options = Object.assign({}, _options, {
|
||||||
|
address: this._address,
|
||||||
|
topics
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return this._api.eth.newFilter(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribe (eventName = null, options = {}, callback) {
|
||||||
|
try {
|
||||||
|
const event = this._findEvent(eventName);
|
||||||
|
return this._subscribe(event, options, callback);
|
||||||
|
} catch (e) {
|
||||||
|
return Promise.reject(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_subscribe (event = null, _options, callback) {
|
_subscribe (event = null, _options, callback) {
|
||||||
const subscriptionId = nextSubscriptionId++;
|
const subscriptionId = nextSubscriptionId++;
|
||||||
const options = Object.assign({}, _options, {
|
const { skipInitFetch } = _options;
|
||||||
address: this._address,
|
delete _options['skipInitFetch'];
|
||||||
topics: [event ? event.signature : null]
|
|
||||||
});
|
|
||||||
|
|
||||||
return this._api.eth
|
return this
|
||||||
.newFilter(options)
|
._createEthFilter(event, _options)
|
||||||
.then((filterId) => {
|
.then((filterId) => {
|
||||||
|
this._subscriptions[subscriptionId] = {
|
||||||
|
options: _options,
|
||||||
|
callback,
|
||||||
|
filterId
|
||||||
|
};
|
||||||
|
|
||||||
|
if (skipInitFetch) {
|
||||||
|
this._subscribeToChanges();
|
||||||
|
return subscriptionId;
|
||||||
|
}
|
||||||
|
|
||||||
return this._api.eth
|
return this._api.eth
|
||||||
.getFilterLogs(filterId)
|
.getFilterLogs(filterId)
|
||||||
.then((logs) => {
|
.then((logs) => {
|
||||||
callback(null, this.parseEventLogs(logs));
|
callback(null, this.parseEventLogs(logs));
|
||||||
this._subscriptions[subscriptionId] = {
|
|
||||||
options,
|
|
||||||
callback,
|
|
||||||
filterId
|
|
||||||
};
|
|
||||||
|
|
||||||
|
this._subscribeToChanges();
|
||||||
return subscriptionId;
|
return subscriptionId;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -285,19 +316,89 @@ export default class Contract {
|
|||||||
unsubscribe (subscriptionId) {
|
unsubscribe (subscriptionId) {
|
||||||
return this._api.eth
|
return this._api.eth
|
||||||
.uninstallFilter(this._subscriptions[subscriptionId].filterId)
|
.uninstallFilter(this._subscriptions[subscriptionId].filterId)
|
||||||
.then(() => {
|
|
||||||
delete this._subscriptions[subscriptionId];
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('unsubscribe', error);
|
console.error('unsubscribe', error);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
delete this._subscriptions[subscriptionId];
|
||||||
|
this._unsubscribeFromChanges();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_sendSubscriptionChanges = () => {
|
_subscribeToChanges = () => {
|
||||||
const subscriptions = Object.values(this._subscriptions);
|
const subscriptions = Object.values(this._subscriptions);
|
||||||
const timeout = () => setTimeout(this._sendSubscriptionChanges, 1000);
|
|
||||||
|
|
||||||
Promise
|
const pendingSubscriptions = subscriptions
|
||||||
|
.filter((s) => s.options.toBlock && s.options.toBlock === 'pending');
|
||||||
|
|
||||||
|
const otherSubscriptions = subscriptions
|
||||||
|
.filter((s) => !(s.options.toBlock && s.options.toBlock === 'pending'));
|
||||||
|
|
||||||
|
if (pendingSubscriptions.length > 0 && !this._subscribedToPendings) {
|
||||||
|
this._subscribedToPendings = true;
|
||||||
|
this._subscribeToPendings();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (otherSubscriptions.length > 0 && !this._subscribedToBlock) {
|
||||||
|
this._subscribedToBlock = true;
|
||||||
|
this._subscribeToBlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_unsubscribeFromChanges = () => {
|
||||||
|
const subscriptions = Object.values(this._subscriptions);
|
||||||
|
|
||||||
|
const pendingSubscriptions = subscriptions
|
||||||
|
.filter((s) => s.options.toBlock && s.options.toBlock === 'pending');
|
||||||
|
|
||||||
|
const otherSubscriptions = subscriptions
|
||||||
|
.filter((s) => !(s.options.toBlock && s.options.toBlock === 'pending'));
|
||||||
|
|
||||||
|
if (pendingSubscriptions.length === 0 && this._subscribedToPendings) {
|
||||||
|
this._subscribedToPendings = false;
|
||||||
|
clearTimeout(this._pendingsSubscriptionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (otherSubscriptions.length === 0 && this._subscribedToBlock) {
|
||||||
|
this._subscribedToBlock = false;
|
||||||
|
this._api.unsubscribe(this._blockSubscriptionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_subscribeToBlock = () => {
|
||||||
|
this._api
|
||||||
|
.subscribe('eth_blockNumber', (error) => {
|
||||||
|
if (error) {
|
||||||
|
console.error('::_subscribeToBlock', error, error && error.stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
const subscriptions = Object.values(this._subscriptions)
|
||||||
|
.filter((s) => !(s.options.toBlock && s.options.toBlock === 'pending'));
|
||||||
|
|
||||||
|
this._sendSubscriptionChanges(subscriptions);
|
||||||
|
})
|
||||||
|
.then((blockSubId) => {
|
||||||
|
this._blockSubscriptionId = blockSubId;
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.error('::_subscribeToBlock', e, e && e.stack);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_subscribeToPendings = () => {
|
||||||
|
const subscriptions = Object.values(this._subscriptions)
|
||||||
|
.filter((s) => s.options.toBlock && s.options.toBlock === 'pending');
|
||||||
|
|
||||||
|
const timeout = () => setTimeout(() => this._subscribeToPendings(), 1000);
|
||||||
|
|
||||||
|
this._sendSubscriptionChanges(subscriptions)
|
||||||
|
.then(() => {
|
||||||
|
this._pendingsSubscriptionId = timeout();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_sendSubscriptionChanges = (subscriptions) => {
|
||||||
|
return Promise
|
||||||
.all(
|
.all(
|
||||||
subscriptions.map((subscription) => {
|
subscriptions.map((subscription) => {
|
||||||
return this._api.eth.getFilterChanges(subscription.filterId);
|
return this._api.eth.getFilterChanges(subscription.filterId);
|
||||||
@ -315,12 +416,9 @@ export default class Contract {
|
|||||||
console.error('_sendSubscriptionChanges', error);
|
console.error('_sendSubscriptionChanges', error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
timeout();
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('_sendSubscriptionChanges', error);
|
console.error('_sendSubscriptionChanges', error);
|
||||||
timeout();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -437,6 +437,7 @@ describe('api/contract/Contract', () => {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const logs = [{
|
const logs = [{
|
||||||
address: '0x22bff18ec62281850546a664bb63a5c06ac5f76c',
|
address: '0x22bff18ec62281850546a664bb63a5c06ac5f76c',
|
||||||
blockHash: '0xa9280530a3b47bee2fc80f2862fd56502ae075350571d724d6442ea4c597347b',
|
blockHash: '0xa9280530a3b47bee2fc80f2862fd56502ae075350571d724d6442ea4c597347b',
|
||||||
@ -450,6 +451,7 @@ describe('api/contract/Contract', () => {
|
|||||||
transactionHash: '0xca16f537d761d13e4e80953b754e2b15541f267d6cad9381f750af1bae1e4917',
|
transactionHash: '0xca16f537d761d13e4e80953b754e2b15541f267d6cad9381f750af1bae1e4917',
|
||||||
transactionIndex: '0x0'
|
transactionIndex: '0x0'
|
||||||
}];
|
}];
|
||||||
|
|
||||||
const parsed = [{
|
const parsed = [{
|
||||||
address: '0x22bfF18ec62281850546a664bb63a5C06AC5F76C',
|
address: '0x22bfF18ec62281850546a664bb63a5C06AC5F76C',
|
||||||
blockHash: '0xa9280530a3b47bee2fc80f2862fd56502ae075350571d724d6442ea4c597347b',
|
blockHash: '0xa9280530a3b47bee2fc80f2862fd56502ae075350571d724d6442ea4c597347b',
|
||||||
@ -466,11 +468,13 @@ describe('api/contract/Contract', () => {
|
|||||||
sender: { type: 'address', value: '0x63Cf90D3f0410092FC0fca41846f596223979195' }
|
sender: { type: 'address', value: '0x63Cf90D3f0410092FC0fca41846f596223979195' }
|
||||||
},
|
},
|
||||||
topics: [
|
topics: [
|
||||||
'0x954ba6c157daf8a26539574ffa64203c044691aa57251af95f4b48d85ec00dd5', '0x0000000000000000000000000000000000000000000000000001000000004fe0'
|
'0x954ba6c157daf8a26539574ffa64203c044691aa57251af95f4b48d85ec00dd5',
|
||||||
|
'0x0000000000000000000000000000000000000000000000000001000000004fe0'
|
||||||
],
|
],
|
||||||
transactionHash: '0xca16f537d761d13e4e80953b754e2b15541f267d6cad9381f750af1bae1e4917',
|
transactionHash: '0xca16f537d761d13e4e80953b754e2b15541f267d6cad9381f750af1bae1e4917',
|
||||||
transactionIndex: new BigNumber(0)
|
transactionIndex: new BigNumber(0)
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let contract;
|
let contract;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -496,18 +500,19 @@ describe('api/contract/Contract', () => {
|
|||||||
scope = mockHttp([
|
scope = mockHttp([
|
||||||
{ method: 'eth_newFilter', reply: { result: '0x123' } },
|
{ method: 'eth_newFilter', reply: { result: '0x123' } },
|
||||||
{ method: 'eth_getFilterLogs', reply: { result: logs } },
|
{ method: 'eth_getFilterLogs', reply: { result: logs } },
|
||||||
|
{ method: 'eth_getFilterChanges', reply: { result: logs } },
|
||||||
{ method: 'eth_newFilter', reply: { result: '0x123' } },
|
{ method: 'eth_newFilter', reply: { result: '0x123' } },
|
||||||
{ method: 'eth_getFilterLogs', reply: { result: logs } }
|
{ method: 'eth_getFilterLogs', reply: { result: logs } }
|
||||||
]);
|
]);
|
||||||
cbb = sinon.stub();
|
cbb = sinon.stub();
|
||||||
cbe = sinon.stub();
|
cbe = sinon.stub();
|
||||||
|
|
||||||
return contract.subscribe('Message', {}, cbb);
|
return contract.subscribe('Message', { toBlock: 'pending' }, cbb);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets the subscriptionId returned', () => {
|
it('sets the subscriptionId returned', () => {
|
||||||
return contract
|
return contract
|
||||||
.subscribe('Message', {}, cbe)
|
.subscribe('Message', { toBlock: 'pending' }, cbe)
|
||||||
.then((subscriptionId) => {
|
.then((subscriptionId) => {
|
||||||
expect(subscriptionId).to.equal(1);
|
expect(subscriptionId).to.equal(1);
|
||||||
});
|
});
|
||||||
@ -515,7 +520,7 @@ describe('api/contract/Contract', () => {
|
|||||||
|
|
||||||
it('creates a new filter and retrieves the logs on it', () => {
|
it('creates a new filter and retrieves the logs on it', () => {
|
||||||
return contract
|
return contract
|
||||||
.subscribe('Message', {}, cbe)
|
.subscribe('Message', { toBlock: 'pending' }, cbe)
|
||||||
.then((subscriptionId) => {
|
.then((subscriptionId) => {
|
||||||
expect(scope.isDone()).to.be.true;
|
expect(scope.isDone()).to.be.true;
|
||||||
});
|
});
|
||||||
@ -523,7 +528,7 @@ describe('api/contract/Contract', () => {
|
|||||||
|
|
||||||
it('returns the logs to the callback', () => {
|
it('returns the logs to the callback', () => {
|
||||||
return contract
|
return contract
|
||||||
.subscribe('Message', {}, cbe)
|
.subscribe('Message', { toBlock: 'pending' }, cbe)
|
||||||
.then((subscriptionId) => {
|
.then((subscriptionId) => {
|
||||||
expect(cbe).to.have.been.calledWith(null, parsed);
|
expect(cbe).to.have.been.calledWith(null, parsed);
|
||||||
});
|
});
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
|
import { range } from 'lodash';
|
||||||
|
|
||||||
import { isArray, isHex, isInstanceOf, isString } from '../util/types';
|
import { isArray, isHex, isInstanceOf, isString } from '../util/types';
|
||||||
|
|
||||||
@ -50,14 +51,19 @@ export function inHash (hash) {
|
|||||||
return inHex(hash);
|
return inHex(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function pad (input, length) {
|
||||||
|
const value = inHex(input).substr(2, length * 2);
|
||||||
|
return '0x' + value + range(length * 2 - value.length).map(() => '0').join('');
|
||||||
|
}
|
||||||
|
|
||||||
export function inTopics (_topics) {
|
export function inTopics (_topics) {
|
||||||
let topics = (_topics || [])
|
let topics = (_topics || [])
|
||||||
.filter((topic) => topic)
|
.filter((topic) => topic === null || topic)
|
||||||
.map(inHex);
|
.map((topic) => topic === null ? null : pad(topic, 32));
|
||||||
|
|
||||||
while (topics.length < 4) {
|
// while (topics.length < 4) {
|
||||||
topics.push(null);
|
// topics.push(null);
|
||||||
}
|
// }
|
||||||
|
|
||||||
return topics;
|
return topics;
|
||||||
}
|
}
|
||||||
|
@ -123,6 +123,11 @@ export default class Parity {
|
|||||||
.then((accounts) => (accounts || []).map(outAddress));
|
.then((accounts) => (accounts || []).map(outAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
killAccount (account, password) {
|
||||||
|
return this._transport
|
||||||
|
.execute('parity_killAccount', inAddress(account), password);
|
||||||
|
}
|
||||||
|
|
||||||
listGethAccounts () {
|
listGethAccounts () {
|
||||||
return this._transport
|
return this._transport
|
||||||
.execute('parity_listGethAccounts')
|
.execute('parity_listGethAccounts')
|
||||||
|
@ -59,6 +59,7 @@ export default class Personal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (data.method) {
|
switch (data.method) {
|
||||||
|
case 'parity_killAccount':
|
||||||
case 'parity_importGethAccounts':
|
case 'parity_importGethAccounts':
|
||||||
case 'personal_newAccount':
|
case 'personal_newAccount':
|
||||||
case 'parity_newAccountFromPhrase':
|
case 'parity_newAccountFromPhrase':
|
||||||
|
@ -29,21 +29,33 @@ export default class Ws extends JsonRpcBase {
|
|||||||
this._token = token;
|
this._token = token;
|
||||||
this._messages = {};
|
this._messages = {};
|
||||||
|
|
||||||
this._connecting = true;
|
this._connecting = false;
|
||||||
|
this._connected = false;
|
||||||
this._lastError = null;
|
this._lastError = null;
|
||||||
this._autoConnect = false;
|
this._autoConnect = true;
|
||||||
|
this._retries = 0;
|
||||||
|
this._reconnectTimeoutId = null;
|
||||||
|
|
||||||
this._connect();
|
this._connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateToken (token) {
|
updateToken (token) {
|
||||||
this._token = token;
|
this._token = token;
|
||||||
this._autoConnect = false;
|
this._autoConnect = true;
|
||||||
|
|
||||||
this._connect();
|
this._connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
_connect () {
|
_connect () {
|
||||||
|
if (this._connecting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._reconnectTimeoutId) {
|
||||||
|
window.clearTimeout(this._reconnectTimeoutId);
|
||||||
|
this._reconnectTimeoutId = null;
|
||||||
|
}
|
||||||
|
|
||||||
const time = parseInt(new Date().getTime() / 1000, 10);
|
const time = parseInt(new Date().getTime() / 1000, 10);
|
||||||
const sha3 = keccak_256(`${this._token}:${time}`);
|
const sha3 = keccak_256(`${this._token}:${time}`);
|
||||||
const hash = `${sha3}_${time}`;
|
const hash = `${sha3}_${time}`;
|
||||||
@ -53,6 +65,7 @@ export default class Ws extends JsonRpcBase {
|
|||||||
this._ws.onopen = null;
|
this._ws.onopen = null;
|
||||||
this._ws.onclose = null;
|
this._ws.onclose = null;
|
||||||
this._ws.onmessage = null;
|
this._ws.onmessage = null;
|
||||||
|
this._ws.close();
|
||||||
this._ws = null;
|
this._ws = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,6 +78,27 @@ export default class Ws extends JsonRpcBase {
|
|||||||
this._ws.onopen = this._onOpen;
|
this._ws.onopen = this._onOpen;
|
||||||
this._ws.onclose = this._onClose;
|
this._ws.onclose = this._onClose;
|
||||||
this._ws.onmessage = this._onMessage;
|
this._ws.onmessage = this._onMessage;
|
||||||
|
|
||||||
|
// Get counts in dev mode
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
this._count = 0;
|
||||||
|
this._lastCount = {
|
||||||
|
timestamp: Date.now(),
|
||||||
|
count: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
window.setInterval(() => {
|
||||||
|
const n = this._count - this._lastCount.count;
|
||||||
|
const t = (Date.now() - this._lastCount.timestamp) / 1000;
|
||||||
|
const s = Math.round(1000 * n / t) / 1000;
|
||||||
|
|
||||||
|
if (this._debug) {
|
||||||
|
console.log('::parityWS', `speed: ${s} req/s`, `count: ${this._count}`);
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
window._parityWS = this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onOpen = (event) => {
|
_onOpen = (event) => {
|
||||||
@ -72,6 +106,7 @@ export default class Ws extends JsonRpcBase {
|
|||||||
this._connected = true;
|
this._connected = true;
|
||||||
this._connecting = false;
|
this._connecting = false;
|
||||||
this._autoConnect = true;
|
this._autoConnect = true;
|
||||||
|
this._retries = 0;
|
||||||
|
|
||||||
Object.keys(this._messages)
|
Object.keys(this._messages)
|
||||||
.filter((id) => this._messages[id].queued)
|
.filter((id) => this._messages[id].queued)
|
||||||
@ -79,18 +114,39 @@ export default class Ws extends JsonRpcBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onClose = (event) => {
|
_onClose = (event) => {
|
||||||
console.log('ws:onClose', event);
|
|
||||||
this._connected = false;
|
this._connected = false;
|
||||||
this._connecting = false;
|
this._connecting = false;
|
||||||
|
|
||||||
|
this._lastError = event;
|
||||||
|
|
||||||
if (this._autoConnect) {
|
if (this._autoConnect) {
|
||||||
setTimeout(() => this._connect(), 500);
|
const timeout = this.retryTimeout;
|
||||||
|
|
||||||
|
const time = timeout < 1000
|
||||||
|
? Math.round(timeout) + 'ms'
|
||||||
|
: (Math.round(timeout / 10) / 100) + 's';
|
||||||
|
|
||||||
|
console.log('ws:onClose', `trying again in ${time}...`);
|
||||||
|
|
||||||
|
this._reconnectTimeoutId = setTimeout(() => {
|
||||||
|
this._connect();
|
||||||
|
}, timeout);
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('ws:onClose', event);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onError = (event) => {
|
_onError = (event) => {
|
||||||
console.error('ws:onError', event);
|
// Only print error if the WS is connected
|
||||||
this._lastError = event;
|
// ie. don't print if error == closed
|
||||||
|
window.setTimeout(() => {
|
||||||
|
if (this._connected) {
|
||||||
|
console.error('ws:onError', event);
|
||||||
|
this._lastError = event;
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onMessage = (event) => {
|
_onMessage = (event) => {
|
||||||
@ -127,11 +183,16 @@ export default class Ws extends JsonRpcBase {
|
|||||||
_send = (id) => {
|
_send = (id) => {
|
||||||
const message = this._messages[id];
|
const message = this._messages[id];
|
||||||
|
|
||||||
message.queued = !this._connected;
|
|
||||||
|
|
||||||
if (this._connected) {
|
if (this._connected) {
|
||||||
this._ws.send(message.json);
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
this._count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._ws.send(message.json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message.queued = !this._connected;
|
||||||
|
message.timestamp = Date.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
execute (method, ...params) {
|
execute (method, ...params) {
|
||||||
@ -159,4 +220,27 @@ export default class Ws extends JsonRpcBase {
|
|||||||
get lastError () {
|
get lastError () {
|
||||||
return this._lastError;
|
return this._lastError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exponential Timeout for Retries
|
||||||
|
*
|
||||||
|
* @see http://dthain.blogspot.de/2009/02/exponential-backoff-in-distributed.html
|
||||||
|
*/
|
||||||
|
get retryTimeout () {
|
||||||
|
// R between 1 and 2
|
||||||
|
const R = Math.random() + 1;
|
||||||
|
// Initial timeout (100ms)
|
||||||
|
const T = 100;
|
||||||
|
// Exponential Factor
|
||||||
|
const F = 2;
|
||||||
|
// Max timeout (4s)
|
||||||
|
const M = 4000;
|
||||||
|
// Current number of retries
|
||||||
|
const N = this._retries;
|
||||||
|
|
||||||
|
// Increase retries number
|
||||||
|
this._retries++;
|
||||||
|
|
||||||
|
return Math.min(R * T * Math.pow(F, N), M);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,3 +29,7 @@ export function hex2Ascii (_hex) {
|
|||||||
|
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function asciiToHex (string) {
|
||||||
|
return '0x' + string.split('').map((s) => s.charCodeAt(0).toString(16)).join('');
|
||||||
|
}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import { isAddress as isAddressValid, toChecksumAddress } from '../../abi/util/address';
|
import { isAddress as isAddressValid, toChecksumAddress } from '../../abi/util/address';
|
||||||
import { decodeCallData, decodeMethodInput, methodToAbi } from './decode';
|
import { decodeCallData, decodeMethodInput, methodToAbi } from './decode';
|
||||||
import { bytesToHex, hex2Ascii } from './format';
|
import { bytesToHex, hex2Ascii, asciiToHex } from './format';
|
||||||
import { fromWei, toWei } from './wei';
|
import { fromWei, toWei } from './wei';
|
||||||
import { sha3 } from './sha3';
|
import { sha3 } from './sha3';
|
||||||
import { isArray, isFunction, isHex, isInstanceOf, isString } from './types';
|
import { isArray, isFunction, isHex, isInstanceOf, isString } from './types';
|
||||||
@ -31,6 +31,7 @@ export default {
|
|||||||
isString,
|
isString,
|
||||||
bytesToHex,
|
bytesToHex,
|
||||||
hex2Ascii,
|
hex2Ascii,
|
||||||
|
asciiToHex,
|
||||||
createIdentityImg,
|
createIdentityImg,
|
||||||
decodeCallData,
|
decodeCallData,
|
||||||
decodeMethodInput,
|
decodeMethodInput,
|
||||||
|
@ -21,25 +21,39 @@ export default class Registry {
|
|||||||
this._api = api;
|
this._api = api;
|
||||||
this._contracts = [];
|
this._contracts = [];
|
||||||
this._instance = null;
|
this._instance = null;
|
||||||
|
this._fetching = false;
|
||||||
|
this._queue = [];
|
||||||
|
|
||||||
this.getInstance();
|
this.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
getInstance () {
|
getInstance () {
|
||||||
return new Promise((resolve, reject) => {
|
if (this._instance) {
|
||||||
if (this._instance) {
|
return Promise.resolve(this._instance);
|
||||||
resolve(this._instance);
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._api.parity
|
if (this._fetching) {
|
||||||
.registryAddress()
|
return new Promise((resolve) => {
|
||||||
.then((address) => {
|
this._queue.push({ resolve });
|
||||||
this._instance = this._api.newContract(abis.registry, address).instance;
|
});
|
||||||
resolve(this._instance);
|
}
|
||||||
})
|
|
||||||
.catch(reject);
|
this._fetching = true;
|
||||||
});
|
|
||||||
|
return this._api.parity
|
||||||
|
.registryAddress()
|
||||||
|
.then((address) => {
|
||||||
|
this._fetching = false;
|
||||||
|
this._instance = this._api.newContract(abis.registry, address).instance;
|
||||||
|
|
||||||
|
this._queue.forEach((queued) => {
|
||||||
|
queued.resolve(this._instance);
|
||||||
|
});
|
||||||
|
|
||||||
|
this._queue = [];
|
||||||
|
|
||||||
|
return this._instance;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getContract (_name) {
|
getContract (_name) {
|
||||||
|
@ -14,39 +14,39 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import { stringify } from 'querystring';
|
|
||||||
|
|
||||||
export const checkIfVerified = (contract, account) => {
|
export const checkIfVerified = (contract, account) => {
|
||||||
return contract.instance.certified.call({}, [account]);
|
return contract.instance.certified.call({}, [account]);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const checkIfRequested = (contract, account) => {
|
export const checkIfRequested = (contract, account) => {
|
||||||
return new Promise((resolve, reject) => {
|
let subId = null;
|
||||||
contract.subscribe('Requested', {
|
let resolved = false;
|
||||||
fromBlock: 0, toBlock: 'pending'
|
|
||||||
}, (err, logs) => {
|
|
||||||
if (err) {
|
|
||||||
return reject(err);
|
|
||||||
}
|
|
||||||
const e = logs.find((l) => {
|
|
||||||
return l.type === 'mined' && l.params.who && l.params.who.value === account;
|
|
||||||
});
|
|
||||||
resolve(e ? e.transactionHash : false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const postToServer = (query) => {
|
return new Promise((resolve, reject) => {
|
||||||
query = stringify(query);
|
contract
|
||||||
return fetch('https://sms-verification.parity.io/?' + query, {
|
.subscribe('Requested', {
|
||||||
method: 'POST', mode: 'cors', cache: 'no-store'
|
fromBlock: 0, toBlock: 'pending'
|
||||||
})
|
}, (err, logs) => {
|
||||||
.then((res) => {
|
if (err) {
|
||||||
return res.json().then((data) => {
|
return reject(err);
|
||||||
if (res.ok) {
|
}
|
||||||
return data.message;
|
const e = logs.find((l) => {
|
||||||
}
|
return l.type === 'mined' && l.params.who && l.params.who.value === account;
|
||||||
throw new Error(data.message || 'unknown error');
|
});
|
||||||
});
|
|
||||||
|
resolve(e ? e.transactionHash : false);
|
||||||
|
resolved = true;
|
||||||
|
|
||||||
|
if (subId) {
|
||||||
|
contract.unsubscribe(subId);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((_subId) => {
|
||||||
|
subId = _subId;
|
||||||
|
|
||||||
|
if (resolved) {
|
||||||
|
contract.unsubscribe(subId);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -105,7 +105,7 @@ export function attachInstances () {
|
|||||||
])
|
])
|
||||||
.then(([registryAddress, netChain]) => {
|
.then(([registryAddress, netChain]) => {
|
||||||
const registry = api.newContract(abis.registry, registryAddress).instance;
|
const registry = api.newContract(abis.registry, registryAddress).instance;
|
||||||
isTest = netChain === 'morden' || netChain === 'testnet';
|
isTest = ['morden', 'ropsten', 'testnet'].includes(netChain);
|
||||||
|
|
||||||
console.log(`contract was found at registry=${registryAddress}`);
|
console.log(`contract was found at registry=${registryAddress}`);
|
||||||
console.log(`running on ${netChain}, isTest=${isTest}`);
|
console.log(`running on ${netChain}, isTest=${isTest}`);
|
||||||
|
17
js/src/dapps/dappreg.html
Normal file
17
js/src/dapps/dappreg.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<link rel="icon" href="/parity-logo-black-no-text.png" type="image/png">
|
||||||
|
<title>Dapp Registry</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container"></div>
|
||||||
|
<script src="vendor.js"></script>
|
||||||
|
<script src="commons.js"></script>
|
||||||
|
<script src="/parity-utils/parity.js"></script>
|
||||||
|
<script src="dappreg.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
35
js/src/dapps/dappreg.js
Normal file
35
js/src/dapps/dappreg.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import injectTapEventPlugin from 'react-tap-event-plugin';
|
||||||
|
import { useStrict } from 'mobx';
|
||||||
|
|
||||||
|
injectTapEventPlugin();
|
||||||
|
useStrict(true);
|
||||||
|
|
||||||
|
import Application from './dappreg/Application';
|
||||||
|
|
||||||
|
import '../../assets/fonts/Roboto/font.css';
|
||||||
|
import '../../assets/fonts/RobotoMono/font.css';
|
||||||
|
import './style.css';
|
||||||
|
import './dappreg.html';
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<Application />,
|
||||||
|
document.querySelector('#container')
|
||||||
|
);
|
58
js/src/dapps/dappreg/Application/application.css
Normal file
58
js/src/dapps/dappreg/Application/application.css
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
/* This file is part of Parity.
|
||||||
|
/*
|
||||||
|
/* Parity 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.
|
||||||
|
/*
|
||||||
|
/* Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.body {
|
||||||
|
color: #333;
|
||||||
|
background: #eee;
|
||||||
|
padding: 4.5em 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.apps {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 0.5em;
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 980px;
|
||||||
|
padding: 1.5em;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
font-size: 0.75em;
|
||||||
|
margin: 1em;
|
||||||
|
padding: 1.5em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
background: #44e;
|
||||||
|
border-radius: 0 0 0.25em 0.25em;
|
||||||
|
color: #fff;
|
||||||
|
left: 0;
|
||||||
|
padding: 1em;
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 5em;
|
||||||
|
font-size: 2em;
|
||||||
|
color: #999;
|
||||||
|
}
|
64
js/src/dapps/dappreg/Application/application.js
Normal file
64
js/src/dapps/dappreg/Application/application.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
|
||||||
|
import DappsStore from '../dappsStore';
|
||||||
|
|
||||||
|
import ButtonBar from '../ButtonBar';
|
||||||
|
import Dapp from '../Dapp';
|
||||||
|
import ModalDelete from '../ModalDelete';
|
||||||
|
import ModalRegister from '../ModalRegister';
|
||||||
|
import ModalUpdate from '../ModalUpdate';
|
||||||
|
import SelectDapp from '../SelectDapp';
|
||||||
|
import Warning from '../Warning';
|
||||||
|
import styles from './application.css';
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export default class Application extends Component {
|
||||||
|
dappsStore = DappsStore.instance();
|
||||||
|
|
||||||
|
render () {
|
||||||
|
if (this.dappsStore.isLoading) {
|
||||||
|
return (
|
||||||
|
<div className={ styles.loading }>
|
||||||
|
Loading application
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={ styles.body }>
|
||||||
|
<div className={ styles.header }>
|
||||||
|
DAPP REGISTRY, a global view of distributed applications available on the network. Putting the puzzle together.
|
||||||
|
</div>
|
||||||
|
<div className={ styles.apps }>
|
||||||
|
<SelectDapp />
|
||||||
|
<ButtonBar />
|
||||||
|
<Dapp />
|
||||||
|
</div>
|
||||||
|
<div className={ styles.footer }>
|
||||||
|
{ this.dappsStore.count } applications registered, { this.dappsStore.ownedCount } owned by user
|
||||||
|
</div>
|
||||||
|
<Warning />
|
||||||
|
<ModalDelete />
|
||||||
|
<ModalRegister />
|
||||||
|
<ModalUpdate />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -14,4 +14,4 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
export default from './RequestPendingWeb3';
|
export default from './application';
|
38
js/src/dapps/dappreg/Button/button.css
Normal file
38
js/src/dapps/dappreg/Button/button.css
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
/* This file is part of Parity.
|
||||||
|
/*
|
||||||
|
/* Parity 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.
|
||||||
|
/*
|
||||||
|
/* Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.button {
|
||||||
|
background: #44e;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1em;
|
||||||
|
margin: 1em 0.375em;
|
||||||
|
opacity: 0.85;
|
||||||
|
padding: 0.75em 2em;
|
||||||
|
|
||||||
|
&[disabled] {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: default;
|
||||||
|
background: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-warning="true"] {
|
||||||
|
background: #e44;
|
||||||
|
}
|
||||||
|
}
|
52
js/src/dapps/dappreg/Button/button.js
Normal file
52
js/src/dapps/dappreg/Button/button.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
|
||||||
|
import styles from './button.css';
|
||||||
|
|
||||||
|
export default class Button extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
warning: PropTypes.bool,
|
||||||
|
onClick: PropTypes.func.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { className, disabled, label, warning } = this.props;
|
||||||
|
const classes = `${styles.button} ${className}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={ classes }
|
||||||
|
data-warning={ warning }
|
||||||
|
disabled={ disabled }
|
||||||
|
onClick={ this.onClick }>
|
||||||
|
{ label }
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick = (event) => {
|
||||||
|
if (this.props.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.props.onClick(event);
|
||||||
|
}
|
||||||
|
}
|
@ -14,4 +14,4 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
export default from './RequestFinishedWeb3';
|
export default from './button';
|
21
js/src/dapps/dappreg/ButtonBar/buttonBar.css
Normal file
21
js/src/dapps/dappreg/ButtonBar/buttonBar.css
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
/* This file is part of Parity.
|
||||||
|
/*
|
||||||
|
/* Parity 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.
|
||||||
|
/*
|
||||||
|
/* Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.buttonbar {
|
||||||
|
text-align: center;
|
||||||
|
margin: 1em 0 0 0;
|
||||||
|
}
|
101
js/src/dapps/dappreg/ButtonBar/buttonBar.js
Normal file
101
js/src/dapps/dappreg/ButtonBar/buttonBar.js
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
|
||||||
|
import DappsStore from '../dappsStore';
|
||||||
|
import ModalStore from '../modalStore';
|
||||||
|
|
||||||
|
import Button from '../Button';
|
||||||
|
import styles from './buttonBar.css';
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export default class ButtonBar extends Component {
|
||||||
|
dappsStore = DappsStore.instance();
|
||||||
|
modalStore = ModalStore.instance();
|
||||||
|
|
||||||
|
render () {
|
||||||
|
let buttons = [];
|
||||||
|
|
||||||
|
if (this.dappsStore.isEditing || this.dappsStore.isNew) {
|
||||||
|
buttons = [
|
||||||
|
<Button
|
||||||
|
key='cancel'
|
||||||
|
label='Cancel'
|
||||||
|
warning
|
||||||
|
onClick={ this.onCancelClick } />,
|
||||||
|
<Button
|
||||||
|
key='save'
|
||||||
|
label={ this.dappsStore.isNew ? 'Register' : 'Update' }
|
||||||
|
disabled={ !this.dappsStore.canSave }
|
||||||
|
onClick={ this.onSaveClick } />
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
buttons = [
|
||||||
|
<Button
|
||||||
|
key='delete'
|
||||||
|
label='Delete'
|
||||||
|
warning
|
||||||
|
disabled={ !this.dappsStore.currentApp.isOwner && !this.dappsStore.isContractOwner }
|
||||||
|
onClick={ this.onDeleteClick } />,
|
||||||
|
<Button
|
||||||
|
key='edit'
|
||||||
|
label='Edit'
|
||||||
|
disabled={ !this.dappsStore.currentApp.isOwner }
|
||||||
|
onClick={ this.onEditClick } />,
|
||||||
|
<Button
|
||||||
|
key='new'
|
||||||
|
label='New'
|
||||||
|
onClick={ this.onNewClick } />
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={ styles.buttonbar }>
|
||||||
|
{ buttons }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onCancelClick = () => {
|
||||||
|
if (this.dappsStore.isEditing) {
|
||||||
|
this.dappsStore.setEditing(false);
|
||||||
|
} else {
|
||||||
|
this.dappsStore.setNew(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeleteClick = () => {
|
||||||
|
this.modalStore.showDelete();
|
||||||
|
}
|
||||||
|
|
||||||
|
onEditClick = () => {
|
||||||
|
this.dappsStore.setEditing(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
onNewClick = () => {
|
||||||
|
this.dappsStore.setNew(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
onSaveClick = () => {
|
||||||
|
if (this.dappsStore.isEditing) {
|
||||||
|
this.modalStore.showUpdate();
|
||||||
|
} else {
|
||||||
|
this.modalStore.showRegister();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
js/src/dapps/dappreg/ButtonBar/index.js
Normal file
17
js/src/dapps/dappreg/ButtonBar/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
export default from './buttonBar';
|
19
js/src/dapps/dappreg/Dapp/dapp.css
Normal file
19
js/src/dapps/dappreg/Dapp/dapp.css
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
/* This file is part of Parity.
|
||||||
|
/*
|
||||||
|
/* Parity 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.
|
||||||
|
/*
|
||||||
|
/* Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.app {
|
||||||
|
}
|
162
js/src/dapps/dappreg/Dapp/dapp.js
Normal file
162
js/src/dapps/dappreg/Dapp/dapp.js
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
|
||||||
|
import { api } from '../parity';
|
||||||
|
import DappsStore from '../dappsStore';
|
||||||
|
|
||||||
|
import Input from '../Input';
|
||||||
|
import SelectAccount from '../SelectAccount';
|
||||||
|
import styles from './dapp.css';
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export default class Dapp extends Component {
|
||||||
|
dappsStore = DappsStore.instance();
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const app = this.dappsStore.isNew || this.dappsStore.isEditing
|
||||||
|
? this.dappsStore.wipApp
|
||||||
|
: this.dappsStore.currentApp;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={ styles.app }>
|
||||||
|
{ this.dappsStore.isNew ? this.renderOwnerSelect(app) : this.renderOwnerStatic(app) }
|
||||||
|
{ this.renderInputs(app) }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderInputs (app) {
|
||||||
|
if (this.dappsStore.isNew) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
this.renderHashInput(app, 'image', 'Image hash, as generated by Githubhint', true),
|
||||||
|
this.renderHashInput(app, 'manifest', 'Manifest hash, as generated by Githubhint'),
|
||||||
|
this.renderHashInput(app, 'content', 'Content hash, as generated by Githubhint')
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
renderOwnerSelect (app) {
|
||||||
|
const overlayImage = (
|
||||||
|
<img
|
||||||
|
className={ styles.overlayImage }
|
||||||
|
src={ api.util.createIdentityImg(this.dappsStore.currentAccount.address, 4) } />
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
hint={ this.dappsStore.currentAccount.address }
|
||||||
|
label='Owner, select the application owner and editor'
|
||||||
|
overlay={ overlayImage }>
|
||||||
|
<SelectAccount />
|
||||||
|
</Input>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderOwnerStatic (app) {
|
||||||
|
const overlayImage = (
|
||||||
|
<img
|
||||||
|
className={ styles.overlayImage }
|
||||||
|
src={ api.util.createIdentityImg(app.owner, 4) } />
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
hint={ app.owner }
|
||||||
|
label='Owner, the application owner and editor'
|
||||||
|
overlay={ overlayImage }>
|
||||||
|
<input value={ app.ownerName } readOnly />
|
||||||
|
</Input>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderHashInput (app, type, label, withImage = false) {
|
||||||
|
const onChange = (event) => this.onChangeHash(event, type);
|
||||||
|
const hash = app[`${type}Hash`];
|
||||||
|
|
||||||
|
let overlayImage = null;
|
||||||
|
if (withImage && hash) {
|
||||||
|
overlayImage = (
|
||||||
|
<img
|
||||||
|
className={ styles.overlayImage }
|
||||||
|
src={ `/api/content/${hash.substr(2)}` } />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
hint={ app[`${type}Error`] || app[`${type}Url`] || '...' }
|
||||||
|
label={ label }
|
||||||
|
key={ `${type}Edit` }
|
||||||
|
overlay={ overlayImage }>
|
||||||
|
<input
|
||||||
|
value={ app[`${type}Hash`] || '' }
|
||||||
|
data-dirty={ app[`${type}Changed`] }
|
||||||
|
data-error={ !!app[`${type}Error`] }
|
||||||
|
readOnly={ !this.dappsStore.isEditing && !this.dappsStore.isNew }
|
||||||
|
onChange={ onChange } />
|
||||||
|
</Input>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeHash (event, type) {
|
||||||
|
if (!this.dappsStore.isNew && !this.dappsStore.isEditing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hash = event.target.value;
|
||||||
|
let changed = false;
|
||||||
|
let url = null;
|
||||||
|
|
||||||
|
if (this.dappsStore.isNew) {
|
||||||
|
if (hash && hash.length) {
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.dappsStore.currentApp[`${type}Hash`] !== hash) {
|
||||||
|
changed = true;
|
||||||
|
} else {
|
||||||
|
url = this.dappsStore.currentApp[`${type}Url`];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dappsStore.editWip({
|
||||||
|
[`${type}Changed`]: changed,
|
||||||
|
[`${type}Error`]: null,
|
||||||
|
[`${type}Hash`]: hash,
|
||||||
|
[`${type}Url`]: changed ? 'Resolving url from hash' : url
|
||||||
|
});
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
if (hash.length) {
|
||||||
|
this.dappsStore
|
||||||
|
.lookupHash(hash)
|
||||||
|
.then((url) => {
|
||||||
|
this.dappsStore.editWip({
|
||||||
|
[`${type}Error`]: url ? null : 'Unable to resolve url',
|
||||||
|
[`${type}Url`]: url
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.dappsStore.editWip({ [`${type}Url`]: null });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
js/src/dapps/dappreg/Dapp/index.js
Normal file
17
js/src/dapps/dappreg/Dapp/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
export default from './dapp';
|
17
js/src/dapps/dappreg/Input/index.js
Normal file
17
js/src/dapps/dappreg/Input/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
export default from './input';
|
92
js/src/dapps/dappreg/Input/input.css
Normal file
92
js/src/dapps/dappreg/Input/input.css
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
/* This file is part of Parity.
|
||||||
|
/*
|
||||||
|
/* Parity 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.
|
||||||
|
/*
|
||||||
|
/* Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.input {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
input, select {
|
||||||
|
background: rgba(255, 255, 255, 0.85);
|
||||||
|
border: 4px solid rgba(223, 223, 223, 0.85);
|
||||||
|
border-radius: 0.25em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: #333;
|
||||||
|
font-size: 1em;
|
||||||
|
margin: 0.25em 0 0.25em 0;
|
||||||
|
padding: 0.5em 0.5em 1.5em 0.5em;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
padding-bottom: 1.5em;
|
||||||
|
|
||||||
|
&[data-dirty="true"] {
|
||||||
|
background: rgba(255, 255, 203, 0.85);
|
||||||
|
border-color: rgba(203, 203, 151, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-error="true"] {
|
||||||
|
background: rgba(255, 223, 223, 0.85) !important;
|
||||||
|
border-color: rgba(223, 191, 191, 0.85) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[readonly] {
|
||||||
|
background: rgba(239, 239, 239, 0.85);
|
||||||
|
border-color: rgba(223, 223, 223, 0.85);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
color: #888;
|
||||||
|
display: block;
|
||||||
|
font-size: 0.75em;
|
||||||
|
margin-top: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
-moz-appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
height: 58px;
|
||||||
|
|
||||||
|
&[disabled] {
|
||||||
|
background: rgba(239, 239, 239, 0.85);
|
||||||
|
border-color: rgba(223, 223, 223, 0.85);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint {
|
||||||
|
color: #888;
|
||||||
|
display: block;
|
||||||
|
font-size: 0.75em;
|
||||||
|
position: absolute;
|
||||||
|
right: 52px;
|
||||||
|
text-align: right;
|
||||||
|
top: 52px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
right: 10px;
|
||||||
|
position: absolute;
|
||||||
|
top: 30px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-radius: 50%;
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
js/src/dapps/dappreg/Input/input.js
Normal file
47
js/src/dapps/dappreg/Input/input.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
|
||||||
|
import styles from './input.css';
|
||||||
|
|
||||||
|
export default class Input extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
children: PropTypes.node.isRequired,
|
||||||
|
hint: PropTypes.string,
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
overlay: PropTypes.node
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { children, hint, label, overlay } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={ styles.input }>
|
||||||
|
<label>
|
||||||
|
{ label }
|
||||||
|
</label>
|
||||||
|
{ children }
|
||||||
|
<div className={ styles.hint }>
|
||||||
|
{ hint }
|
||||||
|
</div>
|
||||||
|
<div className={ styles.overlay }>
|
||||||
|
{ overlay }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
17
js/src/dapps/dappreg/Modal/index.js
Normal file
17
js/src/dapps/dappreg/Modal/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
export default from './modal';
|
116
js/src/dapps/dappreg/Modal/modal.css
Normal file
116
js/src/dapps/dappreg/Modal/modal.css
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
/* This file is part of Parity.
|
||||||
|
/*
|
||||||
|
/* Parity 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.
|
||||||
|
/*
|
||||||
|
/* Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
.body {
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
text-align: center;
|
||||||
|
z-index: 50;
|
||||||
|
|
||||||
|
.dialog {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 0 0 0.25em 0.25em;
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 840px;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
line-height: 1.5em;
|
||||||
|
padding: 2em;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.section {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
color: #f44;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.section+.section {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
padding: 0.5em 1.625em;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
background: #44e;
|
||||||
|
color: #fff;
|
||||||
|
opacity: 0.85;
|
||||||
|
padding: 1em;
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
background: #e44;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
background: rgba(204, 204, 204, 0.7);
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 49;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.account {
|
||||||
|
div {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint {
|
||||||
|
display: block !important;
|
||||||
|
color: #888;
|
||||||
|
font-size: 0.75em;
|
||||||
|
margin-top: -0.5em;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heading {
|
||||||
|
color: #888;
|
||||||
|
font-size: 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.light {
|
||||||
|
color: #888;
|
||||||
|
}
|
66
js/src/dapps/dappreg/Modal/modal.js
Normal file
66
js/src/dapps/dappreg/Modal/modal.js
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
|
||||||
|
import styles from './modal.css';
|
||||||
|
|
||||||
|
export default class Modal extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
buttons: PropTypes.node,
|
||||||
|
children: PropTypes.node,
|
||||||
|
error: PropTypes.object,
|
||||||
|
header: PropTypes.string
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { children, buttons, error, header } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={ styles.modal }>
|
||||||
|
<div className={ styles.overlay } />
|
||||||
|
<div className={ styles.body }>
|
||||||
|
<div className={ styles.dialog }>
|
||||||
|
<div className={ `${styles.header} ${error ? styles.error : ''}` }>
|
||||||
|
{ header }
|
||||||
|
</div>
|
||||||
|
<div className={ styles.content }>
|
||||||
|
{ error ? this.renderError() : children }
|
||||||
|
</div>
|
||||||
|
<div className={ styles.footer }>
|
||||||
|
{ buttons }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderError () {
|
||||||
|
const { error } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className={ styles.section }>
|
||||||
|
Your operation failed to complete sucessfully. The following error was returned:
|
||||||
|
</div>
|
||||||
|
<div className={ `${styles.section} ${styles.error}` }>
|
||||||
|
{ error.toString() }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
17
js/src/dapps/dappreg/ModalDelete/index.js
Normal file
17
js/src/dapps/dappreg/ModalDelete/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
export default from './modalDelete';
|
159
js/src/dapps/dappreg/ModalDelete/modalDelete.js
Normal file
159
js/src/dapps/dappreg/ModalDelete/modalDelete.js
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
|
||||||
|
import { api } from '../parity';
|
||||||
|
import DappsStore from '../dappsStore';
|
||||||
|
import ModalStore from '../modalStore';
|
||||||
|
|
||||||
|
import Button from '../Button';
|
||||||
|
import Modal from '../Modal';
|
||||||
|
|
||||||
|
import styles from '../Modal/modal.css';
|
||||||
|
|
||||||
|
const HEADERS = [
|
||||||
|
'Error During Deletion',
|
||||||
|
'Confirm Application Deletion',
|
||||||
|
'Waiting for Signer Confirmation',
|
||||||
|
'Waiting for Transaction Receipt',
|
||||||
|
'Deletion Completed'
|
||||||
|
];
|
||||||
|
const STEP_ERROR = 0;
|
||||||
|
const STEP_CONFIRM = 1;
|
||||||
|
const STEP_SIGNER = 2;
|
||||||
|
const STEP_TXRECEIPT = 3;
|
||||||
|
const STEP_DONE = 4;
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export default class ModalDelete extends Component {
|
||||||
|
dappsStore = DappsStore.instance();
|
||||||
|
modalStore = ModalStore.instance();
|
||||||
|
|
||||||
|
render () {
|
||||||
|
if (!this.modalStore.showingDelete) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
buttons={ this.renderButtons() }
|
||||||
|
error={ this.modalStore.errorDelete }
|
||||||
|
header={ HEADERS[this.modalStore.stepDelete] }>
|
||||||
|
{ this.renderStep() }
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderButtons () {
|
||||||
|
switch (this.modalStore.stepDelete) {
|
||||||
|
case STEP_ERROR:
|
||||||
|
case STEP_DONE:
|
||||||
|
return [
|
||||||
|
<Button
|
||||||
|
key='close'
|
||||||
|
label='Close'
|
||||||
|
onClick={ this.onClickClose } />
|
||||||
|
];
|
||||||
|
case STEP_CONFIRM:
|
||||||
|
return [
|
||||||
|
<Button
|
||||||
|
key='cancel'
|
||||||
|
label='No, Cancel'
|
||||||
|
onClick={ this.onClickClose } />,
|
||||||
|
<Button
|
||||||
|
key='delete'
|
||||||
|
label='Yes, Delete'
|
||||||
|
warning
|
||||||
|
onClick={ this.onClickYes } />
|
||||||
|
];
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStep () {
|
||||||
|
switch (this.modalStore.stepDelete) {
|
||||||
|
case STEP_CONFIRM:
|
||||||
|
return this.renderStepConfirm();
|
||||||
|
case STEP_SIGNER:
|
||||||
|
return this.renderStepWait('Waiting for transaction confirmation in the Parity secure signer');
|
||||||
|
case STEP_TXRECEIPT:
|
||||||
|
return this.renderStepWait('Waiting for the transaction receipt from the network');
|
||||||
|
case STEP_DONE:
|
||||||
|
return this.renderStepCompleted();
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStepCompleted () {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className={ styles.section }>
|
||||||
|
Your application has been removed from the registry.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStepConfirm () {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className={ styles.section }>
|
||||||
|
You are about to remove a distributed application from the registry, the details of this application is given below. Removal does not return any fees, however the application will not be available to users anymore.
|
||||||
|
</div>
|
||||||
|
<div className={ styles.section }>
|
||||||
|
<div className={ styles.heading }>
|
||||||
|
Owner account
|
||||||
|
</div>
|
||||||
|
<div className={ styles.account }>
|
||||||
|
<img src={ api.util.createIdentityImg(this.dappsStore.currentApp.owner, 3) } />
|
||||||
|
<div>{ this.dappsStore.currentApp.ownerName }</div>
|
||||||
|
<div className={ styles.address }>{ this.dappsStore.currentApp.owner }</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={ styles.section }>
|
||||||
|
<div className={ styles.heading }>
|
||||||
|
Application identifier
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{ this.dappsStore.currentApp.id }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStepWait (waitingFor) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className={ styles.section }>
|
||||||
|
{ waitingFor }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickClose = () => {
|
||||||
|
this.modalStore.hideDelete();
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickYes = () => {
|
||||||
|
this.modalStore.doDelete();
|
||||||
|
}
|
||||||
|
}
|
17
js/src/dapps/dappreg/ModalRegister/index.js
Normal file
17
js/src/dapps/dappreg/ModalRegister/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
export default from './modalRegister';
|
159
js/src/dapps/dappreg/ModalRegister/modalRegister.js
Normal file
159
js/src/dapps/dappreg/ModalRegister/modalRegister.js
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
|
||||||
|
import { api } from '../parity';
|
||||||
|
import DappsStore from '../dappsStore';
|
||||||
|
import ModalStore from '../modalStore';
|
||||||
|
|
||||||
|
import Button from '../Button';
|
||||||
|
import Modal from '../Modal';
|
||||||
|
|
||||||
|
import styles from '../Modal/modal.css';
|
||||||
|
|
||||||
|
const HEADERS = [
|
||||||
|
'Error During Registration',
|
||||||
|
'Confirm Application Registration',
|
||||||
|
'Waiting for Signer Confirmation',
|
||||||
|
'Waiting for Transaction Receipt',
|
||||||
|
'Registration Completed'
|
||||||
|
];
|
||||||
|
const STEP_ERROR = 0;
|
||||||
|
const STEP_CONFIRM = 1;
|
||||||
|
const STEP_SIGNER = 2;
|
||||||
|
const STEP_TXRECEIPT = 3;
|
||||||
|
const STEP_DONE = 4;
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export default class ModalRegister extends Component {
|
||||||
|
dappsStore = DappsStore.instance();
|
||||||
|
modalStore = ModalStore.instance();
|
||||||
|
|
||||||
|
render () {
|
||||||
|
if (!this.modalStore.showingRegister) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
buttons={ this.renderButtons() }
|
||||||
|
error={ this.modalStore.errorRegister }
|
||||||
|
header={ HEADERS[this.modalStore.stepRegister] }>
|
||||||
|
{ this.renderStep() }
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderButtons () {
|
||||||
|
switch (this.modalStore.stepRegister) {
|
||||||
|
case STEP_ERROR:
|
||||||
|
case STEP_DONE:
|
||||||
|
return [
|
||||||
|
<Button
|
||||||
|
key='close'
|
||||||
|
label='Close'
|
||||||
|
onClick={ this.onClickClose } />
|
||||||
|
];
|
||||||
|
case STEP_CONFIRM:
|
||||||
|
return [
|
||||||
|
<Button
|
||||||
|
key='cancel'
|
||||||
|
label='No, Cancel'
|
||||||
|
onClick={ this.onClickClose } />,
|
||||||
|
<Button
|
||||||
|
key='register'
|
||||||
|
label='Yes, Register'
|
||||||
|
warning
|
||||||
|
onClick={ this.onClickConfirmYes } />
|
||||||
|
];
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStep () {
|
||||||
|
switch (this.modalStore.stepRegister) {
|
||||||
|
case STEP_CONFIRM:
|
||||||
|
return this.renderStepConfirm();
|
||||||
|
case STEP_SIGNER:
|
||||||
|
return this.renderStepWait('Waiting for transaction confirmation in the Parity secure signer');
|
||||||
|
case STEP_TXRECEIPT:
|
||||||
|
return this.renderStepWait('Waiting for the transaction receipt from the network');
|
||||||
|
case STEP_DONE:
|
||||||
|
return this.renderStepCompleted();
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStepCompleted () {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className={ styles.section }>
|
||||||
|
Your application has been registered in the registry.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStepConfirm () {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className={ styles.section }>
|
||||||
|
You are about to register a new distributed application on the network, the details of this application is given below. This will require a non-refundable fee of { api.util.fromWei(this.dappsStore.fee).toFormat(3) }<small>ETH</small>.
|
||||||
|
</div>
|
||||||
|
<div className={ styles.section }>
|
||||||
|
<div className={ styles.heading }>
|
||||||
|
Selected owner account
|
||||||
|
</div>
|
||||||
|
<div className={ styles.account }>
|
||||||
|
<img src={ api.util.createIdentityImg(this.dappsStore.currentAccount.address, 3) } />
|
||||||
|
<div>{ this.dappsStore.currentAccount.name }</div>
|
||||||
|
<div className={ styles.hint }>{ this.dappsStore.currentAccount.address }</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={ styles.section }>
|
||||||
|
<div className={ styles.heading }>
|
||||||
|
Unique assigned application identifier
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{ this.dappsStore.wipApp.id }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStepWait (waitingFor) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className={ styles.section }>
|
||||||
|
{ waitingFor }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickClose = () => {
|
||||||
|
this.modalStore.hideRegister();
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickConfirmYes = () => {
|
||||||
|
this.modalStore.doRegister();
|
||||||
|
}
|
||||||
|
}
|
17
js/src/dapps/dappreg/ModalUpdate/index.js
Normal file
17
js/src/dapps/dappreg/ModalUpdate/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
export default from './modalUpdate';
|
169
js/src/dapps/dappreg/ModalUpdate/modalUpdate.js
Normal file
169
js/src/dapps/dappreg/ModalUpdate/modalUpdate.js
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
|
||||||
|
import DappsStore from '../dappsStore';
|
||||||
|
import ModalStore from '../modalStore';
|
||||||
|
|
||||||
|
import Button from '../Button';
|
||||||
|
import Modal from '../Modal';
|
||||||
|
|
||||||
|
import styles from '../Modal/modal.css';
|
||||||
|
|
||||||
|
const HEADERS = [
|
||||||
|
'Error During Update',
|
||||||
|
'Confirm Application Update',
|
||||||
|
'Waiting for Signer Confirmation',
|
||||||
|
'Waiting for Transaction Receipt',
|
||||||
|
'Update Completed'
|
||||||
|
];
|
||||||
|
const STEP_ERROR = 0;
|
||||||
|
const STEP_CONFIRM = 1;
|
||||||
|
const STEP_SIGNER = 2;
|
||||||
|
const STEP_TXRECEIPT = 3;
|
||||||
|
const STEP_DONE = 4;
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export default class ModalUpdate extends Component {
|
||||||
|
dappsStore = DappsStore.instance();
|
||||||
|
modalStore = ModalStore.instance();
|
||||||
|
|
||||||
|
render () {
|
||||||
|
if (!this.modalStore.showingUpdate) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
buttons={ this.renderButtons() }
|
||||||
|
error={ this.modalStore.errorUpdate }
|
||||||
|
header={ HEADERS[this.modalStore.stepUpdate] }>
|
||||||
|
{ this.renderStep() }
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderButtons () {
|
||||||
|
switch (this.modalStore.stepUpdate) {
|
||||||
|
case STEP_ERROR:
|
||||||
|
case STEP_DONE:
|
||||||
|
return [
|
||||||
|
<Button
|
||||||
|
key='close'
|
||||||
|
label='Close'
|
||||||
|
onClick={ this.onClickClose } />
|
||||||
|
];
|
||||||
|
case STEP_CONFIRM:
|
||||||
|
return [
|
||||||
|
<Button
|
||||||
|
key='cancel'
|
||||||
|
label='No, Cancel'
|
||||||
|
onClick={ this.onClickClose } />,
|
||||||
|
<Button
|
||||||
|
key='delete'
|
||||||
|
label='Yes, Update'
|
||||||
|
warning
|
||||||
|
onClick={ this.onClickYes } />
|
||||||
|
];
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStep () {
|
||||||
|
switch (this.modalStore.stepUpdate) {
|
||||||
|
case STEP_CONFIRM:
|
||||||
|
return this.renderStepConfirm();
|
||||||
|
case STEP_SIGNER:
|
||||||
|
return this.renderStepWait('Waiting for transaction confirmation in the Parity secure signer');
|
||||||
|
case STEP_TXRECEIPT:
|
||||||
|
return this.renderStepWait('Waiting for the transaction receipt from the network');
|
||||||
|
case STEP_DONE:
|
||||||
|
return this.renderStepCompleted();
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStepCompleted () {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className={ styles.section }>
|
||||||
|
Your application metadata has been updated in the registry.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStepConfirm () {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className={ styles.section }>
|
||||||
|
You are about to update the application details in the registry, the details of these updates are given below. Please note that each update will generate a seperate transaction.
|
||||||
|
</div>
|
||||||
|
<div className={ styles.section }>
|
||||||
|
<div className={ styles.heading }>
|
||||||
|
Application identifier
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{ this.dappsStore.wipApp.id }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{ this.renderChanges() }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderChanges () {
|
||||||
|
return ['content', 'image', 'manifest']
|
||||||
|
.filter((type) => this.dappsStore.wipApp[`${type}Changed`])
|
||||||
|
.map((type) => {
|
||||||
|
return (
|
||||||
|
<div className={ styles.section } key={ `${type}Update` }>
|
||||||
|
<div className={ styles.heading }>
|
||||||
|
Updates to { type } hash
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div>{ this.dappsStore.wipApp[`${type}Hash`] || '(removed)' }</div>
|
||||||
|
<div className={ styles.hint }>
|
||||||
|
{ this.dappsStore.wipApp[`${type}Url`] || 'current url to be removed from registry' }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStepWait (waitingFor) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className={ styles.section }>
|
||||||
|
{ waitingFor }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickClose = () => {
|
||||||
|
this.modalStore.hideUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickYes = () => {
|
||||||
|
this.modalStore.doUpdate();
|
||||||
|
}
|
||||||
|
}
|
17
js/src/dapps/dappreg/SelectAccount/index.js
Normal file
17
js/src/dapps/dappreg/SelectAccount/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
export default from './selectAccount';
|
49
js/src/dapps/dappreg/SelectAccount/selectAccount.js
Normal file
49
js/src/dapps/dappreg/SelectAccount/selectAccount.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
|
||||||
|
import DappsStore from '../dappsStore';
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export default class SelectAccount extends Component {
|
||||||
|
dappsStore = DappsStore.instance();
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<select
|
||||||
|
value={ this.dappsStore.currentAccount.address }
|
||||||
|
onChange={ this.onSelect }>
|
||||||
|
{ this.renderOptions() }
|
||||||
|
</select>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderOptions () {
|
||||||
|
return this.dappsStore.accounts.map((account) => {
|
||||||
|
return (
|
||||||
|
<option value={ account.address } key={ account.address }>
|
||||||
|
{ account.name }
|
||||||
|
</option>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelect = (event) => {
|
||||||
|
this.dappsStore.setCurrentAccount(event.target.value);
|
||||||
|
}
|
||||||
|
}
|
17
js/src/dapps/dappreg/SelectDapp/index.js
Normal file
17
js/src/dapps/dappreg/SelectDapp/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
export default from './selectDapp';
|
76
js/src/dapps/dappreg/SelectDapp/selectDapp.js
Normal file
76
js/src/dapps/dappreg/SelectDapp/selectDapp.js
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
|
||||||
|
import DappsStore from '../dappsStore';
|
||||||
|
|
||||||
|
import Input from '../Input';
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export default class SelectDapp extends Component {
|
||||||
|
dappsStore = DappsStore.instance();
|
||||||
|
|
||||||
|
render () {
|
||||||
|
if (this.dappsStore.isNew) {
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
hint='...'
|
||||||
|
label='Application Id, the unique assigned identifier'>
|
||||||
|
<input value={ this.dappsStore.wipApp.id } readOnly />
|
||||||
|
</Input>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let overlayImg = null;
|
||||||
|
if (this.dappsStore.currentApp.imageHash) {
|
||||||
|
overlayImg = (
|
||||||
|
<img src={ `/api/content/${this.dappsStore.currentApp.imageHash.substr(2)}` } />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
hint={ this.dappsStore.currentApp.id }
|
||||||
|
label='Application, the actual application details to show below'
|
||||||
|
overlay={ overlayImg }>
|
||||||
|
<select
|
||||||
|
disabled={ this.dappsStore.isEditing }
|
||||||
|
value={ this.dappsStore.currentApp.id }
|
||||||
|
onChange={ this.onSelect }>
|
||||||
|
{ this.renderOptions() }
|
||||||
|
</select>
|
||||||
|
</Input>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderOptions () {
|
||||||
|
return this.dappsStore.apps.map((app) => {
|
||||||
|
return (
|
||||||
|
<option
|
||||||
|
value={ app.id }
|
||||||
|
key={ app.id }>
|
||||||
|
{ app.name }
|
||||||
|
</option>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelect = (event) => {
|
||||||
|
this.dappsStore.setCurrentApp(event.target.value);
|
||||||
|
}
|
||||||
|
}
|
17
js/src/dapps/dappreg/Warning/index.js
Normal file
17
js/src/dapps/dappreg/Warning/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
export default from './warning';
|
36
js/src/dapps/dappreg/Warning/warning.css
Normal file
36
js/src/dapps/dappreg/Warning/warning.css
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
/* This file is part of Parity.
|
||||||
|
/*
|
||||||
|
/* Parity 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.
|
||||||
|
/*
|
||||||
|
/* Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
background: #f44;
|
||||||
|
border-top-right-radius: 0.25em;
|
||||||
|
bottom: 0;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.75em;
|
||||||
|
left: 0;
|
||||||
|
line-height: 1.5em;
|
||||||
|
opacity: 1;
|
||||||
|
padding: 1.5em;
|
||||||
|
position: fixed;
|
||||||
|
max-width: 540px;
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
|
div+div {
|
||||||
|
margin-top: 1.5em;
|
||||||
|
}
|
||||||
|
}
|
51
js/src/dapps/dappreg/Warning/warning.js
Normal file
51
js/src/dapps/dappreg/Warning/warning.js
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
|
||||||
|
import { api } from '../parity';
|
||||||
|
import DappsStore from '../dappsStore';
|
||||||
|
import ModalStore from '../modalStore';
|
||||||
|
|
||||||
|
import styles from './warning.css';
|
||||||
|
|
||||||
|
@observer
|
||||||
|
export default class Warning extends Component {
|
||||||
|
dappsStore = DappsStore.instance();
|
||||||
|
modalStore = ModalStore.instance();
|
||||||
|
|
||||||
|
render () {
|
||||||
|
if (!this.modalStore.showingWarning) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={ styles.warning } onClick={ this.onClose }>
|
||||||
|
<div>
|
||||||
|
WARNING: Registering a dapp is for developers only. Please ensure you understand the steps needed to develop and deploy applications, should you wish to use this dapp for anything apart from queries.
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
A non-refundable fee of { api.util.fromWei(this.dappsStore.fee).toFormat(3) }<small>ETH</small> is required for any registration.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose = () => {
|
||||||
|
this.modalStore.hideWarning();
|
||||||
|
}
|
||||||
|
}
|
482
js/src/dapps/dappreg/dappsStore.js
Normal file
482
js/src/dapps/dappreg/dappsStore.js
Normal file
@ -0,0 +1,482 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import BigNumber from 'bignumber.js';
|
||||||
|
import { action, computed, observable, transaction } from 'mobx';
|
||||||
|
|
||||||
|
import * as abis from '../../contracts/abi';
|
||||||
|
import builtins from '../../views/Dapps/builtin.json';
|
||||||
|
|
||||||
|
import { api } from './parity';
|
||||||
|
|
||||||
|
let instance = null;
|
||||||
|
|
||||||
|
export default class DappsStore {
|
||||||
|
@observable accounts = [];
|
||||||
|
@observable addresses = [];
|
||||||
|
@observable apps = [];
|
||||||
|
@observable contractOwner = null;
|
||||||
|
@observable currentAccount = null;
|
||||||
|
@observable currentApp = null;
|
||||||
|
@observable count = 0;
|
||||||
|
@observable fee = new BigNumber(0);
|
||||||
|
@observable isContractOwner = false;
|
||||||
|
@observable isEditing = false;
|
||||||
|
@observable isLoading = true;
|
||||||
|
@observable isNew = false;
|
||||||
|
@observable wipApp = null;
|
||||||
|
|
||||||
|
_startTime = Date.now();
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
this._loadDapps();
|
||||||
|
}
|
||||||
|
|
||||||
|
static instance () {
|
||||||
|
if (!instance) {
|
||||||
|
instance = new DappsStore();
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get canSave () {
|
||||||
|
const app = this.wipApp;
|
||||||
|
|
||||||
|
const hasError = app.contentError || app.imageError || app.manifestError;
|
||||||
|
const isDirty = this.isNew || app.contentChanged || app.imageChanged || app.manifestChanged;
|
||||||
|
const isEditMode = this.isEditing || this.isNew;
|
||||||
|
|
||||||
|
return isEditMode && isDirty && !hasError;
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get isCurrentEditable () {
|
||||||
|
return !!this.accounts.find((account) => account.address === this.currentApp.owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get ownedCount () {
|
||||||
|
return (this.apps.filter((app) => app.isOwner) || []).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action copyToWip = () => {
|
||||||
|
let wipApp;
|
||||||
|
|
||||||
|
if (this.isNew) {
|
||||||
|
wipApp = {
|
||||||
|
id: api.util.sha3(`${this._startTime}_${Date.now()}`),
|
||||||
|
contentHash: null,
|
||||||
|
contentUrl: null,
|
||||||
|
imageHash: null,
|
||||||
|
imageUrl: null,
|
||||||
|
manifestHash: null,
|
||||||
|
manifestUrl: null
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const app = this.currentApp;
|
||||||
|
|
||||||
|
wipApp = {
|
||||||
|
id: app.id,
|
||||||
|
contentHash: app.contentHash,
|
||||||
|
contentUrl: app.contentUrl,
|
||||||
|
imageHash: app.imageHash,
|
||||||
|
imageUrl: app.imageUrl,
|
||||||
|
manifestHash: app.manifestHash,
|
||||||
|
manifestUrl: app.manifestUrl,
|
||||||
|
owner: app.owner,
|
||||||
|
ownerName: app.ownerName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.wipApp = Object.assign(wipApp, {
|
||||||
|
contentChanged: false,
|
||||||
|
contentError: null,
|
||||||
|
imageChanged: false,
|
||||||
|
imageError: null,
|
||||||
|
manifestChanged: false,
|
||||||
|
manifestError: null
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.wipApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action editWip = (details) => {
|
||||||
|
if (this.isNew || this.isEditing) {
|
||||||
|
transaction(() => {
|
||||||
|
Object
|
||||||
|
.keys(details)
|
||||||
|
.forEach((key) => {
|
||||||
|
this.wipApp[key] = details[key];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.wipApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action sortApps = (apps = this.apps) => {
|
||||||
|
transaction(() => {
|
||||||
|
const ownApps = apps
|
||||||
|
.filter((app) => app.isOwner)
|
||||||
|
.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
const otherApps = apps
|
||||||
|
.filter((app) => !app.isOwner)
|
||||||
|
.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
|
||||||
|
this.apps = ownApps.concat(otherApps);
|
||||||
|
this.currentApp = this.apps[0];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setApps = (apps) => {
|
||||||
|
this.sortApps(apps.filter((app) => {
|
||||||
|
const bnid = new BigNumber(app.id);
|
||||||
|
return bnid.gt(0);
|
||||||
|
}));
|
||||||
|
|
||||||
|
return this.apps;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action _addApp = (app) => {
|
||||||
|
transaction(() => {
|
||||||
|
this.setApps(this.apps.concat([app]));
|
||||||
|
this.setCurrentApp(app.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action addApp = (appId, account) => {
|
||||||
|
this
|
||||||
|
._loadDapp({
|
||||||
|
id: appId,
|
||||||
|
isOwner: true,
|
||||||
|
name: `- ${appId}`,
|
||||||
|
owner: account.address,
|
||||||
|
ownerName: account.name
|
||||||
|
})
|
||||||
|
.then(this._addApp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action refreshApp = (appId) => {
|
||||||
|
this._loadDapp(this.apps.find((app) => app.id === appId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@action removeApp = (appId) => {
|
||||||
|
this.setApps(this.apps.filter((app) => app.id !== appId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setAppInfo = (app, info) => {
|
||||||
|
transaction(() => {
|
||||||
|
Object.keys(info).forEach((key) => {
|
||||||
|
app[key] = info[key];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setAccounts = (accountsInfo) => {
|
||||||
|
transaction(() => {
|
||||||
|
this.addresses = Object
|
||||||
|
.keys(accountsInfo)
|
||||||
|
.map((address) => {
|
||||||
|
const account = accountsInfo[address];
|
||||||
|
account.address = address;
|
||||||
|
return account;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.accounts = this.addresses.filter((account) => account.uuid);
|
||||||
|
this.currentAccount = this.accounts[0];
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.accounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setContractOwner = (contractOwner) => {
|
||||||
|
transaction(() => {
|
||||||
|
this.contractOwner = contractOwner;
|
||||||
|
this.isContractOwner = !!this.accounts.find((account) => account.address === contractOwner);
|
||||||
|
});
|
||||||
|
return contractOwner;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setCurrentApp = (id) => {
|
||||||
|
this.currentApp = this.apps.find((app) => app.id === id);
|
||||||
|
return this.currentApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setCurrentAccount = (address) => {
|
||||||
|
this.currentAccount = this.accounts.find((account) => account.address === address);
|
||||||
|
return this.currentAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setCount = (count) => {
|
||||||
|
this.count = count;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setEditing = (mode) => {
|
||||||
|
transaction(() => {
|
||||||
|
this.isEditing = mode;
|
||||||
|
this.copyToWip();
|
||||||
|
});
|
||||||
|
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setFee = (fee) => {
|
||||||
|
this.fee = fee;
|
||||||
|
return fee;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setLoading = (loading) => {
|
||||||
|
this.isLoading = loading;
|
||||||
|
return loading;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setNew = (mode) => {
|
||||||
|
transaction(() => {
|
||||||
|
this.isNew = mode;
|
||||||
|
this.copyToWip();
|
||||||
|
});
|
||||||
|
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
lookupHash (hash) {
|
||||||
|
return this._retrieveUrl(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getCount () {
|
||||||
|
return this._instanceReg
|
||||||
|
.count.call()
|
||||||
|
.then((count) => {
|
||||||
|
this.setCount(count.toNumber());
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Store:getCount', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_getFee () {
|
||||||
|
return this._instanceReg
|
||||||
|
.fee.call()
|
||||||
|
.then(this.setFee)
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Store:getFee', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_getOwner () {
|
||||||
|
return this._instanceReg
|
||||||
|
.owner.call()
|
||||||
|
.then(this.setContractOwner)
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Store:getOwner', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadDapps () {
|
||||||
|
return this._loadRegistry()
|
||||||
|
.then(() => Promise.all([
|
||||||
|
this._attachContracts(),
|
||||||
|
this._loadAccounts()
|
||||||
|
]))
|
||||||
|
.then(() => Promise.all([
|
||||||
|
this._getCount(),
|
||||||
|
this._getFee(),
|
||||||
|
this._getOwner()
|
||||||
|
]))
|
||||||
|
.then(() => {
|
||||||
|
const promises = [];
|
||||||
|
|
||||||
|
for (let index = 0; index < this.count; index++) {
|
||||||
|
promises.push(this._instanceReg.at.call({}, [index]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(promises);
|
||||||
|
})
|
||||||
|
.then((appsInfo) => {
|
||||||
|
return Promise.all(
|
||||||
|
this
|
||||||
|
.setApps(appsInfo.map(([appId, owner]) => {
|
||||||
|
const isOwner = !!this.accounts.find((account) => account.address === owner);
|
||||||
|
const account = this.addresses.find((account) => account.address === owner);
|
||||||
|
const id = api.util.bytesToHex(appId);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
owner,
|
||||||
|
ownerName: account ? account.name : owner,
|
||||||
|
isOwner,
|
||||||
|
name: `- ${id}`
|
||||||
|
};
|
||||||
|
}))
|
||||||
|
.map(this._loadDapp)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.sortApps();
|
||||||
|
this.setLoading(this.count === 0);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Store:loadDapps', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadDapp = (app) => {
|
||||||
|
return Promise
|
||||||
|
.all([
|
||||||
|
this._loadMeta(app.id, 'CONTENT'),
|
||||||
|
this._loadMeta(app.id, 'IMG'),
|
||||||
|
this._loadMeta(app.id, 'MANIFEST')
|
||||||
|
])
|
||||||
|
.then(([contentHash, imageHash, manifestHash]) => {
|
||||||
|
return Promise
|
||||||
|
.all([
|
||||||
|
this._retrieveUrl(contentHash),
|
||||||
|
this._retrieveUrl(imageHash),
|
||||||
|
this._retrieveUrl(manifestHash)
|
||||||
|
])
|
||||||
|
.then(([contentUrl, imageUrl, manifestUrl]) => {
|
||||||
|
return this
|
||||||
|
._loadManifest(app.id, manifestHash)
|
||||||
|
.then((manifest) => {
|
||||||
|
this.setAppInfo(app, {
|
||||||
|
manifest,
|
||||||
|
manifestHash,
|
||||||
|
manifestUrl,
|
||||||
|
contentHash,
|
||||||
|
contentUrl,
|
||||||
|
imageHash,
|
||||||
|
imageUrl,
|
||||||
|
name: (manifest && manifest.name) || `- ${app.id}`
|
||||||
|
});
|
||||||
|
|
||||||
|
return app;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Store:loadDapp', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadMeta (appId, key) {
|
||||||
|
return this._instanceReg
|
||||||
|
.meta.call({}, [appId, key])
|
||||||
|
.then((meta) => {
|
||||||
|
const hash = api.util.bytesToHex(meta);
|
||||||
|
const bnhash = new BigNumber(hash);
|
||||||
|
|
||||||
|
return bnhash.gt(0)
|
||||||
|
? hash
|
||||||
|
: null;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Store:loadMeta', error);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadManifest (appId, manifestHash) {
|
||||||
|
const builtin = builtins.find((app) => app.id === appId);
|
||||||
|
|
||||||
|
if (builtin) {
|
||||||
|
return Promise.resolve(builtin);
|
||||||
|
} else if (!manifestHash) {
|
||||||
|
return Promise.resolve(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch(`/api/content/${manifestHash.substr(2)}/`, { redirect: 'follow', mode: 'cors' })
|
||||||
|
.then((response) => {
|
||||||
|
return response.ok
|
||||||
|
? response.json()
|
||||||
|
: null;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Store:loadManifest', error);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_retrieveUrl (urlHash) {
|
||||||
|
if (!urlHash) {
|
||||||
|
return Promise.resolve(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._instanceGhh
|
||||||
|
.entries.call({}, [urlHash])
|
||||||
|
.then(([repo, _commit, owner]) => {
|
||||||
|
const bnowner = new BigNumber(owner);
|
||||||
|
|
||||||
|
if (bnowner.eq(0)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const commit = api.util.bytesToHex(_commit);
|
||||||
|
const bncommit = new BigNumber(commit);
|
||||||
|
|
||||||
|
if (bncommit.eq(0)) {
|
||||||
|
return repo;
|
||||||
|
} else {
|
||||||
|
return `https://codeload.github.com/${repo}/zip/${commit.substr(2)}`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Store:retriveUrl', error);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadAccounts () {
|
||||||
|
return api.parity
|
||||||
|
.accounts()
|
||||||
|
.then(this.setAccounts)
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Store:loadAccounts', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadRegistry () {
|
||||||
|
return api.parity
|
||||||
|
.registryAddress()
|
||||||
|
.then((registryAddress) => {
|
||||||
|
console.log(`the registry was found at ${registryAddress}`);
|
||||||
|
this._registry = api.newContract(abis.registry, registryAddress).instance;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Store:loadRegistry', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_attachContracts () {
|
||||||
|
return Promise
|
||||||
|
.all([
|
||||||
|
this._registry.getAddress.call({}, [api.util.sha3('dappreg'), 'A']),
|
||||||
|
this._registry.getAddress.call({}, [api.util.sha3('githubhint'), 'A'])
|
||||||
|
])
|
||||||
|
.then(([dappregAddress, ghhAddress]) => {
|
||||||
|
console.log(`dappreg was found at ${dappregAddress}`);
|
||||||
|
this._contractReg = api.newContract(abis.dappreg, dappregAddress);
|
||||||
|
this._instanceReg = this._contractReg.instance;
|
||||||
|
console.log(`githubhint was found at ${ghhAddress}`);
|
||||||
|
this._contractGhh = api.newContract(abis.githubhint, ghhAddress);
|
||||||
|
this._instanceGhh = this._contractGhh.instance;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Store:attachContract', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
266
js/src/dapps/dappreg/modalStore.js
Normal file
266
js/src/dapps/dappreg/modalStore.js
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { action, observable, transaction } from 'mobx';
|
||||||
|
|
||||||
|
import { trackRequest } from './parity';
|
||||||
|
import DappsStore from './dappsStore';
|
||||||
|
|
||||||
|
let instance = null;
|
||||||
|
|
||||||
|
export default class ModalStore {
|
||||||
|
@observable errorDelete = null;
|
||||||
|
@observable errorRegister = null;
|
||||||
|
@observable errorUpdate = null;
|
||||||
|
@observable stepDelete = 0;
|
||||||
|
@observable stepRegister = 0;
|
||||||
|
@observable stepUpdate = 0;
|
||||||
|
@observable showingDelete = false;
|
||||||
|
@observable showingRegister = false;
|
||||||
|
@observable showingUpdate = false;
|
||||||
|
@observable showingWarning = true;
|
||||||
|
|
||||||
|
_dappsStore = DappsStore.instance();
|
||||||
|
|
||||||
|
static instance () {
|
||||||
|
if (!instance) {
|
||||||
|
instance = new ModalStore();
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setDeleteError (error) {
|
||||||
|
transaction(() => {
|
||||||
|
this.setDeleteStep(0);
|
||||||
|
this.errorDelete = error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setDeleteStep (step) {
|
||||||
|
this.stepDelete = step;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action showDelete () {
|
||||||
|
transaction(() => {
|
||||||
|
this.setDeleteStep(1);
|
||||||
|
this.errorDelete = null;
|
||||||
|
this.showingDelete = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action hideDelete () {
|
||||||
|
this.showingDelete = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setRegisterError (error) {
|
||||||
|
transaction(() => {
|
||||||
|
this.setRegisterStep(0);
|
||||||
|
this.errorRegister = error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setRegisterStep (step) {
|
||||||
|
this.stepRegister = step;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action showRegister () {
|
||||||
|
transaction(() => {
|
||||||
|
this.setRegisterStep(1);
|
||||||
|
this.errorRegister = null;
|
||||||
|
this.showingRegister = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action hideRegister () {
|
||||||
|
transaction(() => {
|
||||||
|
this._dappsStore.setEditing(false);
|
||||||
|
this._dappsStore.setNew(false);
|
||||||
|
this.showingRegister = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setUpdateError (error) {
|
||||||
|
transaction(() => {
|
||||||
|
this.setUpdateStep(0);
|
||||||
|
this.errorUpdate = error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setUpdateStep (step) {
|
||||||
|
this.stepUpdate = step;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action showUpdate () {
|
||||||
|
transaction(() => {
|
||||||
|
this.setUpdateStep(1);
|
||||||
|
this.errorUpdate = null;
|
||||||
|
this.showingUpdate = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action hideUpdate () {
|
||||||
|
transaction(() => {
|
||||||
|
this._dappsStore.setEditing(false);
|
||||||
|
this._dappsStore.setNew(false);
|
||||||
|
this.showingUpdate = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action hideWarning () {
|
||||||
|
this.showingWarning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
doDelete () {
|
||||||
|
this.setDeleteStep(2);
|
||||||
|
|
||||||
|
const appId = this._dappsStore.currentApp.id;
|
||||||
|
const values = [appId];
|
||||||
|
const options = {
|
||||||
|
from: this._dappsStore.currentApp.isOwner ? this._dappsStore.currentApp.owner : this._dappsStore.contractOwner
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('ModalStore:doDelete', `performing deletion for ${appId} from ${options.from}`);
|
||||||
|
|
||||||
|
this._dappsStore._instanceReg
|
||||||
|
.unregister.estimateGas(options, values)
|
||||||
|
.then((gas) => {
|
||||||
|
const newGas = gas.mul(1.2);
|
||||||
|
|
||||||
|
console.log('ModalStore:doDelete', `gas estimated as ${gas.toFormat(0)}, setting to ${newGas.toFormat(0)}`);
|
||||||
|
|
||||||
|
options.gas = newGas.toFixed(0);
|
||||||
|
|
||||||
|
const request = this._dappsStore._instanceReg.unregister.postTransaction(options, values);
|
||||||
|
const statusCallback = (error, status) => {
|
||||||
|
if (error) {
|
||||||
|
} else if (status.signerRequestId) {
|
||||||
|
} else if (status.transactionHash) {
|
||||||
|
this.setDeleteStep(3);
|
||||||
|
} else if (status.transactionReceipt) {
|
||||||
|
this.setDeleteStep(4);
|
||||||
|
this._dappsStore.removeApp(appId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return trackRequest(request, statusCallback);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('ModalStore:doDelete', error);
|
||||||
|
this.setDeleteError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
doRegister () {
|
||||||
|
this.setRegisterStep(2);
|
||||||
|
|
||||||
|
const appId = this._dappsStore.wipApp.id;
|
||||||
|
const values = [appId];
|
||||||
|
const options = {
|
||||||
|
from: this._dappsStore.currentAccount.address,
|
||||||
|
value: this._dappsStore.fee
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('ModalStore:doRegister', `performing registration for ${appId} from ${this._dappsStore.currentAccount.address}`);
|
||||||
|
|
||||||
|
this._dappsStore._instanceReg
|
||||||
|
.register.estimateGas(options, values)
|
||||||
|
.then((gas) => {
|
||||||
|
const newGas = gas.mul(1.2);
|
||||||
|
|
||||||
|
console.log('ModalStore:doRegister', `gas estimated as ${gas.toFormat(0)}, setting to ${newGas.toFormat(0)}`);
|
||||||
|
|
||||||
|
options.gas = newGas.toFixed(0);
|
||||||
|
|
||||||
|
const request = this._dappsStore._instanceReg.register.postTransaction(options, values);
|
||||||
|
const statusCallback = (error, status) => {
|
||||||
|
if (error) {
|
||||||
|
} else if (status.signerRequestId) {
|
||||||
|
} else if (status.transactionHash) {
|
||||||
|
this.setRegisterStep(3);
|
||||||
|
} else if (status.transactionReceipt) {
|
||||||
|
this.setRegisterStep(4);
|
||||||
|
this._dappsStore.addApp(appId, this._dappsStore.currentAccount);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return trackRequest(request, statusCallback);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('ModalStore:doRegister', error);
|
||||||
|
this.setRegisterError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
doUpdate () {
|
||||||
|
this.setUpdateStep(2);
|
||||||
|
|
||||||
|
const appId = this._dappsStore.wipApp.id;
|
||||||
|
const options = {
|
||||||
|
from: this._dappsStore.wipApp.owner
|
||||||
|
};
|
||||||
|
const types = {
|
||||||
|
'content': 'CONTENT',
|
||||||
|
'image': 'IMG',
|
||||||
|
'manifest': 'MANIFEST'
|
||||||
|
};
|
||||||
|
const values = Object
|
||||||
|
.keys(types)
|
||||||
|
.filter((type) => this._dappsStore.wipApp[`${type}Changed`])
|
||||||
|
.map((type) => {
|
||||||
|
return [appId, types[type], this._dappsStore.wipApp[`${type}Hash`] || '0x0'];
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('ModalStore:doUpdate', `performing updates for ${appId} from ${options.from}`);
|
||||||
|
|
||||||
|
Promise
|
||||||
|
.all(values.map((value) => this._dappsStore._instanceReg.setMeta.estimateGas(options, value)))
|
||||||
|
.then((gas) => {
|
||||||
|
const newGas = gas.map((gas) => gas.mul(1.2));
|
||||||
|
|
||||||
|
gas.forEach((gas, index) => {
|
||||||
|
console.log('ModalStore:doUpdate', `${values[index][1]} gas estimated as ${gas.toFormat(0)}, setting to ${newGas[index].toFormat(0)}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
const statusCallback = (error, status) => {
|
||||||
|
if (error) {
|
||||||
|
} else if (status.signerRequestId) {
|
||||||
|
} else if (status.transactionHash) {
|
||||||
|
this.setUpdateStep(3);
|
||||||
|
} else if (status.transactionReceipt) {
|
||||||
|
this.setUpdateStep(4);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Promise.all(
|
||||||
|
newGas.map((gas, index) => {
|
||||||
|
return trackRequest(
|
||||||
|
this._dappsStore._instanceReg.setMeta.postTransaction(
|
||||||
|
Object.assign(options, { gas: gas.toFixed(0) }), values[index]
|
||||||
|
), statusCallback
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this._dappsStore.refreshApp(appId);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('ModalStore:doUpdate', error);
|
||||||
|
this.setUpdateError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
53
js/src/dapps/dappreg/parity.js
Normal file
53
js/src/dapps/dappreg/parity.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
const { api } = window.parity;
|
||||||
|
|
||||||
|
function trackRequest (requestPromise, statusCallback) {
|
||||||
|
return requestPromise
|
||||||
|
.then((signerRequestId) => {
|
||||||
|
console.log('trackRequest', `posted to signer with requestId ${signerRequestId.toString()}`);
|
||||||
|
statusCallback(null, { signerRequestId });
|
||||||
|
|
||||||
|
return api.pollMethod('parity_checkRequest', signerRequestId);
|
||||||
|
})
|
||||||
|
.then((transactionHash) => {
|
||||||
|
console.log('trackRequest', `received transaction hash ${transactionHash}`);
|
||||||
|
statusCallback(null, { transactionHash });
|
||||||
|
|
||||||
|
return api.pollMethod('eth_getTransactionReceipt', transactionHash, (receipt) => {
|
||||||
|
if (!receipt || !receipt.blockNumber || receipt.blockNumber.eq(0)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then((transactionReceipt) => {
|
||||||
|
console.log('trackRequest', 'received transaction receipt', transactionReceipt);
|
||||||
|
statusCallback(null, { transactionReceipt });
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('trackRequest', error);
|
||||||
|
statusCallback(error);
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
api,
|
||||||
|
trackRequest
|
||||||
|
};
|
@ -32,8 +32,13 @@ export default class Application extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
const poll = () => this.fetchTransactionData().then(poll).catch(poll);
|
const poll = () => {
|
||||||
this._timeout = setTimeout(poll, 2000);
|
this._timeout = window.setTimeout(() => {
|
||||||
|
this.fetchTransactionData().then(poll).catch(poll);
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
poll();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
|
@ -77,7 +77,7 @@ export default class Lookup extends Component {
|
|||||||
label='Lookup'
|
label='Lookup'
|
||||||
primary
|
primary
|
||||||
icon={ <SearchIcon /> }
|
icon={ <SearchIcon /> }
|
||||||
onClick={ this.onLookupClick }
|
onTouchTap={ this.onLookupClick }
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<CardText>{ output }</CardText>
|
<CardText>{ output }</CardText>
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import { sha3, toWei } from '../parity.js';
|
import { sha3, api } from '../parity.js';
|
||||||
|
|
||||||
const alreadyQueued = (queue, action, name) =>
|
const alreadyQueued = (queue, action, name) =>
|
||||||
!!queue.find((entry) => entry.action === action && entry.name === name);
|
!!queue.find((entry) => entry.action === action && entry.name === name);
|
||||||
@ -29,6 +29,8 @@ export const reserve = (name) => (dispatch, getState) => {
|
|||||||
const state = getState();
|
const state = getState();
|
||||||
const account = state.accounts.selected;
|
const account = state.accounts.selected;
|
||||||
const contract = state.contract;
|
const contract = state.contract;
|
||||||
|
const fee = state.fee;
|
||||||
|
|
||||||
if (!contract || !account) return;
|
if (!contract || !account) return;
|
||||||
if (alreadyQueued(state.names.queue, 'reserve', name)) return;
|
if (alreadyQueued(state.names.queue, 'reserve', name)) return;
|
||||||
const reserve = contract.functions.find((f) => f.name === 'reserve');
|
const reserve = contract.functions.find((f) => f.name === 'reserve');
|
||||||
@ -36,19 +38,28 @@ export const reserve = (name) => (dispatch, getState) => {
|
|||||||
name = name.toLowerCase();
|
name = name.toLowerCase();
|
||||||
const options = {
|
const options = {
|
||||||
from: account.address,
|
from: account.address,
|
||||||
value: toWei(1).toString()
|
value: fee
|
||||||
};
|
};
|
||||||
const values = [ sha3(name) ];
|
const values = [ sha3(name) ];
|
||||||
|
|
||||||
dispatch(reserveStart(name));
|
dispatch(reserveStart(name));
|
||||||
|
|
||||||
reserve.estimateGas(options, values)
|
reserve.estimateGas(options, values)
|
||||||
.then((gas) => {
|
.then((gas) => {
|
||||||
options.gas = gas.mul(1.2).toFixed(0);
|
options.gas = gas.mul(1.2).toFixed(0);
|
||||||
return reserve.postTransaction(options, values);
|
return reserve.postTransaction(options, values);
|
||||||
})
|
})
|
||||||
.then((data) => {
|
.then((requestId) => {
|
||||||
|
return api.pollMethod('parity_checkRequest', requestId);
|
||||||
|
})
|
||||||
|
.then((txhash) => {
|
||||||
dispatch(reserveSuccess(name));
|
dispatch(reserveSuccess(name));
|
||||||
}).catch((err) => {
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
if (err && err.type === 'REQUEST_REJECTED') {
|
||||||
|
return dispatch(reserveFail(name));
|
||||||
|
}
|
||||||
|
|
||||||
console.error(`could not reserve ${name}`);
|
console.error(`could not reserve ${name}`);
|
||||||
if (err) console.error(err.stack);
|
if (err) console.error(err.stack);
|
||||||
dispatch(reserveFail(name));
|
dispatch(reserveFail(name));
|
||||||
@ -79,9 +90,17 @@ export const drop = (name) => (dispatch, getState) => {
|
|||||||
options.gas = gas.mul(1.2).toFixed(0);
|
options.gas = gas.mul(1.2).toFixed(0);
|
||||||
return drop.postTransaction(options, values);
|
return drop.postTransaction(options, values);
|
||||||
})
|
})
|
||||||
.then((data) => {
|
.then((requestId) => {
|
||||||
|
return api.pollMethod('parity_checkRequest', requestId);
|
||||||
|
})
|
||||||
|
.then((txhash) => {
|
||||||
dispatch(dropSuccess(name));
|
dispatch(dropSuccess(name));
|
||||||
}).catch((err) => {
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
if (err && err.type === 'REQUEST_REJECTED') {
|
||||||
|
dispatch(reserveFail(name));
|
||||||
|
}
|
||||||
|
|
||||||
console.error(`could not drop ${name}`);
|
console.error(`could not drop ${name}`);
|
||||||
if (err) console.error(err.stack);
|
if (err) console.error(err.stack);
|
||||||
dispatch(reserveFail(name));
|
dispatch(reserveFail(name));
|
||||||
|
@ -86,6 +86,22 @@ export default class Names extends Component {
|
|||||||
name: ''
|
name: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
|
componentWillReceiveProps (nextProps) {
|
||||||
|
const nextQueue = nextProps.queue;
|
||||||
|
const prevQueue = this.props.queue;
|
||||||
|
|
||||||
|
if (nextQueue.length > prevQueue.length) {
|
||||||
|
const newQueued = nextQueue[nextQueue.length - 1];
|
||||||
|
const newName = newQueued.name;
|
||||||
|
|
||||||
|
if (newName !== this.state.name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ name: '' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { action, name } = this.state;
|
const { action, name } = this.state;
|
||||||
const { fee, pending, queue } = this.props;
|
const { fee, pending, queue } = this.props;
|
||||||
@ -120,7 +136,7 @@ export default class Names extends Component {
|
|||||||
label={ action === 'reserve' ? 'Reserve' : 'Drop' }
|
label={ action === 'reserve' ? 'Reserve' : 'Drop' }
|
||||||
primary
|
primary
|
||||||
icon={ <CheckIcon /> }
|
icon={ <CheckIcon /> }
|
||||||
onClick={ this.onSubmitClick }
|
onTouchTap={ this.onSubmitClick }
|
||||||
/>
|
/>
|
||||||
{ queue.length > 0
|
{ queue.length > 0
|
||||||
? (<div>{ useSignerText }{ renderQueue(queue) }</div>)
|
? (<div>{ useSignerText }{ renderQueue(queue) }</div>)
|
||||||
|
@ -52,7 +52,7 @@ export default class Records extends Component {
|
|||||||
label='Save'
|
label='Save'
|
||||||
primary
|
primary
|
||||||
icon={ <SaveIcon /> }
|
icon={ <SaveIcon /> }
|
||||||
onClick={ this.onSaveClick }
|
onTouchTap={ this.onSaveClick }
|
||||||
/>
|
/>
|
||||||
</CardText>
|
</CardText>
|
||||||
</Card>
|
</Card>
|
||||||
|
@ -46,6 +46,12 @@ import './index.html';
|
|||||||
|
|
||||||
injectTapEventPlugin();
|
injectTapEventPlugin();
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
// Expose the React Performance Tools on the`window` object
|
||||||
|
const Perf = require('react-addons-perf');
|
||||||
|
window.Perf = Perf;
|
||||||
|
}
|
||||||
|
|
||||||
const AUTH_HASH = '#/auth?';
|
const AUTH_HASH = '#/auth?';
|
||||||
const parityUrl = process.env.PARITY_URL ||
|
const parityUrl = process.env.PARITY_URL ||
|
||||||
(
|
(
|
||||||
|
@ -238,6 +238,24 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
killAccount: {
|
||||||
|
desc: 'Deletes an account',
|
||||||
|
params: [
|
||||||
|
{
|
||||||
|
type: Address,
|
||||||
|
desc: 'The account to remove'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: String,
|
||||||
|
desc: 'Account password'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: Boolean,
|
||||||
|
desc: 'true on success'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
listGethAccounts: {
|
listGethAccounts: {
|
||||||
desc: 'Returns a list of the accounts available from Geth',
|
desc: 'Returns a list of the accounts available from Geth',
|
||||||
params: [],
|
params: [],
|
||||||
|
@ -54,8 +54,6 @@ export default class RecoveryPhrase extends Component {
|
|||||||
<Input
|
<Input
|
||||||
hint='the account recovery phrase'
|
hint='the account recovery phrase'
|
||||||
label='account recovery phrase'
|
label='account recovery phrase'
|
||||||
multiLine
|
|
||||||
rows={ 1 }
|
|
||||||
value={ recoveryPhrase }
|
value={ recoveryPhrase }
|
||||||
onChange={ this.onEditPhrase } />
|
onChange={ this.onEditPhrase } />
|
||||||
<Input
|
<Input
|
||||||
@ -112,17 +110,23 @@ export default class RecoveryPhrase extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onEditPhrase = (event) => {
|
onEditPhrase = (event) => {
|
||||||
const value = event.target.value;
|
const recoveryPhrase = event.target.value
|
||||||
let error = null;
|
.toLowerCase() // wordlists are lowercase
|
||||||
|
.trim() // remove whitespace at both ends
|
||||||
|
.replace(/\s/g, ' ') // replace any whitespace with single space
|
||||||
|
.replace(/ +/g, ' '); // replace multiple spaces with a single space
|
||||||
|
|
||||||
if (!value || value.trim().length < 25) {
|
const parts = recoveryPhrase.split(' ');
|
||||||
error = ERRORS.noPhrase;
|
let recoveryPhraseError = null;
|
||||||
|
|
||||||
|
if (!recoveryPhrase || recoveryPhrase.length < 25 || parts.length < 8) {
|
||||||
|
recoveryPhraseError = ERRORS.noPhrase;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
recoveryPhrase: value,
|
recoveryPhrase,
|
||||||
recoveryPhraseError: error,
|
recoveryPhraseError,
|
||||||
isValidPhrase: !error
|
isValidPhrase: !recoveryPhraseError
|
||||||
}, this.updateParent);
|
}, this.updateParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
54
js/src/modals/DeleteAccount/deleteAccount.css
Normal file
54
js/src/modals/DeleteAccount/deleteAccount.css
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/* Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
/* This file is part of Parity.
|
||||||
|
/*
|
||||||
|
/* Parity 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.
|
||||||
|
/*
|
||||||
|
/* Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.body {
|
||||||
|
.hero {
|
||||||
|
padding-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nameinfo {
|
||||||
|
display: inline-block;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 1.25em;
|
||||||
|
padding-bottom: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address {
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
padding-top: 1em;
|
||||||
|
font-size: 0.75em;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password {
|
||||||
|
padding: 1em 5em;
|
||||||
|
}
|
||||||
|
}
|
125
js/src/modals/DeleteAccount/deleteAccount.js
Normal file
125
js/src/modals/DeleteAccount/deleteAccount.js
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { bindActionCreators } from 'redux';
|
||||||
|
|
||||||
|
import { ConfirmDialog, IdentityIcon, IdentityName, Input } from '../../ui';
|
||||||
|
import { newError } from '../../redux/actions';
|
||||||
|
|
||||||
|
import styles from './deleteAccount.css';
|
||||||
|
|
||||||
|
class DeleteAccount extends Component {
|
||||||
|
static contextTypes = {
|
||||||
|
api: PropTypes.object.isRequired,
|
||||||
|
router: PropTypes.object
|
||||||
|
}
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
account: PropTypes.object.isRequired,
|
||||||
|
onClose: PropTypes.func.isRequired,
|
||||||
|
newError: PropTypes.func.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
state = {
|
||||||
|
password: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { account } = this.props;
|
||||||
|
const { password } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConfirmDialog
|
||||||
|
className={ styles.body }
|
||||||
|
title='confirm removal'
|
||||||
|
visible
|
||||||
|
onDeny={ this.closeDeleteDialog }
|
||||||
|
onConfirm={ this.onDeleteConfirmed }>
|
||||||
|
<div className={ styles.hero }>
|
||||||
|
Are you sure you want to permanently delete the following account?
|
||||||
|
</div>
|
||||||
|
<div className={ styles.info }>
|
||||||
|
<IdentityIcon
|
||||||
|
className={ styles.icon }
|
||||||
|
address={ account.address } />
|
||||||
|
<div className={ styles.nameinfo }>
|
||||||
|
<div className={ styles.header }>
|
||||||
|
<IdentityName address={ account.address } unknown />
|
||||||
|
</div>
|
||||||
|
<div className={ styles.address }>
|
||||||
|
{ account.address }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={ styles.description }>
|
||||||
|
{ account.meta.description }
|
||||||
|
</div>
|
||||||
|
<div className={ styles.password }>
|
||||||
|
<Input
|
||||||
|
label='account password'
|
||||||
|
hint='provide the account password to confirm the account deletion'
|
||||||
|
type='password'
|
||||||
|
value={ password }
|
||||||
|
onChange={ this.onChangePassword } />
|
||||||
|
</div>
|
||||||
|
</ConfirmDialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangePassword = (event, password) => {
|
||||||
|
this.setState({ password });
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeleteConfirmed = () => {
|
||||||
|
const { api, router } = this.context;
|
||||||
|
const { account, newError } = this.props;
|
||||||
|
const { password } = this.state;
|
||||||
|
|
||||||
|
api.parity
|
||||||
|
.killAccount(account.address, password)
|
||||||
|
.then((result) => {
|
||||||
|
if (result === true) {
|
||||||
|
router.push('/accounts');
|
||||||
|
this.closeDeleteDialog();
|
||||||
|
} else {
|
||||||
|
newError(new Error('Deletion failed.'));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('onDeleteConfirmed', error);
|
||||||
|
newError(new Error(`Deletion failed: ${error.message}`));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
closeDeleteDialog = () => {
|
||||||
|
this.props.onClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps (dispatch) {
|
||||||
|
return bindActionCreators({ newError }, dispatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(DeleteAccount);
|
17
js/src/modals/DeleteAccount/index.js
Normal file
17
js/src/modals/DeleteAccount/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
export default from './deleteAccount';
|
@ -36,6 +36,7 @@ export default class DetailsStep extends Component {
|
|||||||
onFuncChange: PropTypes.func,
|
onFuncChange: PropTypes.func,
|
||||||
values: PropTypes.array.isRequired,
|
values: PropTypes.array.isRequired,
|
||||||
valuesError: PropTypes.array.isRequired,
|
valuesError: PropTypes.array.isRequired,
|
||||||
|
warning: PropTypes.string,
|
||||||
onValueChange: PropTypes.func.isRequired
|
onValueChange: PropTypes.func.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,6 +45,7 @@ export default class DetailsStep extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Form>
|
<Form>
|
||||||
|
{ this.renderWarning() }
|
||||||
<AddressSelect
|
<AddressSelect
|
||||||
label='from account'
|
label='from account'
|
||||||
hint='the account to transact with'
|
hint='the account to transact with'
|
||||||
@ -178,6 +180,20 @@ export default class DetailsStep extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderWarning () {
|
||||||
|
const { warning } = this.props;
|
||||||
|
|
||||||
|
if (!warning) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={ styles.warning }>
|
||||||
|
{ warning }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
onFuncChange = (event, index, signature) => {
|
onFuncChange = (event, index, signature) => {
|
||||||
const { contract, onFuncChange } = this.props;
|
const { contract, onFuncChange } = this.props;
|
||||||
|
|
||||||
|
@ -33,3 +33,12 @@
|
|||||||
.txhash {
|
.txhash {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
border-radius: 0.5em;
|
||||||
|
background: #f80;
|
||||||
|
color: white;
|
||||||
|
font-size: 0.75em;
|
||||||
|
padding: 0.75em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
@ -15,17 +15,21 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { bindActionCreators } from 'redux';
|
||||||
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
|
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
|
||||||
import ContentClear from 'material-ui/svg-icons/content/clear';
|
import ContentClear from 'material-ui/svg-icons/content/clear';
|
||||||
|
|
||||||
import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '../../ui';
|
import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '../../ui';
|
||||||
|
import { MAX_GAS_ESTIMATION } from '../../util/constants';
|
||||||
import { validateAddress, validateUint } from '../../util/validation';
|
import { validateAddress, validateUint } from '../../util/validation';
|
||||||
|
|
||||||
import DetailsStep from './DetailsStep';
|
import DetailsStep from './DetailsStep';
|
||||||
|
|
||||||
|
import ERRORS from '../Transfer/errors';
|
||||||
import { ERROR_CODES } from '../../api/transport/error';
|
import { ERROR_CODES } from '../../api/transport/error';
|
||||||
|
|
||||||
export default class ExecuteContract extends Component {
|
class ExecuteContract extends Component {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
api: PropTypes.object.isRequired,
|
api: PropTypes.object.isRequired,
|
||||||
store: PropTypes.object.isRequired
|
store: PropTypes.object.isRequired
|
||||||
@ -36,6 +40,7 @@ export default class ExecuteContract extends Component {
|
|||||||
fromAddress: PropTypes.string,
|
fromAddress: PropTypes.string,
|
||||||
accounts: PropTypes.object,
|
accounts: PropTypes.object,
|
||||||
contract: PropTypes.object,
|
contract: PropTypes.object,
|
||||||
|
gasLimit: PropTypes.object.isRequired,
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
onFromAddressChange: PropTypes.func.isRequired
|
onFromAddressChange: PropTypes.func.isRequired
|
||||||
}
|
}
|
||||||
@ -46,6 +51,8 @@ export default class ExecuteContract extends Component {
|
|||||||
fromAddressError: null,
|
fromAddressError: null,
|
||||||
func: null,
|
func: null,
|
||||||
funcError: null,
|
funcError: null,
|
||||||
|
gas: null,
|
||||||
|
gasLimitError: null,
|
||||||
values: [],
|
values: [],
|
||||||
valuesError: [],
|
valuesError: [],
|
||||||
step: 0,
|
step: 0,
|
||||||
@ -64,6 +71,12 @@ export default class ExecuteContract extends Component {
|
|||||||
this.onFuncChange(null, functions[0]);
|
this.onFuncChange(null, functions[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps (newProps) {
|
||||||
|
if (newProps.fromAddress !== this.props.fromAddress) {
|
||||||
|
this.estimateGas(newProps.fromAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { sending } = this.state;
|
const { sending } = this.state;
|
||||||
|
|
||||||
@ -119,7 +132,7 @@ export default class ExecuteContract extends Component {
|
|||||||
|
|
||||||
renderStep () {
|
renderStep () {
|
||||||
const { onFromAddressChange } = this.props;
|
const { onFromAddressChange } = this.props;
|
||||||
const { step, busyState, txhash, rejected } = this.state;
|
const { step, busyState, gasLimitError, txhash, rejected } = this.state;
|
||||||
|
|
||||||
if (rejected) {
|
if (rejected) {
|
||||||
return (
|
return (
|
||||||
@ -135,6 +148,7 @@ export default class ExecuteContract extends Component {
|
|||||||
<DetailsStep
|
<DetailsStep
|
||||||
{ ...this.props }
|
{ ...this.props }
|
||||||
{ ...this.state }
|
{ ...this.state }
|
||||||
|
warning={ gasLimitError }
|
||||||
onAmountChange={ this.onAmountChange }
|
onAmountChange={ this.onAmountChange }
|
||||||
onFromAddressChange={ onFromAddressChange }
|
onFromAddressChange={ onFromAddressChange }
|
||||||
onFuncChange={ this.onFuncChange }
|
onFuncChange={ this.onFuncChange }
|
||||||
@ -156,7 +170,7 @@ export default class ExecuteContract extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onAmountChange = (amount) => {
|
onAmountChange = (amount) => {
|
||||||
this.setState({ amount });
|
this.setState({ amount }, this.estimateGas);
|
||||||
}
|
}
|
||||||
|
|
||||||
onFuncChange = (event, func) => {
|
onFuncChange = (event, func) => {
|
||||||
@ -182,7 +196,7 @@ export default class ExecuteContract extends Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
func,
|
func,
|
||||||
values
|
values
|
||||||
});
|
}, this.estimateGas);
|
||||||
}
|
}
|
||||||
|
|
||||||
onValueChange = (event, index, _value) => {
|
onValueChange = (event, index, _value) => {
|
||||||
@ -211,14 +225,55 @@ export default class ExecuteContract extends Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
values: [].concat(values),
|
values: [].concat(values),
|
||||||
valuesError: [].concat(valuesError)
|
valuesError: [].concat(valuesError)
|
||||||
|
}, () => {
|
||||||
|
if (!valueError) {
|
||||||
|
this.estimateGas();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
estimateGas = (_fromAddress) => {
|
||||||
|
const { api } = this.context;
|
||||||
|
const { fromAddress, gasLimit } = this.props;
|
||||||
|
const { amount, func, values } = this.state;
|
||||||
|
const options = {
|
||||||
|
gas: MAX_GAS_ESTIMATION,
|
||||||
|
from: _fromAddress || fromAddress,
|
||||||
|
value: api.util.toWei(amount || 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!func) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
func
|
||||||
|
.estimateGas(options, values)
|
||||||
|
.then((gasEst) => {
|
||||||
|
const gas = gasEst.mul(1.2);
|
||||||
|
let gasLimitError = null;
|
||||||
|
|
||||||
|
if (gas.gte(MAX_GAS_ESTIMATION)) {
|
||||||
|
gasLimitError = ERRORS.gasException;
|
||||||
|
} else if (gas.gt(gasLimit)) {
|
||||||
|
gasLimitError = ERRORS.gasBlockLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
gas,
|
||||||
|
gasLimitError
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.warn('estimateGas', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
postTransaction = () => {
|
postTransaction = () => {
|
||||||
const { api, store } = this.context;
|
const { api, store } = this.context;
|
||||||
const { fromAddress } = this.props;
|
const { fromAddress } = this.props;
|
||||||
const { amount, func, values } = this.state;
|
const { amount, func, values } = this.state;
|
||||||
const options = {
|
const options = {
|
||||||
|
gas: MAX_GAS_ESTIMATION,
|
||||||
from: fromAddress,
|
from: fromAddress,
|
||||||
value: api.util.toWei(amount || 0)
|
value: api.util.toWei(amount || 0)
|
||||||
};
|
};
|
||||||
@ -237,13 +292,13 @@ export default class ExecuteContract extends Component {
|
|||||||
|
|
||||||
return api
|
return api
|
||||||
.pollMethod('parity_checkRequest', requestId)
|
.pollMethod('parity_checkRequest', requestId)
|
||||||
.catch((e) => {
|
.catch((error) => {
|
||||||
if (e.code === ERROR_CODES.REQUEST_REJECTED) {
|
if (error.code === ERROR_CODES.REQUEST_REJECTED) {
|
||||||
this.setState({ rejected: true });
|
this.setState({ rejected: true });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw e;
|
throw error;
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then((txhash) => {
|
.then((txhash) => {
|
||||||
@ -255,3 +310,18 @@ export default class ExecuteContract extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
const { gasLimit } = state.nodeStatus;
|
||||||
|
|
||||||
|
return { gasLimit };
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps (dispatch) {
|
||||||
|
return bindActionCreators({}, dispatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(ExecuteContract);
|
||||||
|
@ -174,7 +174,7 @@ export default class LoadContract extends Component {
|
|||||||
const secondaryText = description || `Saved ${moment(timestamp).fromNow()}`;
|
const secondaryText = description || `Saved ${moment(timestamp).fromNow()}`;
|
||||||
const remove = removable
|
const remove = removable
|
||||||
? (
|
? (
|
||||||
<IconButton onClick={ onDelete }>
|
<IconButton onTouchTap={ onDelete }>
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)
|
)
|
||||||
|
@ -25,7 +25,7 @@ import ErrorIcon from 'material-ui/svg-icons/navigation/close';
|
|||||||
import { fromWei } from '../../../api/util/wei';
|
import { fromWei } from '../../../api/util/wei';
|
||||||
import { Form, Input } from '../../../ui';
|
import { Form, Input } from '../../../ui';
|
||||||
|
|
||||||
import terms from '../terms-of-service';
|
import { termsOfService } from '../../../3rdparty/sms-verification';
|
||||||
import styles from './gatherData.css';
|
import styles from './gatherData.css';
|
||||||
|
|
||||||
export default class GatherData extends Component {
|
export default class GatherData extends Component {
|
||||||
@ -66,7 +66,7 @@ export default class GatherData extends Component {
|
|||||||
disabled={ isVerified }
|
disabled={ isVerified }
|
||||||
onCheck={ this.consentOnChange }
|
onCheck={ this.consentOnChange }
|
||||||
/>
|
/>
|
||||||
<div className={ styles.terms }>{ terms }</div>
|
<div className={ styles.terms }>{ termsOfService }</div>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -123,8 +123,7 @@ export default class GatherData extends Component {
|
|||||||
<p className={ styles.message }>You already requested verification.</p>
|
<p className={ styles.message }>You already requested verification.</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
} else if (hasRequested === false) {
|
||||||
if (hasRequested === false) {
|
|
||||||
return (
|
return (
|
||||||
<div className={ styles.container }>
|
<div className={ styles.container }>
|
||||||
<SuccessIcon />
|
<SuccessIcon />
|
||||||
|
@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
|
import DoneIcon from 'material-ui/svg-icons/action/done-all';
|
||||||
import ContentClear from 'material-ui/svg-icons/content/clear';
|
import CancelIcon from 'material-ui/svg-icons/content/clear';
|
||||||
|
|
||||||
import { Button, IdentityIcon, Modal } from '../../ui';
|
import { Button, IdentityIcon, Modal } from '../../ui';
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ export default class SMSVerification extends Component {
|
|||||||
const cancel = (
|
const cancel = (
|
||||||
<Button
|
<Button
|
||||||
key='cancel' label='Cancel'
|
key='cancel' label='Cancel'
|
||||||
icon={ <ContentClear /> }
|
icon={ <CancelIcon /> }
|
||||||
onClick={ onClose }
|
onClick={ onClose }
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -92,7 +92,7 @@ export default class SMSVerification extends Component {
|
|||||||
<Button
|
<Button
|
||||||
key='done' label='Done'
|
key='done' label='Done'
|
||||||
disabled={ !isStepValid }
|
disabled={ !isStepValid }
|
||||||
icon={ <ActionDoneAll /> }
|
icon={ <DoneIcon /> }
|
||||||
onClick={ onClose }
|
onClick={ onClose }
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -140,37 +140,47 @@ export default class SMSVerification extends Component {
|
|||||||
setNumber, setConsentGiven, setCode
|
setNumber, setConsentGiven, setCode
|
||||||
} = this.props.store;
|
} = this.props.store;
|
||||||
|
|
||||||
if (phase === 5) {
|
switch (phase) {
|
||||||
return (<Done />);
|
case 0:
|
||||||
}
|
return (
|
||||||
if (phase === 4) {
|
<p>Loading SMS Verification.</p>
|
||||||
return (<SendConfirmation step={ step } tx={ confirmationTx } />);
|
);
|
||||||
}
|
|
||||||
if (phase === 3) {
|
|
||||||
return (
|
|
||||||
<QueryCode
|
|
||||||
number={ number } fee={ fee } isCodeValid={ isCodeValid }
|
|
||||||
setCode={ setCode }
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (phase === 2) {
|
|
||||||
return (<SendRequest step={ step } tx={ requestTx } />);
|
|
||||||
}
|
|
||||||
if (phase === 1) {
|
|
||||||
const { setNumber, setConsentGiven } = this.props.store;
|
|
||||||
return (
|
|
||||||
<GatherData
|
|
||||||
fee={ fee } isNumberValid={ isNumberValid }
|
|
||||||
isVerified={ isVerified } hasRequested={ hasRequested }
|
|
||||||
setNumber={ setNumber } setConsentGiven={ setConsentGiven }
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (phase === 0) {
|
|
||||||
return (<p>Preparing awesomeness!</p>);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
case 1:
|
||||||
|
const { setNumber, setConsentGiven } = this.props.store;
|
||||||
|
return (
|
||||||
|
<GatherData
|
||||||
|
fee={ fee } isNumberValid={ isNumberValid }
|
||||||
|
isVerified={ isVerified } hasRequested={ hasRequested }
|
||||||
|
setNumber={ setNumber } setConsentGiven={ setConsentGiven }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
return (
|
||||||
|
<SendRequest step={ step } tx={ requestTx } />
|
||||||
|
);
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
return (
|
||||||
|
<QueryCode
|
||||||
|
number={ number } fee={ fee } isCodeValid={ isCodeValid }
|
||||||
|
setCode={ setCode }
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
return (
|
||||||
|
<SendConfirmation step={ step } tx={ confirmationTx } />
|
||||||
|
);
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
return (
|
||||||
|
<Done />
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,8 @@ import { sha3 } from '../../api/util/sha3';
|
|||||||
|
|
||||||
import Contracts from '../../contracts';
|
import Contracts from '../../contracts';
|
||||||
|
|
||||||
import { checkIfVerified, checkIfRequested, postToServer } from '../../contracts/sms-verification';
|
import { checkIfVerified, checkIfRequested } from '../../contracts/sms-verification';
|
||||||
|
import { postToServer } from '../../3rdparty/sms-verification';
|
||||||
import checkIfTxFailed from '../../util/check-if-tx-failed';
|
import checkIfTxFailed from '../../util/check-if-tx-failed';
|
||||||
import waitForConfirmations from '../../util/wait-for-block-confirmations';
|
import waitForConfirmations from '../../util/wait-for-block-confirmations';
|
||||||
|
|
||||||
@ -87,7 +88,7 @@ export default class VerificationStore {
|
|||||||
this.account = account;
|
this.account = account;
|
||||||
|
|
||||||
this.step = LOADING;
|
this.step = LOADING;
|
||||||
Contracts.create(api).registry.getContract('smsVerification')
|
Contracts.create(api).registry.getContract('smsverification')
|
||||||
.then((contract) => {
|
.then((contract) => {
|
||||||
this.contract = contract;
|
this.contract = contract;
|
||||||
this.load();
|
this.load();
|
||||||
|
@ -63,6 +63,16 @@ export default class Shapeshift extends Component {
|
|||||||
this.retrieveCoins();
|
this.retrieveCoins();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount () {
|
||||||
|
this.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsubscribe () {
|
||||||
|
// Unsubscribe from Shapeshit
|
||||||
|
const { depositAddress } = this.state;
|
||||||
|
shapeshift.unsubscribe(depositAddress);
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { error, stage } = this.state;
|
const { error, stage } = this.state;
|
||||||
|
|
||||||
@ -205,6 +215,10 @@ export default class Shapeshift extends Component {
|
|||||||
console.log('onShift', result);
|
console.log('onShift', result);
|
||||||
const depositAddress = result.deposit;
|
const depositAddress = result.deposit;
|
||||||
|
|
||||||
|
if (this.state.depositAddress) {
|
||||||
|
this.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
shapeshift.subscribe(depositAddress, this.onExchangeInfo);
|
shapeshift.subscribe(depositAddress, this.onExchangeInfo);
|
||||||
this.setState({ depositAddress });
|
this.setState({ depositAddress });
|
||||||
})
|
})
|
||||||
|
@ -19,7 +19,9 @@ const ERRORS = {
|
|||||||
invalidAddress: 'the supplied address is an invalid network address',
|
invalidAddress: 'the supplied address is an invalid network address',
|
||||||
invalidAmount: 'the supplied amount should be a valid positive number',
|
invalidAmount: 'the supplied amount should be a valid positive number',
|
||||||
invalidDecimals: 'the supplied amount exceeds the allowed decimals',
|
invalidDecimals: 'the supplied amount exceeds the allowed decimals',
|
||||||
largeAmount: 'the transaction total is higher than the available balance'
|
largeAmount: 'the transaction total is higher than the available balance',
|
||||||
|
gasException: 'the transaction will throw an exception with the current values',
|
||||||
|
gasBlockLimit: 'the transaction execution will exceed the block gas limit'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ERRORS;
|
export default ERRORS;
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
/* You should have received a copy of the GNU General Public License
|
/* You should have received a copy of the GNU General Public License
|
||||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
line-height: 1.618em;
|
line-height: 1.618em;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -151,3 +152,13 @@
|
|||||||
.gasPriceDesc {
|
.gasPriceDesc {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
border-radius: 0.5em;
|
||||||
|
background: #f80;
|
||||||
|
color: white;
|
||||||
|
font-size: 0.75em;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
padding: 0.75em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
@ -16,12 +16,15 @@
|
|||||||
|
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { bindActionCreators } from 'redux';
|
||||||
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
|
import ActionDoneAll from 'material-ui/svg-icons/action/done-all';
|
||||||
import ContentClear from 'material-ui/svg-icons/content/clear';
|
import ContentClear from 'material-ui/svg-icons/content/clear';
|
||||||
import NavigationArrowBack from 'material-ui/svg-icons/navigation/arrow-back';
|
import NavigationArrowBack from 'material-ui/svg-icons/navigation/arrow-back';
|
||||||
import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forward';
|
import NavigationArrowForward from 'material-ui/svg-icons/navigation/arrow-forward';
|
||||||
|
|
||||||
import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '../../ui';
|
import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '../../ui';
|
||||||
|
import { DEFAULT_GAS, DEFAULT_GASPRICE, MAX_GAS_ESTIMATION } from '../../util/constants';
|
||||||
|
|
||||||
import Details from './Details';
|
import Details from './Details';
|
||||||
import Extras from './Extras';
|
import Extras from './Extras';
|
||||||
@ -30,8 +33,6 @@ import styles from './transfer.css';
|
|||||||
|
|
||||||
import { ERROR_CODES } from '../../api/transport/error';
|
import { ERROR_CODES } from '../../api/transport/error';
|
||||||
|
|
||||||
const DEFAULT_GAS = '21000';
|
|
||||||
const DEFAULT_GASPRICE = '20000000000';
|
|
||||||
const TITLES = {
|
const TITLES = {
|
||||||
transfer: 'transfer details',
|
transfer: 'transfer details',
|
||||||
sending: 'sending',
|
sending: 'sending',
|
||||||
@ -42,7 +43,7 @@ const TITLES = {
|
|||||||
const STAGES_BASIC = [TITLES.transfer, TITLES.sending, TITLES.complete];
|
const STAGES_BASIC = [TITLES.transfer, TITLES.sending, TITLES.complete];
|
||||||
const STAGES_EXTRA = [TITLES.transfer, TITLES.extras, TITLES.sending, TITLES.complete];
|
const STAGES_EXTRA = [TITLES.transfer, TITLES.extras, TITLES.sending, TITLES.complete];
|
||||||
|
|
||||||
export default class Transfer extends Component {
|
class Transfer extends Component {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
api: PropTypes.object.isRequired,
|
api: PropTypes.object.isRequired,
|
||||||
store: PropTypes.object.isRequired
|
store: PropTypes.object.isRequired
|
||||||
@ -52,6 +53,7 @@ export default class Transfer extends Component {
|
|||||||
account: PropTypes.object,
|
account: PropTypes.object,
|
||||||
balance: PropTypes.object,
|
balance: PropTypes.object,
|
||||||
balances: PropTypes.object,
|
balances: PropTypes.object,
|
||||||
|
gasLimit: PropTypes.object.isRequired,
|
||||||
images: PropTypes.object.isRequired,
|
images: PropTypes.object.isRequired,
|
||||||
onClose: PropTypes.func
|
onClose: PropTypes.func
|
||||||
}
|
}
|
||||||
@ -64,6 +66,7 @@ export default class Transfer extends Component {
|
|||||||
gas: DEFAULT_GAS,
|
gas: DEFAULT_GAS,
|
||||||
gasEst: '0',
|
gasEst: '0',
|
||||||
gasError: null,
|
gasError: null,
|
||||||
|
gasLimitError: null,
|
||||||
gasPrice: DEFAULT_GASPRICE,
|
gasPrice: DEFAULT_GASPRICE,
|
||||||
gasPriceHistogram: {},
|
gasPriceHistogram: {},
|
||||||
gasPriceError: null,
|
gasPriceError: null,
|
||||||
@ -103,6 +106,7 @@ export default class Transfer extends Component {
|
|||||||
visible
|
visible
|
||||||
scroll
|
scroll
|
||||||
>
|
>
|
||||||
|
{ this.renderWarning() }
|
||||||
{ this.renderPage() }
|
{ this.renderPage() }
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
@ -264,6 +268,20 @@ export default class Transfer extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderWarning () {
|
||||||
|
const { gasLimitError } = this.state;
|
||||||
|
|
||||||
|
if (!gasLimitError) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={ styles.warning }>
|
||||||
|
{ gasLimitError }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
isValid () {
|
isValid () {
|
||||||
const detailsValid = !this.state.recipientError && !this.state.valueError && !this.state.totalError;
|
const detailsValid = !this.state.recipientError && !this.state.valueError && !this.state.totalError;
|
||||||
const extrasValid = !this.state.gasError && !this.state.gasPriceError && !this.state.totalError;
|
const extrasValid = !this.state.gasError && !this.state.gasPriceError && !this.state.totalError;
|
||||||
@ -519,6 +537,7 @@ export default class Transfer extends Component {
|
|||||||
|
|
||||||
return token.contract.instance.transfer
|
return token.contract.instance.transfer
|
||||||
.estimateGas({
|
.estimateGas({
|
||||||
|
gas: MAX_GAS_ESTIMATION,
|
||||||
from: account.address,
|
from: account.address,
|
||||||
to: token.address
|
to: token.address
|
||||||
}, [
|
}, [
|
||||||
@ -532,6 +551,7 @@ export default class Transfer extends Component {
|
|||||||
const { account } = this.props;
|
const { account } = this.props;
|
||||||
const { data, recipient, value } = this.state;
|
const { data, recipient, value } = this.state;
|
||||||
const options = {
|
const options = {
|
||||||
|
gas: MAX_GAS_ESTIMATION,
|
||||||
from: account.address,
|
from: account.address,
|
||||||
to: recipient,
|
to: recipient,
|
||||||
value: api.util.toWei(value || 0)
|
value: api.util.toWei(value || 0)
|
||||||
@ -552,19 +572,29 @@ export default class Transfer extends Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { gasLimit } = this.props;
|
||||||
|
|
||||||
(this.state.isEth
|
(this.state.isEth
|
||||||
? this._estimateGasEth()
|
? this._estimateGasEth()
|
||||||
: this._estimateGasToken()
|
: this._estimateGasToken()
|
||||||
).then((_value) => {
|
).then((gasEst) => {
|
||||||
let gas = _value;
|
let gas = gasEst;
|
||||||
|
let gasLimitError = null;
|
||||||
|
|
||||||
if (gas.gt(DEFAULT_GAS)) {
|
if (gas.gt(DEFAULT_GAS)) {
|
||||||
gas = gas.mul(1.2);
|
gas = gas.mul(1.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gas.gte(MAX_GAS_ESTIMATION)) {
|
||||||
|
gasLimitError = ERRORS.gasException;
|
||||||
|
} else if (gas.gt(gasLimit)) {
|
||||||
|
gasLimitError = ERRORS.gasBlockLimit;
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
gas: gas.toFixed(0),
|
gas: gas.toFixed(0),
|
||||||
gasEst: _value.toFormat()
|
gasEst: gasEst.toFormat(),
|
||||||
|
gasLimitError
|
||||||
}, this.recalculate);
|
}, this.recalculate);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@ -649,3 +679,18 @@ export default class Transfer extends Component {
|
|||||||
store.dispatch({ type: 'newError', error });
|
store.dispatch({ type: 'newError', error });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
const { gasLimit } = state.nodeStatus;
|
||||||
|
|
||||||
|
return { gasLimit };
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps (dispatch) {
|
||||||
|
return bindActionCreators({}, dispatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(Transfer);
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
import AddAddress from './AddAddress';
|
import AddAddress from './AddAddress';
|
||||||
import AddContract from './AddContract';
|
import AddContract from './AddContract';
|
||||||
import CreateAccount from './CreateAccount';
|
import CreateAccount from './CreateAccount';
|
||||||
|
import DeleteAccount from './DeleteAccount';
|
||||||
import DeployContract from './DeployContract';
|
import DeployContract from './DeployContract';
|
||||||
import EditMeta from './EditMeta';
|
import EditMeta from './EditMeta';
|
||||||
import ExecuteContract from './ExecuteContract';
|
import ExecuteContract from './ExecuteContract';
|
||||||
@ -32,6 +33,7 @@ export {
|
|||||||
AddAddress,
|
AddAddress,
|
||||||
AddContract,
|
AddContract,
|
||||||
CreateAccount,
|
CreateAccount,
|
||||||
|
DeleteAccount,
|
||||||
DeployContract,
|
DeployContract,
|
||||||
EditMeta,
|
EditMeta,
|
||||||
ExecuteContract,
|
ExecuteContract,
|
||||||
|
@ -14,9 +14,12 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { throttle } from 'lodash';
|
||||||
|
|
||||||
import { getBalances, getTokens } from './balancesActions';
|
import { getBalances, getTokens } from './balancesActions';
|
||||||
import { setAddressImage } from './imagesActions';
|
import { setAddressImage } from './imagesActions';
|
||||||
|
|
||||||
|
import Contracts from '../../contracts';
|
||||||
import * as abis from '../../contracts/abi';
|
import * as abis from '../../contracts/abi';
|
||||||
|
|
||||||
import imagesEthereum from '../../../assets/images/contracts/ethereum-black-64x64.png';
|
import imagesEthereum from '../../../assets/images/contracts/ethereum-black-64x64.png';
|
||||||
@ -31,13 +34,32 @@ export default class Balances {
|
|||||||
constructor (store, api) {
|
constructor (store, api) {
|
||||||
this._api = api;
|
this._api = api;
|
||||||
this._store = store;
|
this._store = store;
|
||||||
|
|
||||||
|
this._tokens = {};
|
||||||
|
this._images = {};
|
||||||
|
|
||||||
this._accountsInfo = null;
|
this._accountsInfo = null;
|
||||||
this._tokens = [];
|
this._tokenreg = null;
|
||||||
|
this._fetchingBalances = false;
|
||||||
|
this._fetchingTokens = false;
|
||||||
|
this._fetchedTokens = false;
|
||||||
|
|
||||||
|
this._tokenregSubId = null;
|
||||||
|
this._tokenregMetaSubId = null;
|
||||||
|
|
||||||
|
// Throttled `retrieveTokens` function
|
||||||
|
// that gets called max once every 20s
|
||||||
|
this._throttledRetrieveTokens = throttle(
|
||||||
|
this._retrieveTokens,
|
||||||
|
20 * 1000,
|
||||||
|
{ trailing: true }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
start () {
|
start () {
|
||||||
this._subscribeBlockNumber();
|
this._subscribeBlockNumber();
|
||||||
this._subscribeAccountsInfo();
|
this._subscribeAccountsInfo();
|
||||||
|
this._retrieveTokens();
|
||||||
}
|
}
|
||||||
|
|
||||||
_subscribeAccountsInfo () {
|
_subscribeAccountsInfo () {
|
||||||
@ -48,10 +70,7 @@ export default class Balances {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._accountsInfo = accountsInfo;
|
this._accountsInfo = accountsInfo;
|
||||||
this._retrieveBalances();
|
this._retrieveTokens();
|
||||||
})
|
|
||||||
.then((subscriptionId) => {
|
|
||||||
console.log('_subscribeAccountsInfo', 'subscriptionId', subscriptionId);
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.warn('_subscribeAccountsInfo', error);
|
console.warn('_subscribeAccountsInfo', error);
|
||||||
@ -62,139 +81,269 @@ export default class Balances {
|
|||||||
this._api
|
this._api
|
||||||
.subscribe('eth_blockNumber', (error) => {
|
.subscribe('eth_blockNumber', (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
return;
|
return console.warn('_subscribeBlockNumber', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { syncing } = this._store.getState().nodeStatus;
|
||||||
|
|
||||||
|
// If syncing, only retrieve balances once every
|
||||||
|
// few seconds
|
||||||
|
if (syncing) {
|
||||||
|
return this._throttledRetrieveTokens();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._throttledRetrieveTokens.cancel();
|
||||||
this._retrieveTokens();
|
this._retrieveTokens();
|
||||||
})
|
})
|
||||||
.then((subscriptionId) => {
|
|
||||||
console.log('_subscribeBlockNumber', 'subscriptionId', subscriptionId);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.warn('_subscribeBlockNumber', error);
|
console.warn('_subscribeBlockNumber', error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTokenRegistry () {
|
||||||
|
if (this._tokenreg) {
|
||||||
|
return Promise.resolve(this._tokenreg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Contracts.get().tokenReg
|
||||||
|
.getContract()
|
||||||
|
.then((tokenreg) => {
|
||||||
|
this._tokenreg = tokenreg;
|
||||||
|
this.attachToTokens();
|
||||||
|
|
||||||
|
return tokenreg;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_retrieveTokens () {
|
_retrieveTokens () {
|
||||||
this._api.parity
|
if (this._fetchingTokens) {
|
||||||
.registryAddress()
|
return;
|
||||||
.then((registryAddress) => {
|
}
|
||||||
const registry = this._api.newContract(abis.registry, registryAddress);
|
|
||||||
|
|
||||||
return registry.instance.getAddress.call({}, [this._api.util.sha3('tokenreg'), 'A']);
|
if (this._fetchedTokens) {
|
||||||
})
|
return this._retrieveBalances();
|
||||||
.then((tokenregAddress) => {
|
}
|
||||||
const tokenreg = this._api.newContract(abis.tokenreg, tokenregAddress);
|
|
||||||
|
|
||||||
|
this._fetchingTokens = true;
|
||||||
|
this._fetchedTokens = false;
|
||||||
|
|
||||||
|
this
|
||||||
|
.getTokenRegistry()
|
||||||
|
.then((tokenreg) => {
|
||||||
return tokenreg.instance.tokenCount
|
return tokenreg.instance.tokenCount
|
||||||
.call()
|
.call()
|
||||||
.then((numTokens) => {
|
.then((numTokens) => {
|
||||||
const promisesTokens = [];
|
const promises = [];
|
||||||
const promisesImages = [];
|
|
||||||
|
|
||||||
while (promisesTokens.length < numTokens.toNumber()) {
|
for (let i = 0; i < numTokens.toNumber(); i++) {
|
||||||
const index = promisesTokens.length;
|
promises.push(this.fetchTokenInfo(tokenreg, i));
|
||||||
|
|
||||||
promisesTokens.push(tokenreg.instance.token.call({}, [index]));
|
|
||||||
promisesImages.push(tokenreg.instance.meta.call({}, [index, 'IMG']));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all(promises);
|
||||||
Promise.all(promisesTokens),
|
|
||||||
Promise.all(promisesImages)
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.then(([_tokens, images]) => {
|
.then(() => {
|
||||||
const tokens = {};
|
this._fetchingTokens = false;
|
||||||
this._tokens = _tokens
|
this._fetchedTokens = true;
|
||||||
.map((_token, index) => {
|
|
||||||
const [address, tag, format, name] = _token;
|
|
||||||
|
|
||||||
const token = {
|
this._store.dispatch(getTokens(this._tokens));
|
||||||
address,
|
|
||||||
name,
|
|
||||||
tag,
|
|
||||||
format: format.toString(),
|
|
||||||
contract: this._api.newContract(abis.eip20, address)
|
|
||||||
};
|
|
||||||
tokens[address] = token;
|
|
||||||
this._store.dispatch(setAddressImage(address, images[index]));
|
|
||||||
|
|
||||||
return token;
|
|
||||||
})
|
|
||||||
.sort((a, b) => {
|
|
||||||
if (a.tag < b.tag) {
|
|
||||||
return -1;
|
|
||||||
} else if (a.tag > b.tag) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
this._store.dispatch(getTokens(tokens));
|
|
||||||
this._retrieveBalances();
|
this._retrieveBalances();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.warn('_retrieveTokens', error);
|
console.warn('balances::_retrieveTokens', error);
|
||||||
this._retrieveBalances();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_retrieveBalances () {
|
_retrieveBalances () {
|
||||||
|
if (this._fetchingBalances) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this._accountsInfo) {
|
if (!this._accountsInfo) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const addresses = Object.keys(this._accountsInfo);
|
this._fetchingBalances = true;
|
||||||
|
|
||||||
|
const addresses = Object
|
||||||
|
.keys(this._accountsInfo)
|
||||||
|
.filter((address) => {
|
||||||
|
const account = this._accountsInfo[address];
|
||||||
|
return !account.meta || !account.meta.deleted;
|
||||||
|
});
|
||||||
|
|
||||||
this._balances = {};
|
this._balances = {};
|
||||||
|
|
||||||
Promise
|
Promise
|
||||||
.all(
|
.all(addresses.map((a) => this.fetchAccountBalance(a)))
|
||||||
addresses.map((address) => Promise.all([
|
.then((balances) => {
|
||||||
this._api.eth.getBalance(address),
|
addresses.forEach((a, idx) => {
|
||||||
this._api.eth.getTransactionCount(address)
|
this._balances[a] = balances[idx];
|
||||||
]))
|
|
||||||
)
|
|
||||||
.then((balanceTxCount) => {
|
|
||||||
return Promise.all(
|
|
||||||
balanceTxCount.map(([value, txCount], idx) => {
|
|
||||||
const address = addresses[idx];
|
|
||||||
|
|
||||||
this._balances[address] = {
|
|
||||||
txCount,
|
|
||||||
tokens: [{
|
|
||||||
token: ETH,
|
|
||||||
value
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
return Promise.all(
|
|
||||||
this._tokens.map((token) => {
|
|
||||||
return token.contract.instance.balanceOf.call({}, [address]);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.then((tokenBalances) => {
|
|
||||||
addresses.forEach((address, idx) => {
|
|
||||||
const balanceOf = tokenBalances[idx];
|
|
||||||
const balance = this._balances[address];
|
|
||||||
|
|
||||||
this._tokens.forEach((token, tidx) => {
|
|
||||||
balance.tokens.push({
|
|
||||||
token,
|
|
||||||
value: balanceOf[tidx]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this._store.dispatch(getBalances(this._balances));
|
this._store.dispatch(getBalances(this._balances));
|
||||||
|
this._fetchingBalances = false;
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.warn('_retrieveBalances', error);
|
console.warn('_retrieveBalances', error);
|
||||||
|
this._fetchingBalances = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
attachToTokens () {
|
||||||
|
this.attachToTokenMetaChange();
|
||||||
|
this.attachToNewToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
attachToNewToken () {
|
||||||
|
if (this._tokenregSubId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._tokenreg
|
||||||
|
.instance
|
||||||
|
.Registered
|
||||||
|
.subscribe({
|
||||||
|
fromBlock: 0,
|
||||||
|
toBlock: 'latest',
|
||||||
|
skipInitFetch: true
|
||||||
|
}, (error, logs) => {
|
||||||
|
if (error) {
|
||||||
|
return console.error('balances::attachToNewToken', 'failed to attach to tokenreg Registered', error.toString(), error.stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
const promises = logs.map((log) => {
|
||||||
|
const id = log.params.id.value.toNumber();
|
||||||
|
return this.fetchTokenInfo(this._tokenreg, id);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(promises);
|
||||||
|
})
|
||||||
|
.then((tokenregSubId) => {
|
||||||
|
this._tokenregSubId = tokenregSubId;
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.warn('balances::attachToNewToken', e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
attachToTokenMetaChange () {
|
||||||
|
if (this._tokenregMetaSubId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._tokenreg
|
||||||
|
.instance
|
||||||
|
.MetaChanged
|
||||||
|
.subscribe({
|
||||||
|
fromBlock: 0,
|
||||||
|
toBlock: 'latest',
|
||||||
|
topics: [ null, this._api.util.asciiToHex('IMG') ],
|
||||||
|
skipInitFetch: true
|
||||||
|
}, (error, logs) => {
|
||||||
|
if (error) {
|
||||||
|
return console.error('balances::attachToTokenMetaChange', 'failed to attach to tokenreg MetaChanged', error.toString(), error.stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case multiple logs for same token
|
||||||
|
// in one block. Take the last value.
|
||||||
|
const tokens = logs
|
||||||
|
.filter((log) => log.type === 'mined')
|
||||||
|
.reduce((_tokens, log) => {
|
||||||
|
const id = log.params.id.value.toNumber();
|
||||||
|
const image = log.params.value.value;
|
||||||
|
|
||||||
|
const token = Object.values(this._tokens).find((c) => c.id === id);
|
||||||
|
const { address } = token;
|
||||||
|
|
||||||
|
_tokens[address] = { address, id, image };
|
||||||
|
return _tokens;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
Object
|
||||||
|
.values(tokens)
|
||||||
|
.forEach((token) => {
|
||||||
|
const { address, image } = token;
|
||||||
|
|
||||||
|
if (this._images[address] !== image.toString()) {
|
||||||
|
this._store.dispatch(setAddressImage(address, image));
|
||||||
|
this._images[address] = image.toString();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then((tokenregMetaSubId) => {
|
||||||
|
this._tokenregMetaSubId = tokenregMetaSubId;
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.warn('balances::attachToTokenMetaChange', e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchTokenInfo (tokenreg, tokenId) {
|
||||||
|
return Promise
|
||||||
|
.all([
|
||||||
|
tokenreg.instance.token.call({}, [tokenId]),
|
||||||
|
tokenreg.instance.meta.call({}, [tokenId, 'IMG'])
|
||||||
|
])
|
||||||
|
.then(([ tokenData, image ]) => {
|
||||||
|
const [ address, tag, format, name ] = tokenData;
|
||||||
|
const contract = this._api.newContract(abis.eip20, address);
|
||||||
|
|
||||||
|
if (this._images[address] !== image.toString()) {
|
||||||
|
this._store.dispatch(setAddressImage(address, image));
|
||||||
|
this._images[address] = image.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = {
|
||||||
|
format: format.toString(),
|
||||||
|
id: tokenId,
|
||||||
|
|
||||||
|
address,
|
||||||
|
tag,
|
||||||
|
name,
|
||||||
|
contract
|
||||||
|
};
|
||||||
|
|
||||||
|
this._tokens[address] = token;
|
||||||
|
|
||||||
|
return token;
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
console.warn('balances::fetchTokenInfo', `couldn't fetch token #${tokenId}`, e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO?: txCount is only shown on an address page, so we
|
||||||
|
* might not need to fetch it for each address for each block,
|
||||||
|
* but only for one address when the user is on the account
|
||||||
|
* view.
|
||||||
|
*/
|
||||||
|
fetchAccountBalance (address) {
|
||||||
|
const _tokens = Object.values(this._tokens);
|
||||||
|
const tokensPromises = _tokens
|
||||||
|
.map((token) => {
|
||||||
|
return token.contract.instance.balanceOf.call({}, [ address ]);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise
|
||||||
|
.all([
|
||||||
|
this._api.eth.getTransactionCount(address),
|
||||||
|
this._api.eth.getBalance(address)
|
||||||
|
].concat(tokensPromises))
|
||||||
|
.then(([ txCount, ethBalance, ...tokensBalance ]) => {
|
||||||
|
const tokens = []
|
||||||
|
.concat(
|
||||||
|
{ token: ETH, value: ethBalance },
|
||||||
|
_tokens
|
||||||
|
.map((token, index) => ({
|
||||||
|
token,
|
||||||
|
value: tokensBalance[index]
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
const balance = { txCount, tokens };
|
||||||
|
return balance;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,8 +71,8 @@ export default class Status {
|
|||||||
* @see src/views/Connection/connection.js
|
* @see src/views/Connection/connection.js
|
||||||
*/
|
*/
|
||||||
_shouldPing = () => {
|
_shouldPing = () => {
|
||||||
const { isConnected, isConnecting } = this._apiStatus;
|
const { isConnected } = this._apiStatus;
|
||||||
return isConnecting || !isConnected;
|
return !isConnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
_stopPollPing = () => {
|
_stopPollPing = () => {
|
||||||
@ -119,7 +119,7 @@ export default class Status {
|
|||||||
|
|
||||||
_pollStatus = () => {
|
_pollStatus = () => {
|
||||||
const nextTimeout = (timeout = 1000) => {
|
const nextTimeout = (timeout = 1000) => {
|
||||||
setTimeout(this._pollStatus, timeout);
|
setTimeout(() => this._pollStatus(), timeout);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { isConnected, isConnecting, needsToken, secureToken } = this._api;
|
const { isConnected, isConnecting, needsToken, secureToken } = this._api;
|
||||||
@ -134,7 +134,8 @@ export default class Status {
|
|||||||
const gotReconnected = !this._apiStatus.isConnected && apiStatus.isConnected;
|
const gotReconnected = !this._apiStatus.isConnected && apiStatus.isConnected;
|
||||||
|
|
||||||
if (gotReconnected) {
|
if (gotReconnected) {
|
||||||
this._pollLongStatus();
|
this._pollLongStatus(true);
|
||||||
|
this._store.dispatch(statusCollection({ isPingable: true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isEqual(apiStatus, this._apiStatus)) {
|
if (!isEqual(apiStatus, this._apiStatus)) {
|
||||||
@ -155,37 +156,32 @@ export default class Status {
|
|||||||
|
|
||||||
const { refreshStatus } = this._store.getState().nodeStatus;
|
const { refreshStatus } = this._store.getState().nodeStatus;
|
||||||
|
|
||||||
const statusPromises = [ this._api.eth.syncing() ];
|
const statusPromises = [ this._api.eth.syncing(), this._api.parity.netPeers() ];
|
||||||
|
|
||||||
if (refreshStatus) {
|
if (refreshStatus) {
|
||||||
statusPromises.push(this._api.eth.hashrate());
|
statusPromises.push(this._api.eth.hashrate());
|
||||||
statusPromises.push(this._api.parity.netPeers());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise
|
Promise
|
||||||
.all(statusPromises)
|
.all(statusPromises)
|
||||||
.then((statusResults) => {
|
.then(([ syncing, netPeers, ...statusResults ]) => {
|
||||||
const status = statusResults.length === 1
|
const status = statusResults.length === 0
|
||||||
? {
|
? { syncing, netPeers }
|
||||||
syncing: statusResults[0]
|
|
||||||
}
|
|
||||||
: {
|
: {
|
||||||
syncing: statusResults[0],
|
syncing, netPeers,
|
||||||
hashrate: statusResults[1],
|
hashrate: statusResults[0]
|
||||||
netPeers: statusResults[2]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isEqual(status, this._status)) {
|
if (!isEqual(status, this._status)) {
|
||||||
this._store.dispatch(statusCollection(status));
|
this._store.dispatch(statusCollection(status));
|
||||||
this._status = status;
|
this._status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
nextTimeout();
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('_pollStatus', error);
|
console.error('_pollStatus', error);
|
||||||
nextTimeout(250);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
nextTimeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -227,7 +223,11 @@ export default class Status {
|
|||||||
* fetched every 30s just in case, and whenever
|
* fetched every 30s just in case, and whenever
|
||||||
* the client got reconnected.
|
* the client got reconnected.
|
||||||
*/
|
*/
|
||||||
_pollLongStatus = () => {
|
_pollLongStatus = (newConnection = false) => {
|
||||||
|
if (!this._api.isConnected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const nextTimeout = (timeout = 30000) => {
|
const nextTimeout = (timeout = 30000) => {
|
||||||
if (this._longStatusTimeoutId) {
|
if (this._longStatusTimeoutId) {
|
||||||
clearTimeout(this._longStatusTimeoutId);
|
clearTimeout(this._longStatusTimeoutId);
|
||||||
@ -246,12 +246,12 @@ export default class Status {
|
|||||||
this._api.parity.netChain(),
|
this._api.parity.netChain(),
|
||||||
this._api.parity.netPort(),
|
this._api.parity.netPort(),
|
||||||
this._api.parity.rpcSettings(),
|
this._api.parity.rpcSettings(),
|
||||||
this._api.parity.enode()
|
newConnection ? Promise.resolve(null) : this._api.parity.enode()
|
||||||
])
|
])
|
||||||
.then(([
|
.then(([
|
||||||
clientVersion, defaultExtraData, netChain, netPort, rpcSettings, enode
|
clientVersion, defaultExtraData, netChain, netPort, rpcSettings, enode
|
||||||
]) => {
|
]) => {
|
||||||
const isTest = netChain === 'morden' || netChain === 'testnet';
|
const isTest = netChain === 'morden' || netChain === 'ropsten' || netChain === 'testnet';
|
||||||
|
|
||||||
const longStatus = {
|
const longStatus = {
|
||||||
clientVersion,
|
clientVersion,
|
||||||
@ -259,21 +259,23 @@ export default class Status {
|
|||||||
netChain,
|
netChain,
|
||||||
netPort,
|
netPort,
|
||||||
rpcSettings,
|
rpcSettings,
|
||||||
enode,
|
|
||||||
isTest
|
isTest
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (enode) {
|
||||||
|
longStatus.enode = enode;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isEqual(longStatus, this._longStatus)) {
|
if (!isEqual(longStatus, this._longStatus)) {
|
||||||
this._store.dispatch(statusCollection(longStatus));
|
this._store.dispatch(statusCollection(longStatus));
|
||||||
this._longStatus = longStatus;
|
this._longStatus = longStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
nextTimeout();
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('_pollLongStatus', error);
|
console.error('_pollLongStatus', error);
|
||||||
nextTimeout(250);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
nextTimeout(newConnection ? 5000 : 30000);
|
||||||
}
|
}
|
||||||
|
|
||||||
_pollLogs = () => {
|
_pollLogs = () => {
|
||||||
|
@ -31,7 +31,7 @@ const initialState = {
|
|||||||
gasLimit: new BigNumber(0),
|
gasLimit: new BigNumber(0),
|
||||||
hashrate: new BigNumber(0),
|
hashrate: new BigNumber(0),
|
||||||
minGasPrice: new BigNumber(0),
|
minGasPrice: new BigNumber(0),
|
||||||
netChain: 'morden',
|
netChain: 'ropsten',
|
||||||
netPeers: {
|
netPeers: {
|
||||||
active: new BigNumber(0),
|
active: new BigNumber(0),
|
||||||
connected: new BigNumber(0),
|
connected: new BigNumber(0),
|
||||||
@ -39,7 +39,7 @@ const initialState = {
|
|||||||
},
|
},
|
||||||
netPort: new BigNumber(0),
|
netPort: new BigNumber(0),
|
||||||
rpcSettings: {},
|
rpcSettings: {},
|
||||||
syncing: false,
|
syncing: true,
|
||||||
isConnected: false,
|
isConnected: false,
|
||||||
isConnecting: false,
|
isConnecting: false,
|
||||||
isPingable: false,
|
isPingable: false,
|
||||||
|
@ -25,12 +25,13 @@ export default class SecureApi extends Api {
|
|||||||
this._isConnecting = true;
|
this._isConnecting = true;
|
||||||
this._connectState = sysuiToken === 'initial' ? 1 : 0;
|
this._connectState = sysuiToken === 'initial' ? 1 : 0;
|
||||||
this._needsToken = false;
|
this._needsToken = false;
|
||||||
this._nextToken = nextToken;
|
|
||||||
this._dappsPort = 8080;
|
this._dappsPort = 8080;
|
||||||
this._dappsInterface = null;
|
this._dappsInterface = null;
|
||||||
this._signerPort = 8180;
|
this._signerPort = 8180;
|
||||||
|
this._followConnectionTimeoutId = null;
|
||||||
|
|
||||||
console.log('SecureApi:constructor', sysuiToken);
|
// Try tokens from localstorage, then from hash
|
||||||
|
this._tokensToTry = [ sysuiToken, nextToken ].filter((t) => t && t.length);
|
||||||
|
|
||||||
this._followConnection();
|
this._followConnection();
|
||||||
}
|
}
|
||||||
@ -40,15 +41,30 @@ export default class SecureApi extends Api {
|
|||||||
console.log('SecureApi:setToken', this._transport.token);
|
console.log('SecureApi:setToken', this._transport.token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_checkNodeUp () {
|
||||||
|
return fetch('/', { method: 'HEAD' })
|
||||||
|
.then(
|
||||||
|
(r) => r.status === 200,
|
||||||
|
() => false
|
||||||
|
)
|
||||||
|
.catch(() => false);
|
||||||
|
}
|
||||||
|
|
||||||
_followConnection = () => {
|
_followConnection = () => {
|
||||||
const nextTick = () => {
|
const nextTick = () => {
|
||||||
setTimeout(() => this._followConnection(), 250);
|
if (this._followConnectionTimeoutId) {
|
||||||
|
clearTimeout(this._followConnectionTimeoutId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._followConnectionTimeoutId = setTimeout(() => this._followConnection(), 250);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setManual = () => {
|
const setManual = () => {
|
||||||
this._connectState = 100;
|
this._connectState = 100;
|
||||||
this._needsToken = true;
|
this._needsToken = true;
|
||||||
this._isConnecting = false;
|
this._isConnecting = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const lastError = this._transport.lastError;
|
const lastError = this._transport.lastError;
|
||||||
const isConnected = this._transport.isConnected;
|
const isConnected = this._transport.isConnected;
|
||||||
|
|
||||||
@ -58,11 +74,23 @@ export default class SecureApi extends Api {
|
|||||||
if (isConnected) {
|
if (isConnected) {
|
||||||
return this.connectSuccess();
|
return this.connectSuccess();
|
||||||
} else if (lastError) {
|
} else if (lastError) {
|
||||||
const nextToken = this._nextToken || 'initial';
|
return this
|
||||||
const nextState = this._nextToken ? 0 : 1;
|
._checkNodeUp()
|
||||||
|
.then((isNodeUp) => {
|
||||||
|
const nextToken = this._tokensToTry[0] || 'initial';
|
||||||
|
const nextState = nextToken !== 'initial' ? 0 : 1;
|
||||||
|
|
||||||
this._nextToken = null;
|
// If previous token was wrong (error while node up), delete it
|
||||||
this.updateToken(nextToken, nextState);
|
if (isNodeUp) {
|
||||||
|
this._tokensToTry = this._tokensToTry.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextToken !== this._transport.token) {
|
||||||
|
this.updateToken(nextToken, nextState);
|
||||||
|
}
|
||||||
|
|
||||||
|
nextTick();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ export default class TypedInput extends Component {
|
|||||||
<IconButton
|
<IconButton
|
||||||
iconStyle={ iconStyle }
|
iconStyle={ iconStyle }
|
||||||
style={ style }
|
style={ style }
|
||||||
onClick={ this.onAddField }
|
onTouchTap={ this.onAddField }
|
||||||
>
|
>
|
||||||
<AddIcon />
|
<AddIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@ -104,7 +104,7 @@ export default class TypedInput extends Component {
|
|||||||
<IconButton
|
<IconButton
|
||||||
iconStyle={ iconStyle }
|
iconStyle={ iconStyle }
|
||||||
style={ style }
|
style={ style }
|
||||||
onClick={ this.onRemoveField }
|
onTouchTap={ this.onRemoveField }
|
||||||
>
|
>
|
||||||
<RemoveIcon />
|
<RemoveIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
@ -16,26 +16,17 @@
|
|||||||
|
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { bindActionCreators } from 'redux';
|
|
||||||
|
|
||||||
class ParityBackground extends Component {
|
class ParityBackground extends Component {
|
||||||
static contextTypes = {
|
|
||||||
muiTheme: PropTypes.object.isRequired
|
|
||||||
}
|
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
style: PropTypes.object.isRequired,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
gradient: PropTypes.string,
|
|
||||||
seed: PropTypes.any,
|
|
||||||
settings: PropTypes.object.isRequired,
|
|
||||||
onClick: PropTypes.func
|
onClick: PropTypes.func
|
||||||
}
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { muiTheme } = this.context;
|
const { children, className, style, onClick } = this.props;
|
||||||
const { children, className, gradient, seed, settings, onClick } = this.props;
|
|
||||||
const style = muiTheme.parity.getBackgroundStyle(gradient, seed || settings.backgroundSeed);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -48,17 +39,29 @@ class ParityBackground extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (_, initProps) {
|
||||||
const { settings } = state;
|
const { gradient, seed, muiTheme } = initProps;
|
||||||
|
|
||||||
return { settings };
|
let _seed = seed;
|
||||||
}
|
let _props = { style: muiTheme.parity.getBackgroundStyle(gradient, seed) };
|
||||||
|
|
||||||
function mapDispatchToProps (dispatch) {
|
return (state, props) => {
|
||||||
return bindActionCreators({}, dispatch);
|
const { backgroundSeed } = state.settings;
|
||||||
|
const { seed } = props;
|
||||||
|
|
||||||
|
const newSeed = seed || backgroundSeed;
|
||||||
|
|
||||||
|
if (newSeed === _seed) {
|
||||||
|
return _props;
|
||||||
|
}
|
||||||
|
|
||||||
|
_seed = newSeed;
|
||||||
|
_props = { style: muiTheme.parity.getBackgroundStyle(gradient, newSeed) };
|
||||||
|
|
||||||
|
return _props;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
mapStateToProps,
|
mapStateToProps
|
||||||
mapDispatchToProps
|
|
||||||
)(ParityBackground);
|
)(ParityBackground);
|
||||||
|
26
js/src/util/constants.js
Normal file
26
js/src/util/constants.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
const DEFAULT_GAS = '21000';
|
||||||
|
const DEFAULT_GASPRICE = '20000000000';
|
||||||
|
|
||||||
|
const MAX_GAS_ESTIMATION = '50000000';
|
||||||
|
|
||||||
|
export {
|
||||||
|
DEFAULT_GAS,
|
||||||
|
DEFAULT_GASPRICE,
|
||||||
|
MAX_GAS_ESTIMATION
|
||||||
|
};
|
@ -17,12 +17,13 @@
|
|||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
|
import ActionDelete from 'material-ui/svg-icons/action/delete';
|
||||||
import ContentCreate from 'material-ui/svg-icons/content/create';
|
import ContentCreate from 'material-ui/svg-icons/content/create';
|
||||||
import ContentSend from 'material-ui/svg-icons/content/send';
|
import ContentSend from 'material-ui/svg-icons/content/send';
|
||||||
import LockIcon from 'material-ui/svg-icons/action/lock';
|
import LockIcon from 'material-ui/svg-icons/action/lock';
|
||||||
import VerifyIcon from 'material-ui/svg-icons/action/verified-user';
|
import VerifyIcon from 'material-ui/svg-icons/action/verified-user';
|
||||||
|
|
||||||
import { EditMeta, Shapeshift, SMSVerification, Transfer, PasswordManager } from '../../modals';
|
import { EditMeta, DeleteAccount, Shapeshift, SMSVerification, Transfer, PasswordManager } from '../../modals';
|
||||||
import { Actionbar, Button, Page } from '../../ui';
|
import { Actionbar, Button, Page } from '../../ui';
|
||||||
|
|
||||||
import shapeshiftBtn from '../../../assets/images/shapeshift-btn.png';
|
import shapeshiftBtn from '../../../assets/images/shapeshift-btn.png';
|
||||||
@ -50,6 +51,7 @@ class Account extends Component {
|
|||||||
propName = null
|
propName = null
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
showDeleteDialog: false,
|
||||||
showEditDialog: false,
|
showEditDialog: false,
|
||||||
showFundDialog: false,
|
showFundDialog: false,
|
||||||
showVerificationDialog: false,
|
showVerificationDialog: false,
|
||||||
@ -62,8 +64,8 @@ class Account extends Component {
|
|||||||
const { api } = this.context;
|
const { api } = this.context;
|
||||||
const { address } = this.props.params;
|
const { address } = this.props.params;
|
||||||
|
|
||||||
const store = new VerificationStore(api, address);
|
const verificationStore = new VerificationStore(api, address);
|
||||||
this.setState({ verificationStore: store });
|
this.setState({ verificationStore });
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
@ -79,6 +81,7 @@ class Account extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={ styles.account }>
|
<div className={ styles.account }>
|
||||||
|
{ this.renderDeleteDialog(account) }
|
||||||
{ this.renderEditDialog(account) }
|
{ this.renderEditDialog(account) }
|
||||||
{ this.renderFundDialog() }
|
{ this.renderFundDialog() }
|
||||||
{ this.renderVerificationDialog() }
|
{ this.renderVerificationDialog() }
|
||||||
@ -131,7 +134,12 @@ class Account extends Component {
|
|||||||
key='passwordManager'
|
key='passwordManager'
|
||||||
icon={ <LockIcon /> }
|
icon={ <LockIcon /> }
|
||||||
label='password'
|
label='password'
|
||||||
onClick={ this.onPasswordClick } />
|
onClick={ this.onPasswordClick } />,
|
||||||
|
<Button
|
||||||
|
key='delete'
|
||||||
|
icon={ <ActionDelete /> }
|
||||||
|
label='delete account'
|
||||||
|
onClick={ this.onDeleteClick } />
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -141,6 +149,20 @@ class Account extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderDeleteDialog (account) {
|
||||||
|
const { showDeleteDialog } = this.state;
|
||||||
|
|
||||||
|
if (!showDeleteDialog) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DeleteAccount
|
||||||
|
account={ account }
|
||||||
|
onClose={ this.onDeleteClose } />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
renderEditDialog (account) {
|
renderEditDialog (account) {
|
||||||
const { showEditDialog } = this.state;
|
const { showEditDialog } = this.state;
|
||||||
|
|
||||||
@ -228,6 +250,14 @@ class Account extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDeleteClick = () => {
|
||||||
|
this.setState({ showDeleteDialog: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeleteClose = () => {
|
||||||
|
this.setState({ showDeleteDialog: false });
|
||||||
|
}
|
||||||
|
|
||||||
onEditClick = () => {
|
onEditClick = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
showEditDialog: !this.state.showEditDialog
|
showEditDialog: !this.state.showEditDialog
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
|
import { isEqual } from 'lodash';
|
||||||
|
|
||||||
import { Balance, Container, ContainerTitle, IdentityIcon, IdentityName, Tags, Input } from '../../../ui';
|
import { Balance, Container, ContainerTitle, IdentityIcon, IdentityName, Tags, Input } from '../../../ui';
|
||||||
|
|
||||||
@ -30,7 +31,6 @@ export default class Summary extends Component {
|
|||||||
link: PropTypes.string,
|
link: PropTypes.string,
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
noLink: PropTypes.bool,
|
noLink: PropTypes.bool,
|
||||||
children: PropTypes.node,
|
|
||||||
handleAddSearchToken: PropTypes.func
|
handleAddSearchToken: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -42,8 +42,42 @@ export default class Summary extends Component {
|
|||||||
name: 'Unnamed'
|
name: 'Unnamed'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
shouldComponentUpdate (nextProps) {
|
||||||
|
const prev = {
|
||||||
|
link: this.props.link, name: this.props.name,
|
||||||
|
noLink: this.props.noLink,
|
||||||
|
meta: this.props.account.meta, address: this.props.account.address
|
||||||
|
};
|
||||||
|
|
||||||
|
const next = {
|
||||||
|
link: nextProps.link, name: nextProps.name,
|
||||||
|
noLink: nextProps.noLink,
|
||||||
|
meta: nextProps.account.meta, address: nextProps.account.address
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isEqual(next, prev)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prevTokens = this.props.balance.tokens || [];
|
||||||
|
const nextTokens = nextProps.balance.tokens || [];
|
||||||
|
|
||||||
|
if (prevTokens.length !== nextTokens.length) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prevValues = prevTokens.map((t) => t.value.toNumber());
|
||||||
|
const nextValues = nextTokens.map((t) => t.value.toNumber());
|
||||||
|
|
||||||
|
if (!isEqual(prevValues, nextValues)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { account, children, handleAddSearchToken } = this.props;
|
const { account, handleAddSearchToken } = this.props;
|
||||||
const { tags } = account.meta;
|
const { tags } = account.meta;
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
@ -71,7 +105,6 @@ export default class Summary extends Component {
|
|||||||
byline={ addressComponent } />
|
byline={ addressComponent } />
|
||||||
|
|
||||||
{ this.renderBalance() }
|
{ this.renderBalance() }
|
||||||
{ children }
|
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -30,3 +30,25 @@
|
|||||||
right: 1em;
|
right: 1em;
|
||||||
top: 4em;
|
top: 4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loadings {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
flex: 0 1 50%;
|
||||||
|
width: 50%;
|
||||||
|
height: 150px;
|
||||||
|
display: flex;
|
||||||
|
padding: 0.25em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -42,33 +42,63 @@ class Accounts extends Component {
|
|||||||
newDialog: false,
|
newDialog: false,
|
||||||
sortOrder: '',
|
sortOrder: '',
|
||||||
searchValues: [],
|
searchValues: [],
|
||||||
searchTokens: []
|
searchTokens: [],
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount () {
|
||||||
|
window.setTimeout(() => {
|
||||||
|
this.setState({ show: true });
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { accounts, hasAccounts, balances } = this.props;
|
|
||||||
const { searchValues, sortOrder } = this.state;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={ styles.accounts }>
|
<div className={ styles.accounts }>
|
||||||
{ this.renderNewDialog() }
|
{ this.renderNewDialog() }
|
||||||
{ this.renderActionbar() }
|
{ this.renderActionbar() }
|
||||||
<Page>
|
|
||||||
<List
|
{ this.state.show ? this.renderAccounts() : this.renderLoading() }
|
||||||
search={ searchValues }
|
|
||||||
accounts={ accounts }
|
|
||||||
balances={ balances }
|
|
||||||
empty={ !hasAccounts }
|
|
||||||
order={ sortOrder }
|
|
||||||
handleAddSearchToken={ this.onAddSearchToken } />
|
|
||||||
<Tooltip
|
|
||||||
className={ styles.accountTooltip }
|
|
||||||
text='your accounts are visible for easy access, allowing you to edit the meta information, make transfers, view transactions and fund the account' />
|
|
||||||
</Page>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderLoading () {
|
||||||
|
const { accounts } = this.props;
|
||||||
|
|
||||||
|
const loadings = ((accounts && Object.keys(accounts)) || []).map((_, idx) => (
|
||||||
|
<div key={ idx } className={ styles.loading }>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={ styles.loadings }>
|
||||||
|
{ loadings }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAccounts () {
|
||||||
|
const { accounts, hasAccounts, balances } = this.props;
|
||||||
|
const { searchValues, sortOrder } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Page>
|
||||||
|
<List
|
||||||
|
search={ searchValues }
|
||||||
|
accounts={ accounts }
|
||||||
|
balances={ balances }
|
||||||
|
empty={ !hasAccounts }
|
||||||
|
order={ sortOrder }
|
||||||
|
handleAddSearchToken={ this.onAddSearchToken } />
|
||||||
|
<Tooltip
|
||||||
|
className={ styles.accountTooltip }
|
||||||
|
text='your accounts are visible for easy access, allowing you to edit the meta information, make transfers, view transactions and fund the account' />
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
renderSearchButton () {
|
renderSearchButton () {
|
||||||
const onChange = (searchTokens, searchValues) => {
|
const onChange = (searchTokens, searchValues) => {
|
||||||
this.setState({ searchTokens, searchValues });
|
this.setState({ searchTokens, searchValues });
|
||||||
|
@ -22,6 +22,10 @@ import { Errors, ParityBackground, Tooltips } from '../../../ui';
|
|||||||
import styles from '../application.css';
|
import styles from '../application.css';
|
||||||
|
|
||||||
export default class Container extends Component {
|
export default class Container extends Component {
|
||||||
|
static contextTypes = {
|
||||||
|
muiTheme: PropTypes.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
children: PropTypes.node.isRequired,
|
children: PropTypes.node.isRequired,
|
||||||
showFirstRun: PropTypes.bool,
|
showFirstRun: PropTypes.bool,
|
||||||
@ -30,9 +34,10 @@ export default class Container extends Component {
|
|||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { children, showFirstRun, onCloseFirstRun } = this.props;
|
const { children, showFirstRun, onCloseFirstRun } = this.props;
|
||||||
|
const { muiTheme } = this.context;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ParityBackground className={ styles.container }>
|
<ParityBackground className={ styles.container } muiTheme={ muiTheme }>
|
||||||
<FirstRun
|
<FirstRun
|
||||||
visible={ showFirstRun }
|
visible={ showFirstRun }
|
||||||
onClose={ onCloseFirstRun } />
|
onClose={ onCloseFirstRun } />
|
||||||
|
@ -23,6 +23,11 @@
|
|||||||
.tabs {
|
.tabs {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs button,
|
.tabs button,
|
||||||
@ -38,6 +43,7 @@
|
|||||||
|
|
||||||
button.tabactive,
|
button.tabactive,
|
||||||
button.tabactive:hover {
|
button.tabactive:hover {
|
||||||
|
color: white !important;
|
||||||
background: rgba(0, 0, 0, 0.25) !important;
|
background: rgba(0, 0, 0, 0.25) !important;
|
||||||
border-radius: 4px 4px 0 0;
|
border-radius: 4px 4px 0 0;
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ import React, { Component, PropTypes } from 'react';
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar';
|
import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar';
|
||||||
import { Tabs, Tab } from 'material-ui/Tabs';
|
import { Tab as MUITab } from 'material-ui/Tabs';
|
||||||
|
|
||||||
import { Badge, Tooltip } from '../../../ui';
|
import { Badge, Tooltip } from '../../../ui';
|
||||||
|
|
||||||
@ -33,20 +33,138 @@ const TABMAP = {
|
|||||||
deploy: 'contract'
|
deploy: 'contract'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Tab extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
active: PropTypes.bool,
|
||||||
|
view: PropTypes.object,
|
||||||
|
children: PropTypes.node,
|
||||||
|
pendings: PropTypes.number,
|
||||||
|
onChange: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
shouldComponentUpdate (nextProps) {
|
||||||
|
return nextProps.active !== this.props.active ||
|
||||||
|
(nextProps.view.id === 'signer' && nextProps.pendings !== this.props.pendings);
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { active, view, children } = this.props;
|
||||||
|
|
||||||
|
const label = this.getLabel(view);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MUITab
|
||||||
|
className={ active ? styles.tabactive : '' }
|
||||||
|
selected={ active }
|
||||||
|
icon={ view.icon }
|
||||||
|
label={ label }
|
||||||
|
onClick={ this.handleClick }
|
||||||
|
>
|
||||||
|
{ children }
|
||||||
|
</MUITab>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLabel (view) {
|
||||||
|
const { label } = view;
|
||||||
|
|
||||||
|
if (view.id === 'signer') {
|
||||||
|
return this.renderSignerLabel(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (view.id === 'status') {
|
||||||
|
return this.renderStatusLabel(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.renderLabel(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderLabel (name, bubble) {
|
||||||
|
return (
|
||||||
|
<div className={ styles.label }>
|
||||||
|
{ name }
|
||||||
|
{ bubble }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSignerLabel (label) {
|
||||||
|
const { pendings } = this.props;
|
||||||
|
|
||||||
|
if (pendings) {
|
||||||
|
const bubble = (
|
||||||
|
<Badge
|
||||||
|
color='red'
|
||||||
|
className={ styles.labelBubble }
|
||||||
|
value={ pendings } />
|
||||||
|
);
|
||||||
|
|
||||||
|
return this.renderLabel(label, bubble);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.renderLabel(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderStatusLabel (label) {
|
||||||
|
// const { isTest, netChain } = this.props;
|
||||||
|
// const bubble = (
|
||||||
|
// <Badge
|
||||||
|
// color={ isTest ? 'red' : 'default' }
|
||||||
|
// className={ styles.labelBubble }
|
||||||
|
// value={ isTest ? 'TEST' : netChain } />
|
||||||
|
// );
|
||||||
|
|
||||||
|
return this.renderLabel(label, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClick = () => {
|
||||||
|
const { onChange, view } = this.props;
|
||||||
|
onChange(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class TabBar extends Component {
|
class TabBar extends Component {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
router: PropTypes.object.isRequired
|
router: PropTypes.object.isRequired
|
||||||
}
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
views: PropTypes.array.isRequired,
|
||||||
|
hash: PropTypes.string.isRequired,
|
||||||
pending: PropTypes.array,
|
pending: PropTypes.array,
|
||||||
isTest: PropTypes.bool,
|
isTest: PropTypes.bool,
|
||||||
netChain: PropTypes.string,
|
netChain: PropTypes.string
|
||||||
settings: PropTypes.object.isRequired
|
};
|
||||||
}
|
|
||||||
|
static defaultProps = {
|
||||||
|
pending: []
|
||||||
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
activeRoute: '/accounts'
|
activeViewId: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
setActiveView (props = this.props) {
|
||||||
|
const { hash, views } = props;
|
||||||
|
const view = views.find((view) => view.value === hash);
|
||||||
|
|
||||||
|
this.setState({ activeViewId: view.id });
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount () {
|
||||||
|
this.setActiveView();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps (nextProps) {
|
||||||
|
if (nextProps.hash !== this.props.hash) {
|
||||||
|
this.setActiveView(nextProps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldComponentUpdate (nextProps, nextState) {
|
||||||
|
return (nextProps.hash !== this.props.hash) ||
|
||||||
|
(nextProps.pending.length !== this.props.pending.length) ||
|
||||||
|
(nextState.activeViewId !== this.state.activeViewId);
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
@ -81,100 +199,64 @@ class TabBar extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderTabs () {
|
renderTabs () {
|
||||||
const { settings } = this.props;
|
const { views, pending } = this.props;
|
||||||
const windowHash = (window.location.hash || '').split('?')[0].split('/')[1];
|
const { activeViewId } = this.state;
|
||||||
const hash = TABMAP[windowHash] || windowHash;
|
|
||||||
|
|
||||||
const items = Object.keys(settings.views)
|
const items = views
|
||||||
.filter((id) => settings.views[id].fixed || settings.views[id].active)
|
.map((view, index) => {
|
||||||
.map((id) => {
|
const body = (view.id === 'accounts')
|
||||||
const view = settings.views[id];
|
? (
|
||||||
let label = this.renderLabel(view.label);
|
<Tooltip className={ styles.tabbarTooltip } text='navigate between the different parts and views of the application, switching between an account view, token view and distributed application view' />
|
||||||
let body = null;
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
if (id === 'accounts') {
|
const active = activeViewId === view.id;
|
||||||
body = (
|
|
||||||
<Tooltip className={ styles.tabbarTooltip } text='navigate between the different parts and views of the application, switching between an account view, token view and distributed application view' />
|
|
||||||
);
|
|
||||||
} else if (id === 'signer') {
|
|
||||||
label = this.renderSignerLabel(label);
|
|
||||||
} else if (id === 'status') {
|
|
||||||
label = this.renderStatusLabel(label);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tab
|
<Tab
|
||||||
className={ hash === view.value ? styles.tabactive : '' }
|
active={ active }
|
||||||
value={ view.value }
|
view={ view }
|
||||||
icon={ view.icon }
|
onChange={ this.onChange }
|
||||||
key={ id }
|
key={ index }
|
||||||
label={ label }
|
pendings={ pending.length }
|
||||||
onActive={ this.onActivate(view.route) }>
|
>
|
||||||
{ body }
|
{ body }
|
||||||
</Tab>
|
</Tab>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs
|
<div
|
||||||
className={ styles.tabs }
|
className={ styles.tabs }
|
||||||
value={ hash }>
|
onChange={ this.onChange }>
|
||||||
{ items }
|
{ items }
|
||||||
</Tabs>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderLabel = (name, bubble) => {
|
|
||||||
return (
|
|
||||||
<div className={ styles.label }>
|
|
||||||
{ name }
|
|
||||||
{ bubble }
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSignerLabel = (label) => {
|
onChange = (view) => {
|
||||||
const { pending } = this.props;
|
|
||||||
let bubble = null;
|
|
||||||
|
|
||||||
if (pending && pending.length) {
|
|
||||||
bubble = (
|
|
||||||
<Badge
|
|
||||||
color='red'
|
|
||||||
className={ styles.labelBubble }
|
|
||||||
value={ pending.length } />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.renderLabel(label, bubble);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderStatusLabel = (label) => {
|
|
||||||
// const { isTest, netChain } = this.props;
|
|
||||||
// const bubble = (
|
|
||||||
// <Badge
|
|
||||||
// color={ isTest ? 'red' : 'default' }
|
|
||||||
// className={ styles.labelBubble }
|
|
||||||
// value={ isTest ? 'TEST' : netChain } />
|
|
||||||
// );
|
|
||||||
|
|
||||||
return this.renderLabel(label, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
onActivate = (activeRoute) => {
|
|
||||||
const { router } = this.context;
|
const { router } = this.context;
|
||||||
|
|
||||||
return (event) => {
|
router.push(view.route);
|
||||||
router.push(activeRoute);
|
this.setState({ activeViewId: view.id });
|
||||||
this.setState({ activeRoute });
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapStateToProps (state) {
|
function mapStateToProps (state) {
|
||||||
const { settings } = state;
|
const { views } = state.settings;
|
||||||
|
|
||||||
return { settings };
|
const filteredViews = Object
|
||||||
|
.keys(views)
|
||||||
|
.filter((id) => views[id].fixed || views[id].active)
|
||||||
|
.map((id) => ({
|
||||||
|
...views[id],
|
||||||
|
id
|
||||||
|
}));
|
||||||
|
|
||||||
|
const windowHash = (window.location.hash || '').split('?')[0].split('/')[1];
|
||||||
|
const hash = TABMAP[windowHash] || windowHash;
|
||||||
|
|
||||||
|
return { views: filteredViews, hash };
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapDispatchToProps (dispatch) {
|
function mapDispatchToProps (dispatch) {
|
||||||
|
@ -52,6 +52,7 @@
|
|||||||
"description": "Have a peak on internals of transaction queue of your node.",
|
"description": "Have a peak on internals of transaction queue of your node.",
|
||||||
"author": "Parity Team <admin@ethcore.io>",
|
"author": "Parity Team <admin@ethcore.io>",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"visible": true,
|
||||||
"secure": true
|
"secure": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user