├── CHANGELOG.md ├── docs └── .gitignore ├── config └── config.go ├── filter ├── switch_filter.go ├── common.go ├── transaction │ ├── tx_filter.go │ └── tx_filter_test.go ├── common_test.go └── block │ ├── block_filter.go │ ├── block_filter_test.go │ ├── state.go │ ├── worker.go │ ├── worker_test.go │ └── state_test.go ├── AUTHORS.md ├── .dockerignore ├── contrib ├── git │ ├── .gitconfig.tmp │ └── .gitmessage.tmp └── LICENSE-APPLY ├── test └── test_cov.sh ├── .gitattributes ├── doc.go ├── scripts ├── unit_test_cov.sh ├── changelog.sh ├── check_spelling.sh ├── install_behave.sh ├── ensure_deps.sh └── check_license.sh ├── .codecov.yml ├── dependencies.txt ├── README.md ├── .gitignore ├── .circleci └── config.yml ├── interface.go ├── version └── version.go ├── util ├── bloom_util.go └── types_util.go ├── port ├── port_test.go └── port.go ├── CONTRIBUTING.md ├── Makefile ├── switch_test.go ├── switch.go ├── LICENSE └── LICENSE.md /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _build/ 2 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type SwitchConfig struct { 4 | VerifySignature bool 5 | ChainID uint64 6 | } 7 | -------------------------------------------------------------------------------- /filter/switch_filter.go: -------------------------------------------------------------------------------- 1 | package filter 2 | 3 | // Filter is used to verify SwitchMsg 4 | type SwitchFilter interface { 5 | Verify(portId int, msg interface{}) error 6 | } 7 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # Credits 2 | 3 | ## Development Lead 4 | 5 | - Ad.BChain [bchain618](https://github.com/bchain618) 6 | 7 | ## Contributors 8 | 9 | None yet. Why not be the first? 10 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright(c) 2018 DSiSc Group. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | .git 7 | .circleci 8 | .github 9 | .codecov.yml 10 | .mailmap 11 | .travis.yml 12 | -------------------------------------------------------------------------------- /contrib/git/.gitconfig.tmp: -------------------------------------------------------------------------------- 1 | [commit] 2 | template = ~/.gitmessage 3 | 4 | [alias] 5 | co = checkout 6 | ci = commit 7 | br = branch 8 | st = status 9 | last = log -1 10 | lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit 11 | -------------------------------------------------------------------------------- /test/test_cov.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -e 4 | 5 | echo "" > coverage.txt 6 | 7 | for pkg in $(go list ./... | grep -v vendor); do 8 | go test -timeout 5m -race -coverprofile=profile.cov -covermode=atomic "$pkg" 9 | if [ -f profile.cov ]; then 10 | cat profile.cov >> coverage.txt 11 | rm profile.cov 12 | fi 13 | done 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | 3 | * text=auto 4 | 5 | *.sh text eol=lf 6 | *.go text eol=lf 7 | *.yaml text eol=lf 8 | *.yml text eol=lf 9 | *.md text eol=lf 10 | *.json text eol=lf 11 | *.proto text eol=lf 12 | *.py text eol=lf 13 | *.js text eol=lf 14 | *.txt text eol=lf 15 | *.sol linguist-language=Solidity 16 | LICENSE text eol=lf 17 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package gossipswitch implements the gossip switch(receive the message from InPort, filter the received message, 3 | and then broadcast the message to OutPort). 4 | 5 | The gossipswitch package implements one complete gossip switch, include: switch InPort(receive message), switch 6 | OutPort(send message),two common message filter(TxFilter: filter transaction message, BlockFilter:filter block 7 | message). 8 | */ 9 | package gossipswitch 10 | -------------------------------------------------------------------------------- /scripts/unit_test_cov.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -e 4 | 5 | # Change directory to project root folder 6 | PROJ_FOLDER=$(cd "$(dirname "$0")/..";pwd) 7 | cd $PROJ_FOLDER 8 | 9 | echo "" > coverage.txt 10 | 11 | for pkg in $(go list ./... | grep -v vendor); do 12 | go test -timeout 5m -race -coverprofile=profile.cov -covermode=atomic "$pkg" 13 | if [ -f profile.cov ]; then 14 | cat profile.cov >> coverage.txt 15 | rm profile.cov 16 | fi 17 | done 18 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | require_ci_to_pass: yes 4 | 5 | coverage: 6 | precision: 2 7 | round: down 8 | range: "50...80" 9 | 10 | status: 11 | project: yes 12 | patch: yes 13 | changes: no 14 | 15 | parsers: 16 | gcov: 17 | branch_detection: 18 | conditional: yes 19 | loop: yes 20 | method: no 21 | macro: no 22 | 23 | comment: 24 | layout: "header, diff" 25 | behavior: default 26 | require_changes: no 27 | -------------------------------------------------------------------------------- /dependencies.txt: -------------------------------------------------------------------------------- 1 | # Imported packages that does not exsit under "vendor" folder. 2 | # The following lines listed by git repositories(each line for one git repo) 3 | # alone with compatible version(branch/tag/commit-id). 4 | 5 | github.com/DSiSc/craft:master 6 | github.com/DSiSc/repository:master 7 | github.com/DSiSc/evm-NG:master 8 | github.com/DSiSc/wallet:master 9 | github.com/DSiSc/monkey:master 10 | github.com/DSiSc/validator:master 11 | github.com/DSiSc/crypto-suite:master 12 | github.com/DSiSc/wasm:master 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gossipswitch 2 | 3 | A Gossip switch implementation. 4 | 5 | [![Build Status](https://circleci.com/gh/DSiSc/gossipswitch/tree/master.svg?style=shield)](https://circleci.com/gh/DSiSc/gossipswitch/tree/master) 6 | [![codecov](https://codecov.io/gh/DSiSc/gossipswitch/branch/master/graph/badge.svg)](https://codecov.io/gh/DSiSc/gossipswitch) 7 | 8 | ## Getting started 9 | 10 | Running it then should be as simple as: 11 | 12 | ``` 13 | $ make all 14 | ``` 15 | 16 | ### Testing 17 | 18 | ``` 19 | $ make test 20 | ``` 21 | 22 | -------------------------------------------------------------------------------- /contrib/git/.gitmessage.tmp: -------------------------------------------------------------------------------- 1 | # head: (): 2 | # - type: feat, fix, docs, style, refactor, test, chore 3 | # - scope: can be empty (eg. if the change is a global or difficult to assign to a single component) 4 | # - subject: start with verb (such as 'change'), 50-character line 5 | # 6 | # body: 72-character wrapped. This should answer: 7 | # * Why was this change necessary? 8 | # * How does it address the problem? 9 | # * Are there any side effects? 10 | # 11 | # footer: 12 | # - Include a link to the ticket, if any. 13 | # - BREAKING CHANGE 14 | # 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile ~/.gitignore_global 6 | 7 | # govendor 8 | #vendor/ 9 | 10 | # IDEs 11 | .project 12 | .settings 13 | .idea 14 | .vscode 15 | 16 | # May be used by the Makefile 17 | build/_workspace/ 18 | build/_vendor/pkg 19 | build/bin/ 20 | 21 | # travis, codecov 22 | profile.tmp 23 | profile.cov 24 | coverage.txt 25 | 26 | # tmp 27 | *.sw? 28 | -------------------------------------------------------------------------------- /scripts/changelog.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright(c) 2018 DSiSc Group. All Rights Reserved. 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | 8 | set -x 9 | 10 | SCRIPT_DIR=$(readlink -f "$(dirname $0)") 11 | CHANGELOG_TEMP="CHANGELOG.new" 12 | 13 | echo "## $2\n$(date)" >> ${CHANGELOG_TEMP} 14 | echo "" >> ${CHANGELOG_TEMP} 15 | git log $1..HEAD --oneline | grep -v Merge | sed -e "s/\([0-9|a-z]*\)/* \[\1\](https:\/\/github.com\/DSiSc\/gossipswitch\/commit\/\1)/" >> ${CHANGELOG_TEMP} 16 | echo "" >> ${CHANGELOG_TEMP} 17 | cat ${SCRIPT_DIR}/../CHANGELOG.md >> ${CHANGELOG_TEMP} 18 | mv -f ${CHANGELOG_TEMP} CHANGELOG.md 19 | -------------------------------------------------------------------------------- /contrib/LICENSE-APPLY: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2018 DSiSc Group. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Golang CircleCI 2.0 configuration file 2 | 3 | version: 2 4 | 5 | jobs: 6 | build: 7 | 8 | docker: 9 | - image: circleci/golang:1.10.3 10 | working_directory: /go/src/github.com/DSiSc/gossipswitch 11 | 12 | steps: 13 | - checkout 14 | 15 | - run: 16 | name: Get dependencies 17 | command: make fetch-deps 18 | 19 | - run: 20 | name: Static checks 21 | command: make static-check 22 | 23 | - run: 24 | name: Correctness check 25 | command: make build && make vet 26 | 27 | - run: 28 | name: Test with coverage 29 | command: | 30 | make coverage 31 | bash <(curl -s https://codecov.io/bash) 32 | -------------------------------------------------------------------------------- /interface.go: -------------------------------------------------------------------------------- 1 | package gossipswitch 2 | 3 | import "github.com/DSiSc/gossipswitch/port" 4 | 5 | // GossipSwitchAPI is gossipswitch's public api. 6 | type GossipSwitchAPI interface { 7 | // InPort get switch's in port by port id, return nil if there is no port with specific id. 8 | InPort(portId int) *port.InPort 9 | 10 | // InPort get switch's out port by port id, return nil if there is no port with specific id. 11 | OutPort(portId int) *port.OutPort 12 | 13 | // Start start the switch. Once started, switch will receive message from in port, and broadcast to 14 | // out port 15 | Start() error 16 | 17 | // Stop stop the switch. Once stopped, switch will stop to receive and broadcast message 18 | Stop() error 19 | 20 | // IsRunning is used to query switch's current status. Return true if running, otherwise false 21 | IsRunning() bool 22 | } 23 | -------------------------------------------------------------------------------- /scripts/check_spelling.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright(c) 2018 DSiSc Group. All Rights Reserved. 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | 8 | CHECK=$(git diff --name-only HEAD * | grep -v .png$ | grep -v .git | grep -v ^CHANGELOG \ 9 | | grep -v ^vendor/ | grep -v ^build/ | sort -u) 10 | 11 | if [[ -z "$CHECK" ]]; then 12 | CHECK=$(git diff-tree --no-commit-id --name-only -r $(git log -2 \ 13 | --pretty=format:"%h") | grep -v .png$ | grep -v .git | grep -v ^CHANGELOG \ 14 | | grep -v ^vendor/ | grep -v ^build/ | sort -u) 15 | fi 16 | 17 | echo "Checking changed go files for spelling errors ..." 18 | errs=`echo $CHECK | xargs misspell -source=text` 19 | if [ -z "$errs" ]; then 20 | echo "spell checker passed" 21 | exit 0 22 | fi 23 | echo "The following files are have spelling errors:" 24 | echo "$errs" 25 | exit 0 26 | -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2018 DSiSc Group. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package version 16 | 17 | // The git commit that was compiled. This will be filled in by the compiler. 18 | var GitCommit string 19 | 20 | // The main version number that is being run at the moment. 21 | const Version = "0.0.1" 22 | 23 | // A pre-release marker for the version. If this is "" (empty string) 24 | // then it means that it is a final release. Otherwise, this is a pre-release 25 | // such as "dev" (in development) 26 | var VersionPrerelease = "dev" 27 | 28 | var BuildDate = "" 29 | -------------------------------------------------------------------------------- /scripts/install_behave.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright(c) 2018 DSiSc Group. All Rights Reserved. 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | 8 | 9 | # 10 | # This script is used on Debian based linux distros. 11 | # (i.e., linux that supports the apt packaging manager.) 12 | # 13 | 14 | # Update system 15 | apt-get update -qq 16 | 17 | # Install Python, pip, behave 18 | # 19 | # install python-dev and libyaml-dev to get compiled speedups 20 | apt-get install --yes python-dev 21 | apt-get install --yes libyaml-dev 22 | 23 | apt-get install --yes python-setuptools 24 | apt-get install --yes python-pip 25 | apt-get install --yes build-essential 26 | # required dependencies for cryptography, which is required by pyOpenSSL 27 | # https://cryptography.io/en/stable/installation/#building-cryptography-on-linux 28 | apt-get install --yes libssl-dev libffi-dev 29 | pip install --upgrade pip 30 | 31 | # Pip packages required for behave tests 32 | pip install -r ../devenv/bddtests-requirements.txt 33 | 34 | # install ruby and apiaryio 35 | #apt-get install --yes ruby ruby-dev gcc 36 | #gem install apiaryio 37 | 38 | # Install Tcl prerequisites for busywork 39 | apt-get install --yes tcl tclx tcllib 40 | 41 | # Install NPM for the SDK 42 | apt-get install --yes npm 43 | -------------------------------------------------------------------------------- /filter/common.go: -------------------------------------------------------------------------------- 1 | package filter 2 | 3 | import ( 4 | gconf "github.com/DSiSc/craft/config" 5 | "github.com/DSiSc/craft/rlp" 6 | "github.com/DSiSc/craft/types" 7 | "github.com/DSiSc/crypto-suite/crypto/sha3" 8 | "hash" 9 | ) 10 | 11 | // get hash algorithm by global config 12 | func HashAlg() hash.Hash { 13 | var alg string 14 | if value, ok := gconf.GlobalConfig.Load(gconf.HashAlgName); ok { 15 | alg = value.(string) 16 | } else { 17 | alg = "SHA256" 18 | } 19 | return sha3.NewHashByAlgName(alg) 20 | } 21 | 22 | // calculate the hash value of the rlp encoded byte of x 23 | func rlpHash(x interface{}) (h types.Hash) { 24 | hw := HashAlg() 25 | rlp.Encode(hw, x) 26 | hw.Sum(h[:0]) 27 | return h 28 | } 29 | 30 | // TxHash calculate tx's hash 31 | func TxHash(tx *types.Transaction) (hash types.Hash) { 32 | if hash := tx.Hash.Load(); hash != nil { 33 | return hash.(types.Hash) 34 | } 35 | v := rlpHash(tx) 36 | tx.Hash.Store(v) 37 | return v 38 | } 39 | 40 | // HeaderHash calculate block's hash 41 | func HeaderHash(block *types.Block) (hash types.Hash) { 42 | //var defaultHash types.Hash 43 | return rlpHash(block.Header) 44 | } 45 | 46 | type RefAddress struct { 47 | Addr types.Address 48 | } 49 | 50 | func NewRefAddress(addr types.Address) *RefAddress { 51 | return &RefAddress{Addr: addr} 52 | } 53 | 54 | func (self *RefAddress) Address() types.Address { 55 | return self.Addr 56 | } 57 | -------------------------------------------------------------------------------- /util/bloom_util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/types" 6 | "github.com/DSiSc/crypto-suite/crypto" 7 | "math/big" 8 | ) 9 | 10 | // SetBytes sets the content of b to the given bytes. 11 | // It panics if d is not of suitable size. 12 | func SetBytes(d []byte, b *types.Bloom) { 13 | if len(b) < len(d) { 14 | panic(fmt.Sprintf("bloom bytes too big %d %d", len(b), len(d))) 15 | } 16 | copy(b[types.BloomByteLength-len(d):], d) 17 | } 18 | 19 | // BytesToBloom converts a byte slice to a bloom filter. 20 | // It panics if b is not of suitable size. 21 | func BytesToBloom(b []byte) types.Bloom { 22 | var bloom types.Bloom 23 | SetBytes(b, &bloom) 24 | return bloom 25 | } 26 | 27 | func bloom9(b []byte) *big.Int { 28 | b = crypto.Keccak256(b) 29 | 30 | r := new(big.Int) 31 | 32 | for i := 0; i < 6; i += 2 { 33 | t := big.NewInt(1) 34 | b := (uint(b[i+1]) + (uint(b[i]) << 8)) & 2047 35 | r.Or(r, t.Lsh(t, b)) 36 | } 37 | 38 | return r 39 | } 40 | 41 | func LogsBloom(logs []*types.Log) *big.Int { 42 | bin := new(big.Int) 43 | for _, log := range logs { 44 | bin.Or(bin, bloom9(log.Address[:])) 45 | for _, b := range log.Topics { 46 | bin.Or(bin, bloom9(b[:])) 47 | } 48 | } 49 | 50 | return bin 51 | } 52 | 53 | func CreateBloom(receipts types.Receipts) types.Bloom { 54 | bin := new(big.Int) 55 | for _, receipt := range receipts { 56 | bin.Or(bin, LogsBloom(receipt.Logs)) 57 | } 58 | 59 | return BytesToBloom(bin.Bytes()) 60 | } 61 | -------------------------------------------------------------------------------- /util/types_util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "encoding/hex" 5 | "github.com/DSiSc/craft/types" 6 | ) 7 | 8 | // Lengths of hashes and addresses in bytes. 9 | const ( 10 | HashLength = 32 11 | AddressLength = 20 12 | ) 13 | 14 | // BytesToAddress returns Address with value b. 15 | // If b is larger than len(h), b will be cropped from the left. 16 | func BytesToAddress(b []byte) types.Address { 17 | var a types.Address 18 | if len(b) > len(a) { 19 | b = b[len(b)-AddressLength:] 20 | } 21 | copy(a[AddressLength-len(b):], b) 22 | return a 23 | } 24 | 25 | // HexToAddress returns Address with byte values of s. 26 | // If s is larger than len(h), s will be cropped from the left. 27 | func HexToAddress(s string) types.Address { return BytesToAddress(FromHex(s)) } 28 | 29 | // FromHex returns the bytes represented by the hexadecimal string s. 30 | // s may be prefixed with "0x". 31 | func FromHex(s string) []byte { 32 | if len(s) > 1 { 33 | if s[0:2] == "0x" || s[0:2] == "0X" { 34 | s = s[2:] 35 | } 36 | } 37 | if len(s)%2 == 1 { 38 | s = "0" + s 39 | } 40 | return Hex2Bytes(s) 41 | } 42 | 43 | // Hex2Bytes returns the bytes represented by the hexadecimal string str. 44 | func Hex2Bytes(str string) []byte { 45 | h, _ := hex.DecodeString(str) 46 | return h 47 | } 48 | 49 | // Encode encodes b as a hex string with 0x prefix. 50 | func Encode(b []byte) string { 51 | enc := make([]byte, len(b)*2+2) 52 | copy(enc, "0x") 53 | hex.Encode(enc[2:], b) 54 | return string(enc) 55 | } 56 | -------------------------------------------------------------------------------- /scripts/ensure_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Change directory to project root folder 4 | PROJ_FOLDER=$(cd "$(dirname "$0")/..";pwd) 5 | cd $PROJ_FOLDER 6 | 7 | # Read "dependencies.txt" under project root 8 | DEPS=$(grep -v "^#" dependencies.txt | grep -v "^$") 9 | 10 | # Go get all the imported packages (except the ones under "vendor" folder) to $GOPATH 11 | for dep in $DEPS; do 12 | dep_repo=$(echo ${dep} | awk -F ':' '{print $1}') 13 | if [ -d "${GOPATH}/src/${dep_repo}" ]; then 14 | cd ${GOPATH}/src/${dep_repo} 15 | git checkout master &> /dev/null 16 | fi 17 | go get -v -u ${dep_repo} 18 | done 19 | 20 | # Check out to desired version 21 | for dep in $DEPS; do 22 | dep_repo=$(echo ${dep} | awk -F ':' '{print $1}') 23 | dep_ver=$(echo ${dep} | awk -F ':' '{print $2}') 24 | if [ -d "${GOPATH}/src/${dep_repo}" ]; then 25 | 26 | echo "[INFO] Ensuring ${dep_repo} on ${dep_ver} ..." 27 | 28 | cd ${GOPATH}/src/${dep_repo} 29 | 30 | git fetch origin > /dev/null 31 | 32 | # Try checkout to ${dep_ver} 33 | git checkout ${dep_ver} > /dev/null && (git pull &> /dev/null | true) 34 | 35 | if [ $? != 0 ]; then 36 | # If failed, checkout to origin/${dep_ver} 37 | git checkout origin/${dep_ver} > /dev/null 38 | if [ $? != 0 ]; then 39 | echo "[ERROR] Got error when checking out ${dep_ver} under ${dep_repo}, please check." 40 | exit 1 41 | else 42 | echo "[INFO] ${dep_repo} is now on ${dep_ver}" 43 | fi 44 | else 45 | echo "[INFO] ${dep_repo} is now on ${dep_ver}" 46 | fi 47 | else 48 | echo "[WARN] ${GOPATH}/src/${dep_repo} not exist, do nothing, please check dependencies.txt." 49 | fi 50 | done 51 | 52 | -------------------------------------------------------------------------------- /scripts/check_license.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright(c) 2018 DSiSc Corp, SecureKey Technologies Inc. All Rights Reserved. 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | 8 | 9 | function filterGeneratedFiles { 10 | for f in $@; do 11 | head -n2 $f | grep -qE '// Code generated by' || echo $f 12 | done 13 | } 14 | 15 | function filterExcludedFiles { 16 | CHECK=`echo "$CHECK" | grep -v .png$ | grep -v .rst$ | grep -v ^.git/ \ 17 | | grep -v .pem$ | grep -v .block$ | grep -v .tx$ | grep -v ^LICENSE$ | grep -v _sk$ \ 18 | | grep -v .key$ | grep -v \\.gen.go$ | grep -v ^Gopkg.lock$ \ 19 | | grep -v .md$ | grep -v ^vendor/ | grep -v ^build/ | grep -v .pb.go$ | sort -u` 20 | 21 | CHECK=$(filterGeneratedFiles "$CHECK") 22 | } 23 | 24 | CHECK=$(git diff --name-only --diff-filter=ACMRTUXB HEAD) 25 | filterExcludedFiles 26 | if [[ -z "$CHECK" ]]; then 27 | LAST_COMMITS=($(git log -2 --pretty=format:"%h")) 28 | CHECK=$(git diff-tree --no-commit-id --name-only --diff-filter=ACMRTUXB -r ${LAST_COMMITS[1]} ${LAST_COMMITS[0]}) 29 | filterExcludedFiles 30 | fi 31 | 32 | if [[ -z "$CHECK" ]]; then 33 | echo "All files are excluded from having license headers" 34 | exit 0 35 | fi 36 | 37 | missing=`echo "$CHECK" | xargs ls -d 2>/dev/null | xargs grep -L "SPDX-License-Identifier"` 38 | if [[ -z "$missing" ]]; then 39 | echo "All files have SPDX-License-Identifier headers" 40 | exit 0 41 | fi 42 | echo "The following files are missing SPDX-License-Identifier headers:" 43 | echo "$missing" 44 | echo 45 | echo "Please replace the Apache license header comment text with:" 46 | echo "SPDX-License-Identifier: Apache-2.0" 47 | 48 | echo 49 | echo "Checking committed files for traditional Apache License headers ..." 50 | missing=`echo "$missing" | xargs ls -d 2>/dev/null | xargs grep -L "http://www.apache.org/licenses/LICENSE-2.0"` 51 | if [[ -z "$missing" ]]; then 52 | echo "All remaining files have Apache 2.0 headers" 53 | exit 0 54 | fi 55 | echo "The following files are missing traditional Apache 2.0 headers:" 56 | echo "$missing" 57 | echo "Fatal Error - All files must have a license header" 58 | exit 1 59 | -------------------------------------------------------------------------------- /filter/transaction/tx_filter.go: -------------------------------------------------------------------------------- 1 | package transaction 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "github.com/DSiSc/craft/log" 8 | "github.com/DSiSc/craft/types" 9 | "github.com/DSiSc/statedb-NG/util" 10 | wallett "github.com/DSiSc/wallet/core/types" 11 | "math/big" 12 | ) 13 | 14 | // TxFilter is an implemention of switch message filter, 15 | // switch will use transaction filter to verify transaction message. 16 | type TxFilter struct { 17 | eventCenter types.EventCenter 18 | verifySignature bool 19 | chainId uint64 20 | } 21 | 22 | // create a new transaction filter instance. 23 | func NewTxFilter(eventCenter types.EventCenter, verifySignature bool, chainId uint64) *TxFilter { 24 | return &TxFilter{ 25 | eventCenter: eventCenter, 26 | verifySignature: verifySignature, 27 | chainId: chainId, 28 | } 29 | } 30 | 31 | // Verify verify a switch message whether is validated. 32 | // return nil if message is validated, otherwise return relative error 33 | func (txValidator *TxFilter) Verify(portId int, msg interface{}) error { 34 | switch msg := msg.(type) { 35 | case *types.Transaction: 36 | return txValidator.doVerify(msg) 37 | default: 38 | return errors.New("unsupported message type") 39 | } 40 | } 41 | 42 | // do verify operation 43 | func (txValidator *TxFilter) doVerify(tx *types.Transaction) error { 44 | if txValidator.verifySignature { 45 | signer := wallett.NewEIP155Signer(big.NewInt(int64(txValidator.chainId))) 46 | //signer := new(wallett.FrontierSigner) 47 | from, err := wallett.Sender(signer, tx) 48 | if nil != err { 49 | log.Error("Get from by tx's signer failed with %v.", err) 50 | err := fmt.Errorf("Get from by tx's signer failed with %v ", err) 51 | txValidator.eventCenter.Notify(types.EventTxVerifyFailed, err) 52 | return err 53 | } 54 | if !bytes.Equal((*tx.Data.From)[:], from.Bytes()) { 55 | log.Error("Transaction signature verify failed. from=%v, tx.data.from=%v, v=%v", from.String(), util.AddressToHex(*(tx.Data.From)), tx.Data.V) 56 | err := fmt.Errorf("Transaction signature verify failed ") 57 | txValidator.eventCenter.Notify(types.EventTxVerifyFailed, err) 58 | return err 59 | } 60 | } 61 | txValidator.eventCenter.Notify(types.EventTxVerifySucceeded, tx) 62 | return nil 63 | } 64 | -------------------------------------------------------------------------------- /port/port_test.go: -------------------------------------------------------------------------------- 1 | package port 2 | 3 | import ( 4 | "github.com/DSiSc/craft/types" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | // Test new a InPort 10 | func Test_NewInPort(t *testing.T) { 11 | assert := assert.New(t) 12 | var inPort = NewInPort(LocalInPortId) 13 | assert.NotNil(inPort, "FAILED: failed to create InPort") 14 | 15 | inPort = NewInPort(RemoteInPortId) 16 | assert.NotNil(inPort, "FAILED: failed to create InPort") 17 | } 18 | 19 | // Test get InPort channel 20 | func TestInPort_Channel(t *testing.T) { 21 | assert := assert.New(t) 22 | var inPort = NewInPort(LocalInPortId) 23 | assert.NotNil(inPort, "FAILED: failed to create InPort") 24 | 25 | inputChannel := inPort.Channel() 26 | assert.NotNil(inputChannel, "FAILED: failed to get InPort channel") 27 | } 28 | 29 | // Test Read message from InPort 30 | func Test_Read(t *testing.T) { 31 | assert := assert.New(t) 32 | var inPort = NewInPort(LocalInPortId) 33 | assert.NotNil(inPort, "FAILED: failed to create InPort") 34 | 35 | txMsg := &types.Transaction{} 36 | go func() { 37 | inPort.Channel() <- txMsg 38 | }() 39 | 40 | recvMsg := <-inPort.Read() 41 | assert.Equal(recvMsg, txMsg, "FAILED: failed to Read the message") 42 | } 43 | 44 | // Test new a OutPort 45 | func Test_NewOutPort(t *testing.T) { 46 | assert := assert.New(t) 47 | var outPort = NewOutPort(LocalOutPortId) 48 | assert.NotNil(outPort, "FAILED: failed to create OutPort") 49 | 50 | outPort = NewOutPort(RemoteOutPortId) 51 | assert.NotNil(outPort, "FAILED: failed to create OutPort") 52 | } 53 | 54 | // Test bind OutPutFunc to OutPort 55 | func TestOutPort_BindToPort(t *testing.T) { 56 | assert := assert.New(t) 57 | var outPort = NewOutPort(LocalOutPortId) 58 | assert.NotNil(outPort, "FAILED: failed to create OutPort") 59 | 60 | outPutFunc := func(msg interface{}) error { 61 | return nil 62 | } 63 | outPort.BindToPort(outPutFunc) 64 | 65 | assert.Condition( 66 | func() (success bool) { 67 | return len(outPort.outPutFuncs) == 1 68 | }, "FAILESD: failed to bind OutPutFunc to OutPort") 69 | } 70 | 71 | // Test Write message to OutPort 72 | func TestOutPort_Write(t *testing.T) { 73 | assert := assert.New(t) 74 | var outPort = NewOutPort(LocalOutPortId) 75 | assert.NotNil(outPort, "FAILED: failed to create OutPort") 76 | 77 | var recvMsgChan = make(chan interface{}) 78 | outPutFunc := func(msg interface{}) error { 79 | recvMsgChan <- msg 80 | return nil 81 | } 82 | outPort.BindToPort(outPutFunc) 83 | 84 | sendMsg := &types.Transaction{} 85 | go outPort.Write(sendMsg) 86 | 87 | recvMsg := <-recvMsgChan 88 | assert.Equal(recvMsg, sendMsg, "FAILED: failed to Write message to OutPort") 89 | } 90 | -------------------------------------------------------------------------------- /port/port.go: -------------------------------------------------------------------------------- 1 | package port 2 | 3 | import ( 4 | "github.com/DSiSc/craft/log" 5 | "sync" 6 | ) 7 | 8 | // common const value 9 | const ( 10 | LocalInPortId = 0 //Local InPort ID, receive the message from local 11 | RemoteInPortId = 1 //Remote InPort ID, receive the message from remote 12 | LocalOutPortId = 0 //Local OutPort ID 13 | RemoteOutPortId = 1 //Remote OutPort ID 14 | ) 15 | 16 | // state is used to record switch port state. e.g., message statistics 17 | type state struct { 18 | InCount uint 19 | OutCount uint 20 | } 21 | 22 | // InPort is switch in port. Message will be send to InPort, and then switch Read the message from InPort 23 | type InPort struct { 24 | id int 25 | state state 26 | channel chan interface{} 27 | } 28 | 29 | // create a new in port instance 30 | func NewInPort(id int) *InPort { 31 | return &InPort{ 32 | id: id, 33 | state: state{}, 34 | channel: make(chan interface{}), 35 | } 36 | } 37 | 38 | // Channel return the input channel of this InPort 39 | func (inPort *InPort) Channel() chan<- interface{} { 40 | return inPort.channel 41 | } 42 | 43 | // PortId return this port's id 44 | func (inPort *InPort) PortId() int { 45 | return inPort.id 46 | } 47 | 48 | // Read message from this InPort, will be blocked until the message arrives. 49 | func (inPort *InPort) Read() <-chan interface{} { 50 | return inPort.channel 51 | } 52 | 53 | // OutPutFunc is binded to switch out port, and OutPort will call OutPutFunc when receive a message from switch 54 | type OutPutFunc func(msg interface{}) error 55 | 56 | // InPort is switch out port. Switch will broadcast message to out port 57 | type OutPort struct { 58 | id int 59 | outPortMtx sync.Mutex 60 | state state 61 | outPutFuncs []OutPutFunc 62 | } 63 | 64 | // create a new out port instance 65 | func NewOutPort(id int) *OutPort { 66 | return &OutPort{ 67 | id: id, 68 | state: state{}, 69 | } 70 | } 71 | 72 | // BindToPort bind a new OutPutFunc to this OutPort. Return error if bind failed 73 | func (outPort *OutPort) BindToPort(outPutFunc OutPutFunc) error { 74 | log.Info("Bind OutPutFunc to OutPort") 75 | outPort.outPortMtx.Lock() 76 | defer outPort.outPortMtx.Unlock() 77 | outPort.outPutFuncs = append(outPort.outPutFuncs, outPutFunc) 78 | return nil 79 | } 80 | 81 | // PortId return this port's id 82 | func (outPort *OutPort) PortId() int { 83 | return outPort.id 84 | } 85 | 86 | // Write message to this OutPort 87 | func (outPort *OutPort) Write(msg interface{}) error { 88 | outPort.outPortMtx.Lock() 89 | defer outPort.outPortMtx.Unlock() 90 | for _, outPutFunc := range outPort.outPutFuncs { 91 | outPutFunc(msg) 92 | } 93 | return nil 94 | } 95 | -------------------------------------------------------------------------------- /filter/common_test.go: -------------------------------------------------------------------------------- 1 | package filter 2 | 3 | import ( 4 | "github.com/DSiSc/craft/types" 5 | "github.com/stretchr/testify/assert" 6 | "math/big" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | var MockHash = types.Hash{ 12 | 0x1d, 0xcf, 0x7, 0xba, 0xfc, 0x42, 0xb0, 0x8d, 0xfd, 0x23, 0x9c, 0x45, 0xa4, 0xb9, 0x38, 0xd, 13 | 0x8d, 0xfe, 0x5d, 0x6f, 0xa7, 0xdb, 0xd5, 0x50, 0xc9, 0x25, 0xb1, 0xb3, 0x4, 0xdc, 0xc5, 0x1c, 14 | } 15 | 16 | func MockBlock() *types.Block { 17 | return &types.Block{ 18 | Header: &types.Header{ 19 | ChainID: 1, 20 | PrevBlockHash: MockHash, 21 | StateRoot: MockHash, 22 | TxRoot: MockHash, 23 | ReceiptsRoot: MockHash, 24 | Height: 1, 25 | Timestamp: uint64(time.Date(2018, time.August, 28, 0, 0, 0, 0, time.UTC).Unix()), 26 | }, 27 | Transactions: make([]*types.Transaction, 0), 28 | } 29 | } 30 | 31 | var MockBlockHash = types.Hash{ 32 | 0x44, 0x49, 0xc9, 0xd9, 0xa3, 0x6a, 0x96, 0xeb, 0x28, 0xc9, 0xe1, 0x80, 0x99, 0x0, 0x5c, 0xcc, 0x65, 0x94, 0x2d, 0x5f, 0x88, 0xdd, 0x1a, 0x5a, 0x9c, 0xcf, 0xff, 0x1, 0xaa, 0x2, 0xf1, 0x76, 33 | } 34 | 35 | func TestHeaderHash(t *testing.T) { 36 | block := MockBlock() 37 | hash := HeaderHash(block) 38 | assert.Equal(t, MockBlockHash, hash) 39 | } 40 | 41 | // New a transaction 42 | func mockTransaction(nonce uint64, to *types.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, from *types.Address) *types.Transaction { 43 | d := types.TxData{ 44 | AccountNonce: nonce, 45 | Recipient: to, 46 | From: from, 47 | Payload: data, 48 | Amount: new(big.Int), 49 | GasLimit: gasLimit, 50 | Price: new(big.Int), 51 | V: new(big.Int), 52 | R: new(big.Int), 53 | S: new(big.Int), 54 | } 55 | if amount != nil { 56 | d.Amount.Set(amount) 57 | } 58 | if gasPrice != nil { 59 | d.Price.Set(gasPrice) 60 | } 61 | 62 | return &types.Transaction{Data: d} 63 | } 64 | 65 | func TestTxHash(t *testing.T) { 66 | assert := assert.New(t) 67 | b := types.Address{ 68 | 0xb2, 0x6f, 0x2b, 0x34, 0x2a, 0xab, 0x24, 0xbc, 0xf6, 0x3e, 69 | 0xa2, 0x18, 0xc6, 0xa9, 0x27, 0x4d, 0x30, 0xab, 0x9a, 0x15, 70 | } 71 | emptyTx := mockTransaction( 72 | 0, 73 | &b, 74 | big.NewInt(0), 75 | 0, 76 | big.NewInt(0), 77 | b[:10], 78 | &b, 79 | ) 80 | exceptHash := types.Hash{ 81 | 0x63, 0xa2, 0xa4, 0x4, 0x8d, 0x2c, 0xe4, 0xe8, 0x95, 0xd9, 0x24, 0x21, 0xb3, 0xc7, 0x36, 0xa8, 0xed, 0xf0, 0x83, 0xb7, 0xab, 0x9d, 0xf6, 0xee, 0x7f, 0x4b, 0x57, 0x19, 0xf9, 0x78, 0xef, 0x93, 82 | } 83 | txHash := TxHash(emptyTx) 84 | assert.Equal(exceptHash, txHash) 85 | 86 | exceptHash1 := TxHash(emptyTx) 87 | assert.Equal(exceptHash, exceptHash1) 88 | } 89 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. 4 | 5 | You can contribute in many ways: 6 | 7 | ## Types of Contributions 8 | 9 | ### Report Bugs 10 | 11 | Report bugs at https://github.com/DSiSc/gossipswitch/issues. 12 | 13 | If you are reporting a bug, please include: 14 | 15 | * Your operating system name and version. 16 | * Any details about your local setup that might be helpful in troubleshooting. 17 | * Detailed steps to reproduce the bug. 18 | 19 | ### Fix Bugs 20 | 21 | Look through the GitHub issues for bugs. Anything tagged with "bug" 22 | is open to whoever wants to implement it. 23 | 24 | ### Implement Features 25 | 26 | Look through the GitHub issues for features. Anything tagged with "feature" 27 | is open to whoever wants to implement it. 28 | 29 | ### Write Documentation 30 | 31 | gossipswitch could always use more documentation, whether as part of the 32 | official gossipswitch docs, in docstrings, or even on the web in blog posts, 33 | articles, and such. 34 | 35 | ### Submit Feedback 36 | 37 | The best way to send feedback is to file an issue at https://github.com/DSiSc/gossipswitch/issues. 38 | 39 | If you are proposing a feature: 40 | 41 | * Explain in detail how it would work. 42 | * Keep the scope as narrow as possible, to make it easier to implement. 43 | * Remember that this is a volunteer-driven project, and that contributions 44 | are welcome :) 45 | 46 | ## Get Started! 47 | 48 | Ready to contribute? Here's how to set up `gossipswitch` for local development. 49 | 50 | 1. Fork the `gossipswitch` repo on GitHub. 51 | 2. Clone your fork locally:: 52 | 53 | $ git clone git@github.com:your_name_here/gossipswitch.git 54 | 55 | 3. Create a branch for local development:: 56 | 57 | $ git checkout -b name-of-your-bugfix-or-feature 58 | 59 | Now you can make your changes locally. 60 | 61 | 4. When you're done making changes, check that your changes pass the tests:: 62 | 63 | $ make test 64 | 65 | 6. Commit your changes and push your branch to GitHub, We use [Angular Commit Guidelines](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#-git-commit-guidelines), Thanks for Angular good job.:: 66 | 67 | $ git add . 68 | $ git commit -m "Your detailed description of your changes." 69 | $ git push origin name-of-your-bugfix-or-feature 70 | 71 | 7. Submit a pull request through the GitHub website. 72 | 73 | Pull Request Guidelines 74 | ----------------------- 75 | 76 | Before you submit a pull request, check that it meets these guidelines: 77 | 78 | 1. The pull request should include tests. 79 | 2. If the pull request adds functionality, the docs should be updated. Put 80 | your new functionality into a function with a docstring, and add the 81 | feature to the list in README.md. 82 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2018 DSiSc Group. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | VERSION=$(shell grep "const Version" version/version.go | sed -E 's/.*"(.+)"$$/\1/') 16 | GIT_COMMIT=$(shell git rev-parse HEAD) 17 | GIT_DIRTY=$(shell test -n "`git status --porcelain`" && echo "+CHANGES" || true) 18 | BUILD_DATE=$(shell date '+%Y-%m-%d-%H:%M:%S') 19 | 20 | .PHONY: default help all build test unit-test devenv gotools clean coverage 21 | 22 | default: all 23 | 24 | help: 25 | @echo 'Management commands for DSiSc/gossipswitch:' 26 | @echo 27 | @echo 'Usage:' 28 | @echo ' make lint Check code style.' 29 | @echo ' make spelling Check code spelling.' 30 | @echo ' make fmt Check code formatting.' 31 | @echo ' make static-check Static code check: style & spelling & formatting.' 32 | @echo ' make build Compile the project.' 33 | @echo ' make vet Examine source code and reports suspicious constructs.' 34 | @echo ' make unit-test Run unit tests with coverage report.' 35 | @echo ' make test Run unit tests with coverage report.' 36 | @echo ' make devenv Prepare devenv for test or build.' 37 | @echo ' make fetch-deps Run govendor fetch for deps.' 38 | @echo ' make gotools Prepare go tools depended.' 39 | @echo ' make clean Clean the directory tree.' 40 | @echo 41 | 42 | all: static-check build test 43 | 44 | fmt: 45 | gofmt -d -l . 46 | 47 | spelling: 48 | bash scripts/check_spelling.sh 49 | 50 | lint: 51 | @echo "Check code style..." 52 | golint `go list ./...` 53 | 54 | static-check: fmt spelling lint 55 | 56 | build: 57 | @echo "building gossipswitch ${VERSION}" 58 | @echo "GOPATH=${GOPATH}" 59 | go build -v -ldflags "-X github.com/DSiSc/gossipswitch/version.GitCommit=${GIT_COMMIT}${GIT_DIRTY} -X github.com/DSiSc/gossipswitch/version.BuildDate=${BUILD_DATE}" ./... 60 | 61 | vet: 62 | @echo "Examine source code and reports suspicious constructs..." 63 | go vet `go list ./...` 64 | 65 | unit-test: 66 | @echo "Run unit tests without coverage report..." 67 | go test -v -count=1 -race ./... 68 | 69 | coverage: 70 | @echo "Run unit tests with coverage report..." 71 | bash scripts/unit_test_cov.sh 72 | 73 | test: vet unit-test 74 | 75 | get-tools: 76 | # official tools 77 | go get -u golang.org/x/lint/golint 78 | @# go get -u golang.org/x/tools/cmd/gotype 79 | @# go get -u golang.org/x/tools/cmd/goimports 80 | @# go get -u golang.org/x/tools/cmd/godoc 81 | @# go get -u golang.org/x/tools/cmd/gorename 82 | @# go get -u golang.org/x/tools/cmd/gomvpkg 83 | 84 | # thirdparty tools 85 | go get -u github.com/stretchr/testify 86 | @# go get -u github.com/kardianos/govendor 87 | @# go get -u github.com/axw/gocov/... 88 | @# go get -u github.com/client9/misspell/cmd/misspell 89 | 90 | fetch-deps: get-tools 91 | @echo "Run go get to fetch dependencies as described in dependencies.txt ..." 92 | @bash scripts/ensure_deps.sh 93 | 94 | ## tools & deps 95 | devenv: get-tools fetch-deps 96 | -------------------------------------------------------------------------------- /filter/block/block_filter.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "github.com/DSiSc/craft/log" 8 | "github.com/DSiSc/craft/types" 9 | common "github.com/DSiSc/gossipswitch/filter" 10 | "github.com/DSiSc/repository" 11 | "sync" 12 | ) 13 | 14 | // TxFilter is an implemention of switch message filter, 15 | // switch will use transaction filter to verify transaction message. 16 | type BlockFilter struct { 17 | eventCenter types.EventCenter 18 | verifySignature bool 19 | lock sync.Mutex 20 | } 21 | 22 | // create a new block filter instance. 23 | func NewBlockFilter(eventCenter types.EventCenter, verifySignature bool) *BlockFilter { 24 | return &BlockFilter{ 25 | eventCenter: eventCenter, 26 | verifySignature: verifySignature, 27 | } 28 | } 29 | 30 | // Verify verify a switch message whether is validated. 31 | // return nil if message is validated, otherwise return relative error 32 | func (filter *BlockFilter) Verify(portId int, msg interface{}) error { 33 | filter.lock.Lock() 34 | defer filter.lock.Unlock() 35 | var err error 36 | switch msg := msg.(type) { 37 | case *types.Block: 38 | err = filter.doValidate(msg) 39 | default: 40 | log.Error("Invalidate block message ") 41 | err = errors.New("Invalidate block message ") 42 | } 43 | 44 | //send verification failed event 45 | if err != nil { 46 | log.Debug("Send message verification failed event") 47 | } 48 | return err 49 | } 50 | 51 | // do verify operation 52 | func (filter *BlockFilter) doValidate(block *types.Block) error { 53 | log.Debug("Start to validate received block %x", block.HeaderHash) 54 | 55 | // verify block header hash 56 | blockHash := common.HeaderHash(block) 57 | if !bytes.Equal(blockHash[:], block.HeaderHash[:]) { 58 | log.Error("block header's hash %x, is not same with expected %x", blockHash, block.HeaderHash) 59 | err := fmt.Errorf("block header's hash %x, is not same with expected %x", blockHash, block.HeaderHash) 60 | filter.eventCenter.Notify(types.EventBlockVerifyFailed, err) 61 | return err 62 | } 63 | 64 | // retrieve previous world state 65 | preBlkHash := block.Header.PrevBlockHash 66 | bc, err := repository.NewRepositoryByBlockHash(preBlkHash) 67 | if err != nil { 68 | log.Error("Failed to validate previous block, as: %v", err) 69 | err := fmt.Errorf("failed to get previous block state, as:%v", err) 70 | filter.eventCenter.Notify(types.EventBlockVerifyFailed, err) 71 | return err 72 | } 73 | 74 | currentHeight := bc.GetCurrentBlockHeight() 75 | if currentHeight >= block.Header.Height { 76 | log.Warn("Local block height %d is bigger than received block %x, height: %d", currentHeight, blockHash, block.Header.Height) 77 | err := fmt.Errorf("Local block height %d is bigger than received block %x, height: %d ", currentHeight, blockHash, block.Header.Height) 78 | filter.eventCenter.Notify(types.EventBlockExisted, err) 79 | return err 80 | } 81 | 82 | // verify block 83 | blockValidator := getValidateWorker(bc, block, filter.verifySignature) 84 | err = blockValidator.VerifyBlock() 85 | if err != nil { 86 | log.Error("Validate block failed, as %v", err) 87 | err := fmt.Errorf("Validate block failed, as %v", err) 88 | filter.eventCenter.Notify(types.EventBlockVerifyFailed, err) 89 | return err 90 | } 91 | 92 | // write block to local database 93 | return bc.WriteBlockWithReceipts(block, blockValidator.GetReceipts()) 94 | } 95 | 96 | // get validate worker by previous world state and block 97 | func getValidateWorker(bc *repository.Repository, block *types.Block, verifySignature bool) *Worker { 98 | return NewWorker(bc, block, verifySignature) 99 | } 100 | -------------------------------------------------------------------------------- /switch_test.go: -------------------------------------------------------------------------------- 1 | package gossipswitch 2 | 3 | import ( 4 | "errors" 5 | "github.com/DSiSc/craft/log" 6 | "github.com/DSiSc/craft/types" 7 | "github.com/DSiSc/gossipswitch/config" 8 | "github.com/DSiSc/gossipswitch/port" 9 | "github.com/stretchr/testify/assert" 10 | "testing" 11 | "time" 12 | ) 13 | 14 | // mock switch filter 15 | type mockSwitchFiler struct { 16 | } 17 | 18 | func (filter *mockSwitchFiler) Verify(portId int, msg interface{}) error { 19 | return nil 20 | } 21 | 22 | // mock switch config 23 | func mockSwitchConfig() *config.SwitchConfig { 24 | return &config.SwitchConfig{ 25 | VerifySignature: true, 26 | } 27 | } 28 | 29 | // Test new a gossipsiwtch 30 | func Test_NewGossipSwitch(t *testing.T) { 31 | assert := assert.New(t) 32 | var sw = NewGossipSwitch(&mockSwitchFiler{}) 33 | assert.NotNil(sw, "FAILED: failed to create GossipSwitch") 34 | } 35 | 36 | // Test new a gossipsiwtch by type 37 | func Test_NewGossipSwitchByType(t *testing.T) { 38 | assert := assert.New(t) 39 | var _, err = NewGossipSwitchByType(TxSwitch, &eventCenter{}, mockSwitchConfig()) 40 | assert.Nil(err, "FAILED: failed to create GossipSwitch") 41 | _, err = NewGossipSwitchByType(BlockSwitch, &eventCenter{}, mockSwitchConfig()) 42 | assert.Nil(err, "FAILED: failed to create GossipSwitch") 43 | } 44 | 45 | // Test get switch in port by id 46 | func Test_InPort(t *testing.T) { 47 | assert := assert.New(t) 48 | var sw = NewGossipSwitch(&mockSwitchFiler{}) 49 | assert.NotNil(sw, "FAILED: failed to create GossipSwitch") 50 | var localInPort = sw.InPort(port.LocalInPortId) 51 | assert.NotNil(localInPort, "FAILED: failed to get local in port") 52 | 53 | var remoteInPort = sw.InPort(port.LocalInPortId) 54 | assert.NotNil(remoteInPort, "FAILED: failed to get remote in port.") 55 | } 56 | 57 | // Test get switch out port by id 58 | func Test_OutPort(t *testing.T) { 59 | assert := assert.New(t) 60 | var sw = NewGossipSwitch(&mockSwitchFiler{}) 61 | assert.NotNil(sw, "FAILED: failed to create GossipSwitch") 62 | 63 | var localOutPort = sw.OutPort(port.LocalOutPortId) 64 | assert.NotNil(localOutPort, "FAILED: failed to get local out port") 65 | 66 | var remoteOutPort = sw.OutPort(port.RemoteOutPortId) 67 | assert.NotNil(remoteOutPort, "FAILED: failed to get remote out port.") 68 | } 69 | 70 | // Test start switch 71 | func Test_Start(t *testing.T) { 72 | assert := assert.New(t) 73 | var sw = NewGossipSwitch(&mockSwitchFiler{}) 74 | assert.NotNil(sw, "FAILED: failed to create GossipSwitch") 75 | 76 | err := sw.Start() 77 | checkSwitchStatus(t, err, sw.isRunning, 1) 78 | } 79 | 80 | // Test stop switch 81 | func Test_Stop(t *testing.T) { 82 | assert := assert.New(t) 83 | var sw = NewGossipSwitch(&mockSwitchFiler{}) 84 | assert.NotNil(sw, "FAILED: failed to create GossipSwitch") 85 | 86 | err := sw.Start() 87 | checkSwitchStatus(t, err, sw.isRunning, 1) 88 | 89 | err = sw.Stop() 90 | checkSwitchStatus(t, err, sw.isRunning, 0) 91 | } 92 | 93 | // Test on receive message 94 | func Test_onRecvMsg(t *testing.T) { 95 | assert := assert.New(t) 96 | var sw = NewGossipSwitch(&mockSwitchFiler{}) 97 | assert.NotNil(sw, "FAILED: failed to create GossipSwitch") 98 | 99 | checkSwitchStatus(t, sw.Start(), sw.isRunning, 1) 100 | 101 | recvMsgChan := make(chan interface{}) 102 | // bind output func to switch out port 103 | outPort := sw.OutPort(port.LocalOutPortId) 104 | outPort.BindToPort(func(msg interface{}) error { 105 | log.Info("received a message") 106 | recvMsgChan <- msg 107 | return nil 108 | }) 109 | 110 | //send message to switch 111 | txMsg := &types.Transaction{} 112 | sw.InPort(port.LocalInPortId).Channel() <- txMsg 113 | 114 | ticker := time.NewTicker(2 * time.Second) 115 | select { 116 | case recvMsg := <-recvMsgChan: 117 | assert.Equal(txMsg, recvMsg) 118 | case <-ticker.C: 119 | assert.Nil(errors.New("failed to receive message")) 120 | 121 | } 122 | } 123 | 124 | // check switch status 125 | func checkSwitchStatus(t *testing.T, err error, currentStatus uint32, expectStatus uint32) { 126 | assert.Equal(t, expectStatus, currentStatus) 127 | if currentStatus == 0 { 128 | log.Info("PASS: succed stopping switch") 129 | } else { 130 | log.Error("PASS: succed starting switch") 131 | } 132 | } 133 | 134 | type eventCenter struct { 135 | } 136 | 137 | // subscriber subscribe specified eventType with eventFunc 138 | func (*eventCenter) Subscribe(eventType types.EventType, eventFunc types.EventFunc) types.Subscriber { 139 | return nil 140 | } 141 | 142 | // subscriber unsubscribe specified eventType 143 | func (*eventCenter) UnSubscribe(eventType types.EventType, subscriber types.Subscriber) (err error) { 144 | return nil 145 | } 146 | 147 | // notify subscriber of eventType 148 | func (*eventCenter) Notify(eventType types.EventType, value interface{}) (err error) { 149 | return nil 150 | } 151 | 152 | // notify specified eventFunc 153 | func (*eventCenter) NotifySubscriber(eventFunc types.EventFunc, value interface{}) { 154 | 155 | } 156 | 157 | // notify subscriber traversing all events 158 | func (*eventCenter) NotifyAll() (errs []error) { 159 | return nil 160 | } 161 | 162 | // unsubscrible all event 163 | func (*eventCenter) UnSubscribeAll() { 164 | } 165 | -------------------------------------------------------------------------------- /switch.go: -------------------------------------------------------------------------------- 1 | package gossipswitch 2 | 3 | import ( 4 | "errors" 5 | "github.com/DSiSc/craft/log" 6 | "github.com/DSiSc/craft/types" 7 | "github.com/DSiSc/gossipswitch/config" 8 | "github.com/DSiSc/gossipswitch/filter" 9 | "github.com/DSiSc/gossipswitch/filter/block" 10 | "github.com/DSiSc/gossipswitch/filter/transaction" 11 | "github.com/DSiSc/gossipswitch/port" 12 | "sync" 13 | "sync/atomic" 14 | ) 15 | 16 | // SwitchType switch type 17 | type SwitchType int 18 | 19 | const ( 20 | TxSwitch SwitchType = iota 21 | BlockSwitch 22 | ) 23 | 24 | // GossipSwitch is the implementation of gossip switch. 25 | // for gossipswitch, if a validated message is received, it will be broadcasted, 26 | // otherwise it will be dropped. 27 | type GossipSwitch struct { 28 | switchMtx sync.Mutex 29 | filter filter.SwitchFilter 30 | inPorts map[int]*port.InPort 31 | outPorts map[int]*port.OutPort 32 | isRunning uint32 // atomic 33 | } 34 | 35 | // NewGossipSwitch create a new switch instance with given filter. 36 | // filter is used to verify the received message 37 | func NewGossipSwitch(filter filter.SwitchFilter) *GossipSwitch { 38 | sw := &GossipSwitch{ 39 | filter: filter, 40 | inPorts: make(map[int]*port.InPort), 41 | outPorts: make(map[int]*port.OutPort), 42 | } 43 | sw.initPort() 44 | return sw 45 | } 46 | 47 | // NewGossipSwitchByType create a new switch instance by type. 48 | // switchType is used to specify the switch type 49 | func NewGossipSwitchByType(switchType SwitchType, eventCenter types.EventCenter, switchConfig *config.SwitchConfig) (*GossipSwitch, error) { 50 | var msgFilter filter.SwitchFilter 51 | switch switchType { 52 | case TxSwitch: 53 | log.Info("New transaction switch") 54 | msgFilter = transaction.NewTxFilter(eventCenter, switchConfig.VerifySignature, switchConfig.ChainID) 55 | case BlockSwitch: 56 | log.Info("New block switch") 57 | msgFilter = block.NewBlockFilter(eventCenter, switchConfig.VerifySignature) 58 | default: 59 | log.Error("Unsupported switch type") 60 | return nil, errors.New("Unsupported switch type ") 61 | } 62 | sw := &GossipSwitch{ 63 | filter: msgFilter, 64 | inPorts: make(map[int]*port.InPort), 65 | outPorts: make(map[int]*port.OutPort), 66 | } 67 | sw.initPort() 68 | return sw, nil 69 | } 70 | 71 | // init switch's port.InPort and port.OutPort 72 | func (sw *GossipSwitch) initPort() { 73 | log.Info("Init switch's ports") 74 | sw.inPorts[port.LocalInPortId] = port.NewInPort(port.LocalInPortId) 75 | sw.inPorts[port.RemoteInPortId] = port.NewInPort(port.RemoteInPortId) 76 | sw.outPorts[port.LocalOutPortId] = port.NewOutPort(port.LocalOutPortId) 77 | sw.outPorts[port.RemoteOutPortId] = port.NewOutPort(port.RemoteOutPortId) 78 | } 79 | 80 | // port.InPort get switch's in port by port id, return nil if there is no port with specific id. 81 | func (sw *GossipSwitch) InPort(portId int) *port.InPort { 82 | log.Debug("Get switch %v in port", portId) 83 | return sw.inPorts[portId] 84 | } 85 | 86 | // port.InPort get switch's out port by port id, return nil if there is no port with specific id. 87 | func (sw *GossipSwitch) OutPort(portId int) *port.OutPort { 88 | log.Debug("Get switch %v out port", portId) 89 | return sw.outPorts[portId] 90 | } 91 | 92 | // Start start the switch. Once started, switch will receive message from in port, and broadcast to 93 | // out port 94 | func (sw *GossipSwitch) Start() error { 95 | log.Info("Begin starting switch") 96 | 97 | if atomic.CompareAndSwapUint32(&sw.isRunning, 0, 1) { 98 | for _, inPort := range sw.inPorts { 99 | go sw.receiveRoutine(inPort) 100 | } 101 | log.Info("Start switch success") 102 | return nil 103 | } 104 | log.Error("Switch already started") 105 | return errors.New("switch already started") 106 | } 107 | 108 | // Stop stop the switch. Once stopped, switch will stop to receive and broadcast message 109 | func (sw *GossipSwitch) Stop() error { 110 | log.Info("Begin stopping switch") 111 | if atomic.CompareAndSwapUint32(&sw.isRunning, 1, 0) { 112 | log.Info("Stop switch success") 113 | return nil 114 | } 115 | log.Error("Switch already stopped") 116 | return errors.New("switch already stopped") 117 | } 118 | 119 | // IsRunning is used to query switch's current status. Return true if running, otherwise false 120 | func (sw *GossipSwitch) IsRunning() bool { 121 | return atomic.LoadUint32(&sw.isRunning) == 1 122 | } 123 | 124 | // listen to receive message from the in port 125 | func (sw *GossipSwitch) receiveRoutine(inPort *port.InPort) { 126 | for { 127 | select { 128 | case msg := <-inPort.Read(): 129 | sw.onRecvMsg(inPort.PortId(), msg) 130 | } 131 | 132 | //check switch status 133 | if !sw.IsRunning() { 134 | break 135 | } 136 | } 137 | } 138 | 139 | // deal with the received message. 140 | func (sw *GossipSwitch) onRecvMsg(portId int, msg interface{}) { 141 | //TODO log.Debug("Received a message %v from port.InPort", msg) 142 | if err := sw.filter.Verify(portId, msg); err == nil { 143 | sw.broadCastMsg(msg) 144 | } 145 | } 146 | 147 | // broadcast the validated message to all out ports. 148 | func (sw *GossipSwitch) broadCastMsg(msg interface{}) error { 149 | //log.Debug("Broadcast message %v to port.OutPorts", msg) 150 | for _, outPort := range sw.outPorts { 151 | go outPort.Write(msg) 152 | } 153 | return nil 154 | } 155 | -------------------------------------------------------------------------------- /filter/block/block_filter_test.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/DSiSc/craft/types" 7 | "github.com/DSiSc/gossipswitch/filter" 8 | "github.com/DSiSc/gossipswitch/port" 9 | "github.com/DSiSc/monkey" 10 | "github.com/DSiSc/repository" 11 | "github.com/DSiSc/repository/config" 12 | "github.com/stretchr/testify/assert" 13 | "reflect" 14 | "testing" 15 | "time" 16 | ) 17 | 18 | var mockGenesisBlock = &types.Block{ 19 | Header: &types.Header{}, 20 | HeaderHash: types.Hash{3, 49, 241, 97, 138, 50, 185, 45, 132, 133, 5, 202, 149, 143, 173, 115, 217, 157, 249, 3, 209, 105, 249, 224, 200, 68, 60, 171, 249, 207, 130, 153}, 21 | } 22 | 23 | // Test new BlockFiltercd . 24 | func Test_NewBlockFilter(t *testing.T) { 25 | assert := assert.New(t) 26 | // init event center 27 | eventCenter := mockEventCenter() 28 | var blockFilter = NewBlockFilter(eventCenter, true) 29 | assert.NotNil(blockFilter, "FAILED: failed to create BlockFilter") 30 | } 31 | 32 | // mock block 33 | func mockBlock() *types.Block { 34 | cfg := config.RepositoryConfig{ 35 | PluginName: repository.PLUGIN_MEMDB, 36 | } 37 | eventCenter := mockEventCenter() 38 | repository.InitRepository(cfg, eventCenter) 39 | 40 | bc, _ := repository.NewLatestStateRepository() 41 | bc.WriteBlock(mockGenesisBlock) 42 | b := &types.Block{ 43 | Header: &types.Header{ 44 | ChainID: 1, 45 | PrevBlockHash: bc.GetCurrentBlock().HeaderHash, 46 | StateRoot: bc.IntermediateRoot(false), 47 | TxRoot: types.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 48 | ReceiptsRoot: types.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 49 | Height: 1, 50 | Timestamp: uint64(time.Date(2018, time.August, 28, 0, 0, 0, 0, time.UTC).Unix()), 51 | MixDigest: types.Hash{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 52 | }, 53 | } 54 | b.Header.PrevBlockHash = filter.HeaderHash(bc.GetCurrentBlock()) 55 | fmt.Println(filter.HeaderHash(mockGenesisBlock)) 56 | b.HeaderHash = filter.HeaderHash(b) 57 | return b 58 | } 59 | 60 | // mock event center 61 | func mockEventCenter() types.EventCenter { 62 | return &eventCenter{} 63 | } 64 | 65 | // Test verify block message. 66 | func TestBlockFilter_Verify(t *testing.T) { 67 | defer monkey.UnpatchAll() 68 | assert := assert.New(t) 69 | // init event center 70 | eventCenter := mockEventCenter() 71 | 72 | var blockFilter = NewBlockFilter(eventCenter, true) 73 | assert.NotNil(blockFilter, "FAILED: failed to create BlockFilter") 74 | 75 | tx := &types.Transaction{} 76 | assert.NotNil(blockFilter.Verify(port.LocalInPortId, tx), "PASS: verify invalid message") 77 | assert.NotNil(blockFilter.Verify(port.RemoteInPortId, tx), "PASS: verify invalid message") 78 | 79 | block := mockBlock() 80 | var validateWorker *Worker 81 | patchGuard := monkey.PatchInstanceMethod(reflect.TypeOf(validateWorker), "VerifyBlock", func(self *Worker) error { 82 | return nil 83 | }) 84 | monkey.PatchInstanceMethod(reflect.TypeOf(validateWorker), "GetReceipts", func(self *Worker) types.Receipts { 85 | return types.Receipts{} 86 | }) 87 | monkey.Patch(getValidateWorker, func(bc *repository.Repository, block *types.Block, verifySignature bool) *Worker { 88 | return validateWorker 89 | }) 90 | assert.Nil(blockFilter.Verify(port.LocalInPortId, block), "PASS: verify valid block") 91 | 92 | patchGuard.Unpatch() 93 | monkey.PatchInstanceMethod(reflect.TypeOf(validateWorker), "VerifyBlock", func(self *Worker) error { 94 | return errors.New("invalid block") 95 | }) 96 | assert.NotNil(blockFilter.Verify(port.LocalInPortId, block), "PASS: verify invalid block") 97 | assert.NotNil(blockFilter.Verify(port.RemoteInPortId, block), "PASS: verify invalid block") 98 | } 99 | 100 | func TestBlockFilter_Verify2(t *testing.T) { 101 | defer monkey.UnpatchAll() 102 | assert := assert.New(t) 103 | // init event center 104 | eventCenter := mockEventCenter() 105 | 106 | var blockFilter = NewBlockFilter(eventCenter, true) 107 | assert.NotNil(blockFilter, "FAILED: failed to create BlockFilter") 108 | 109 | block := mockBlock() 110 | block.Header.Height = 123 111 | var validateWorker *Worker 112 | monkey.PatchInstanceMethod(reflect.TypeOf(validateWorker), "VerifyBlock", func(self *Worker) error { 113 | return nil 114 | }) 115 | monkey.PatchInstanceMethod(reflect.TypeOf(validateWorker), "GetReceipts", func(self *Worker) types.Receipts { 116 | return types.Receipts{} 117 | }) 118 | monkey.Patch(getValidateWorker, func(bc *repository.Repository, block *types.Block, verifySignature bool) *Worker { 119 | return validateWorker 120 | }) 121 | assert.NotNil(blockFilter.Verify(port.LocalInPortId, block), "PASS: verify invalid block") 122 | assert.NotNil(blockFilter.Verify(port.RemoteInPortId, block), "PASS: verify invalid block") 123 | } 124 | 125 | type eventCenter struct { 126 | } 127 | 128 | // subscriber subscribe specified eventType with eventFunc 129 | func (*eventCenter) Subscribe(eventType types.EventType, eventFunc types.EventFunc) types.Subscriber { 130 | return nil 131 | } 132 | 133 | // subscriber unsubscribe specified eventType 134 | func (*eventCenter) UnSubscribe(eventType types.EventType, subscriber types.Subscriber) (err error) { 135 | return nil 136 | } 137 | 138 | // notify subscriber of eventType 139 | func (*eventCenter) Notify(eventType types.EventType, value interface{}) (err error) { 140 | return nil 141 | } 142 | 143 | // notify specified eventFunc 144 | func (*eventCenter) NotifySubscriber(eventFunc types.EventFunc, value interface{}) { 145 | 146 | } 147 | 148 | // notify subscriber traversing all events 149 | func (*eventCenter) NotifyAll() (errs []error) { 150 | return nil 151 | } 152 | 153 | // unsubscrible all event 154 | func (*eventCenter) UnSubscribeAll() { 155 | } 156 | -------------------------------------------------------------------------------- /filter/block/state.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | import ( 4 | "errors" 5 | "github.com/DSiSc/craft/log" 6 | "github.com/DSiSc/craft/types" 7 | evmNg "github.com/DSiSc/evm-NG" 8 | "github.com/DSiSc/repository" 9 | evmCommon "github.com/DSiSc/validator/common" 10 | "github.com/DSiSc/validator/worker/common" 11 | wasmExec "github.com/DSiSc/wasm/exec" 12 | wasmModule "github.com/DSiSc/wasm/wasm" 13 | "math" 14 | "math/big" 15 | ) 16 | 17 | type StateTransition struct { 18 | gp *common.GasPool 19 | tx *types.Transaction 20 | gas uint64 21 | gasPrice *big.Int 22 | initialGas uint64 23 | value *big.Int 24 | data []byte 25 | state *repository.Repository 26 | from types.Address 27 | to types.Address 28 | nonce uint64 29 | header *types.Header 30 | author types.Address 31 | } 32 | 33 | // NewStateTransition initialises and returns a new state transition object. 34 | func NewStateTransition(author types.Address, header *types.Header, chain *repository.Repository, trx *types.Transaction, gp *common.GasPool) *StateTransition { 35 | var receive types.Address 36 | if trx.Data.Recipient == nil /* contract creation */ { 37 | receive = types.Address{} 38 | } else { 39 | receive = *trx.Data.Recipient 40 | } 41 | return &StateTransition{ 42 | author: author, 43 | gp: gp, 44 | tx: trx, 45 | from: *trx.Data.From, 46 | to: receive, 47 | gasPrice: trx.Data.Price, 48 | value: trx.Data.Amount, 49 | data: trx.Data.Payload, 50 | state: chain, 51 | gas: trx.Data.GasLimit, 52 | initialGas: math.MaxUint64, 53 | nonce: trx.Data.AccountNonce, 54 | header: header, 55 | } 56 | } 57 | 58 | // ApplyMessage computes the new state by applying the given message 59 | // against the old state within the environment. 60 | // ApplyMessage returns the bytes returned by any EVM execution (if it took place), 61 | // the gas used (which includes gas refunds) and an error if it failed. An error always 62 | // indicates a core error meaning that the message would always fail for that particular 63 | // state and would never be accepted within a block. 64 | func ApplyTransaction(author types.Address, header *types.Header, chain *repository.Repository, tx *types.Transaction, gp *common.GasPool) ([]byte, uint64, bool, error, types.Address) { 65 | return NewStateTransition(author, header, chain, tx, gp).TransitionDb() 66 | } 67 | 68 | // TransitionDb will transition the state by applying the current message and 69 | // returning the result including the used gas. It returns an error if failed. 70 | // An error indicates a consensus issue. 71 | func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bool, err error, address types.Address) { 72 | if err = st.preCheck(); err != nil { 73 | return 74 | } 75 | if st.isWasmContract(st.tx) { 76 | ret, address, st.gas, err = st.execWasmContract() 77 | } else { 78 | var vmerr error 79 | ret, address, st.gas, vmerr = st.execSolidityContract() 80 | if vmerr != nil { 81 | log.Debug("VM returned with error", "err", vmerr) 82 | // The only possible consensus-error would be if there wasn't 83 | // sufficient balance to make the transfer happen. The first 84 | // balance transfer may never fail. 85 | if vmerr == evmNg.ErrInsufficientBalance { 86 | return ret, 0, false, vmerr, address 87 | } 88 | } 89 | } 90 | return ret, st.gasUsed(), err != nil, err, address 91 | } 92 | 93 | func (st *StateTransition) refundGas() { 94 | // Apply refund counter, capped to half of the used gas. 95 | refund := st.gasUsed() / 2 96 | if refund > st.state.GetRefund() { 97 | refund = st.state.GetRefund() 98 | } 99 | st.gas += refund 100 | 101 | // Return ETH for remaining gas, exchanged at the original rate. 102 | remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice) 103 | st.state.AddBalance(*st.tx.Data.From, remaining) 104 | 105 | // Also return remaining gas to the block gas counter so it is 106 | // available for the next transaction. 107 | st.gp.AddGas(st.gas) 108 | } 109 | 110 | // gasUsed returns the amount of gas used up by the state transition. 111 | func (st *StateTransition) gasUsed() uint64 { 112 | return st.initialGas - st.gas 113 | } 114 | 115 | // check tx's nonce 116 | func (st *StateTransition) preCheck() error { 117 | // Make sure this transaction's nonce is correct. 118 | nonce := st.state.GetNonce(st.from) 119 | if nonce < st.nonce { 120 | return errors.New("blacklisted hash") 121 | } else if nonce > st.nonce { 122 | return errors.New("nonce too high") 123 | } 124 | return nil 125 | } 126 | 127 | // check whether the tx Recipient is wasm contract 128 | func (st *StateTransition) isWasmContract(tx *types.Transaction) bool { 129 | var code []byte 130 | if (nil == tx.Data.Recipient || types.Address{} == *tx.Data.Recipient) { 131 | code = tx.Data.Payload 132 | } else { 133 | code = st.state.GetCode(*tx.Data.Recipient) 134 | } 135 | return wasmModule.IsValidWasmCode(code) 136 | } 137 | 138 | func (st *StateTransition) execSolidityContract() (ret []byte, contractAddr types.Address, leftOverGas uint64, err error) { 139 | from := *st.tx.Data.From 140 | sender := evmCommon.NewRefAddress(from) 141 | //homestead := st.evm.ChainConfig().IsHomestead(st.evm.BlockNumber) 142 | contractCreation := st.tx.Data.Recipient == nil 143 | /* 144 | // Pay intrinsic gas 145 | gas, err := IntrinsicGas(st.data, contractCreation, homestead) 146 | if err != nil { 147 | return nil, 0, false, err 148 | } 149 | if err = st.useGas(gas); err != nil { 150 | return nil, 0, false, err 151 | } 152 | */ 153 | context := evmNg.NewEVMContext(*st.tx, st.header, st.state, st.author) 154 | evm := evmNg.NewEVM(context, st.state) 155 | 156 | if contractCreation { 157 | // ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value) 158 | ret, contractAddr, leftOverGas, err = evm.Create(sender, st.data, math.MaxUint64, st.value) 159 | } else { 160 | // Increment the nonce for the next transaction 161 | st.state.SetNonce(from, st.state.GetNonce(sender.Address())+1) 162 | ret, leftOverGas, err = evm.Call(sender, st.to, st.data, math.MaxUint64, st.value) 163 | } 164 | return ret, contractAddr, leftOverGas, err 165 | } 166 | 167 | func (st *StateTransition) execWasmContract() (ret []byte, contractAddr types.Address, leftOverGas uint64, err error) { 168 | context := wasmExec.NewWasmChainContext(st.tx, st.header, st.state, st.author) 169 | wvm := wasmExec.NewVM(context, st.state) 170 | contractCreation := st.tx.Data.Recipient == nil 171 | if contractCreation { 172 | ret, contractAddr, leftOverGas, err = wvm.Create(*st.tx.Data.From, st.data, math.MaxUint64, st.value) 173 | } else { 174 | st.state.SetNonce(*st.tx.Data.From, st.state.GetNonce(*st.tx.Data.From)+1) 175 | ret, leftOverGas, err = wvm.Call(*st.tx.Data.From, *st.tx.Data.Recipient, st.data, math.MaxUint64, st.value) 176 | } 177 | return ret, contractAddr, leftOverGas, err 178 | } 179 | -------------------------------------------------------------------------------- /filter/block/worker.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "github.com/DSiSc/craft/log" 7 | "github.com/DSiSc/craft/types" 8 | "github.com/DSiSc/repository" 9 | vcommon "github.com/DSiSc/validator/common" 10 | "github.com/DSiSc/validator/tools/merkle_tree" 11 | "github.com/DSiSc/validator/worker/common" 12 | wallett "github.com/DSiSc/wallet/core/types" 13 | "math/big" 14 | ) 15 | 16 | type Worker struct { 17 | block *types.Block 18 | chain *repository.Repository 19 | receipts types.Receipts 20 | logs []*types.Log 21 | signature bool 22 | } 23 | 24 | func NewWorker(chain *repository.Repository, block *types.Block, signVerify bool) *Worker { 25 | return &Worker{ 26 | block: block, 27 | chain: chain, 28 | signature: signVerify, 29 | } 30 | } 31 | 32 | func GetTxsRoot(txs []*types.Transaction) types.Hash { 33 | txHash := make([]types.Hash, 0, len(txs)) 34 | for _, t := range txs { 35 | txHash = append(txHash, vcommon.TxHash(t)) 36 | } 37 | txRoot := merkle_tree.ComputeMerkleRoot(txHash) 38 | return txRoot 39 | } 40 | 41 | func (self *Worker) VerifyBlock() error { 42 | // 1. chainID 43 | currentBlock := self.chain.GetCurrentBlock() 44 | if self.block.Header.ChainID != currentBlock.Header.ChainID { 45 | return fmt.Errorf("wrong Block.Header.ChainID, expected %d, got %d", 46 | currentBlock.Header.ChainID, self.block.Header.ChainID) 47 | } 48 | // 2. hash 49 | if self.block.Header.PrevBlockHash != currentBlock.HeaderHash { 50 | return fmt.Errorf("wrong Block.Header.PrevBlockHash, expected %x, got %x", 51 | currentBlock.HeaderHash, self.block.Header.PrevBlockHash) 52 | } 53 | // 3. height 54 | if self.block.Header.Height != self.chain.GetCurrentBlockHeight()+1 { 55 | return fmt.Errorf("wrong Block.Header.Height, expected %x, got %x", 56 | self.chain.GetCurrentBlockHeight()+1, self.block.Header.Height) 57 | } 58 | // 4. txhash 59 | txsHash := GetTxsRoot(self.block.Transactions) 60 | if self.block.Header.TxRoot != txsHash { 61 | return fmt.Errorf("wrong Block.Header.TxRoot, expected %x, got %x", 62 | txsHash, self.block.Header.TxRoot) 63 | } 64 | //5. header hash 65 | if !(self.block.HeaderHash == types.Hash{}) { 66 | headerHash := vcommon.HeaderHash(self.block) 67 | if self.block.HeaderHash != headerHash { 68 | return fmt.Errorf("wrong Block.HeaderHash, expected %x, got %x", 69 | headerHash, self.block.HeaderHash) 70 | } 71 | } 72 | var ( 73 | receipts types.Receipts 74 | allLogs []*types.Log 75 | gp = new(common.GasPool).AddGas(uint64(65536)) 76 | ) 77 | // 6. verify every transactions in the block by evm 78 | for i, tx := range self.block.Transactions { 79 | self.chain.Prepare(vcommon.TxHash(tx), self.block.Header.PrevBlockHash, i) 80 | receipt, _, err := self.VerifyTransaction(self.block.Header.CoinBase, gp, self.block.Header, tx, new(uint64)) 81 | if err != nil { 82 | log.Error("Tx %x verify failed with error %v.", vcommon.TxHash(tx), err) 83 | return err 84 | } 85 | receipts = append(receipts, receipt) 86 | allLogs = append(allLogs, receipt.Logs...) 87 | } 88 | receiptsHash := make([]types.Hash, 0, len(receipts)) 89 | for _, t := range receipts { 90 | receiptsHash = append(receiptsHash, common.ReceiptHash(t)) 91 | log.Debug("Record tx %x receipt is %x.", t.TxHash, common.ReceiptHash(t)) 92 | } 93 | receiptHash := merkle_tree.ComputeMerkleRoot(receiptsHash) 94 | if !(self.block.Header.ReceiptsRoot == types.Hash{}) { 95 | log.Warn("Receipts root has assigned with %x.", self.block.Header.ReceiptsRoot) 96 | if !(receiptHash == self.block.Header.ReceiptsRoot) { 97 | log.Error("Receipts root has assigned with %x, but not consistent with %x.", 98 | self.block.Header.ReceiptsRoot, receiptHash) 99 | return fmt.Errorf("receipts hash not consistent") 100 | } 101 | } else { 102 | log.Debug("Assign receipts hash %x to block %d.", receiptHash, self.block.Header.Height) 103 | self.block.Header.ReceiptsRoot = receiptHash 104 | } 105 | // 7. verify digest if it exists 106 | if !(self.block.Header.MixDigest == types.Hash{}) { 107 | digestHash := vcommon.HeaderDigest(self.block.Header) 108 | if !bytes.Equal(digestHash[:], self.block.Header.MixDigest[:]) { 109 | log.Error("Block digest not consistent which assignment is [%x], while compute is [%x].", 110 | self.block.Header.MixDigest, digestHash) 111 | return fmt.Errorf("digest not in coincidence") 112 | } 113 | } 114 | // TODO 8. verify state root 115 | self.receipts = receipts 116 | self.logs = allLogs 117 | 118 | return nil 119 | } 120 | 121 | func (self *Worker) VerifyTransaction(author types.Address, gp *common.GasPool, header *types.Header, 122 | tx *types.Transaction, usedGas *uint64) (*types.Receipt, uint64, error) { 123 | // txs signature has been verified by tx switch already, so ignore it here 124 | if self.signature { 125 | if self.VerifyTrsSignature(tx) == false { 126 | log.Error("Transaction signature verify failed.") 127 | return nil, 0, fmt.Errorf("transaction signature failed") 128 | } 129 | } 130 | _, gas, failed, err, contractAddress := ApplyTransaction(author, header, self.chain, tx, gp) 131 | if err != nil { 132 | log.Error("Apply transaction %x failed with error %v.", vcommon.TxHash(tx), err) 133 | return nil, 0, err 134 | } 135 | 136 | root := self.chain.IntermediateRoot(false) 137 | *usedGas += gas 138 | 139 | // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx 140 | // based on the eip phase, we're passing wether the root touch-delete accounts. 141 | receipt := common.NewReceipt(vcommon.HashToByte(root), failed, *usedGas) 142 | receipt.TxHash = vcommon.TxHash(tx) 143 | receipt.GasUsed = gas 144 | // if the transaction created a contract, store the creation address in the receipt. 145 | if tx.Data.Recipient == nil { 146 | log.Info("Create contract with address %x within tx %x.", receipt.ContractAddress, receipt.TxHash) 147 | receipt.ContractAddress = contractAddress 148 | } 149 | // Set the receipt logs and create a bloom for filtering 150 | receipt.Logs = self.chain.GetLogs(receipt.TxHash) 151 | receipt.Bloom = vcommon.CreateBloom(types.Receipts{receipt}) 152 | 153 | return receipt, gas, err 154 | } 155 | 156 | func (self *Worker) VerifyTrsSignature(tx *types.Transaction) bool { 157 | id := self.block.Header.ChainID 158 | chainId := int64(id) 159 | signer := wallett.NewEIP155Signer(big.NewInt(chainId)) 160 | //signer := new(wallett.FrontierSigner) 161 | from, err := wallett.Sender(signer, tx) 162 | if nil != err { 163 | log.Error("Get from by tx's %x signer failed with %v.", vcommon.TxHash(tx), err) 164 | return false 165 | } 166 | if !bytes.Equal((*(tx.Data.From))[:], from.Bytes()) { 167 | log.Error("Transaction signature verify failed, tx.Data.From is %x, while signed from is %x.", *tx.Data.From, from) 168 | return false 169 | } 170 | return true 171 | } 172 | 173 | func (self *Worker) GetReceipts() types.Receipts { 174 | log.Debug("Get receipts.") 175 | return self.receipts 176 | } 177 | -------------------------------------------------------------------------------- /filter/transaction/tx_filter_test.go: -------------------------------------------------------------------------------- 1 | package transaction 2 | 3 | import ( 4 | "github.com/DSiSc/craft/types" 5 | "github.com/DSiSc/gossipswitch/port" 6 | "github.com/DSiSc/gossipswitch/util" 7 | wallett "github.com/DSiSc/wallet/core/types" 8 | "github.com/stretchr/testify/assert" 9 | "math/big" 10 | "testing" 11 | ) 12 | 13 | // Test new TxFilter 14 | func Test_NewTxFilter(t *testing.T) { 15 | assert := assert.New(t) 16 | var txFilter = NewTxFilter(&eventCenter{}, true, 0) 17 | assert.NotNil(txFilter, "FAILED: failed to create TxFilter") 18 | } 19 | 20 | // Test verify transaction message. 21 | func Test_TxFilterVerify(t *testing.T) { 22 | assert := assert.New(t) 23 | var txFilter = NewTxFilter(&eventCenter{}, true, 0) 24 | assert.NotNil(txFilter, "FAILED: failed to create TxFilter") 25 | 26 | addFrom := util.HexToAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b") 27 | originalTx := &types.Transaction{ 28 | Data: types.TxData{ 29 | AccountNonce: uint64(0), 30 | Price: new(big.Int), 31 | Recipient: &addressA, 32 | From: &addFrom, 33 | Amount: new(big.Int), 34 | Payload: addressB[:10], 35 | }, 36 | } 37 | key, _ := wallett.DefaultTestKey() 38 | tx, _ := wallett.SignTx(originalTx, new(wallett.FrontierSigner), key) 39 | assert.Nil(txFilter.Verify(port.LocalInPortId, tx), "PASS: verify validated message") 40 | assert.Nil(txFilter.Verify(port.RemoteInPortId, tx), "PASS: verify validated message") 41 | 42 | block := &types.Block{} 43 | assert.NotNil(txFilter.Verify(port.LocalInPortId, block), "PASS: verify in validated message") 44 | assert.NotNil(txFilter.Verify(port.RemoteInPortId, block), "PASS: verify in validated message") 45 | } 46 | 47 | // Test verify transaction message. 48 | func Test_TxFilterVerify1(t *testing.T) { 49 | assert := assert.New(t) 50 | var txFilter = NewTxFilter(&eventCenter{}, true, 0) 51 | assert.NotNil(txFilter, "FAILED: failed to create TxFilter") 52 | 53 | addFrom := util.HexToAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b") 54 | originalTx := &types.Transaction{ 55 | Data: types.TxData{ 56 | AccountNonce: uint64(0x1), 57 | Price: new(big.Int).SetInt64(0x1), 58 | Recipient: &addressA, 59 | From: &addFrom, 60 | Amount: new(big.Int), 61 | Payload: util.Hex2Bytes("0x608060405234801561001057600080fd5b506040516105d93803806105d983398101604052805160008054600160a060020a0319163317905501805161004c906001906020840190610053565b50506100ee565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061009457805160ff19168380011785556100c1565b828001600101855582156100c1579182015b828111156100c15782518255916020019190600101906100a6565b506100cd9291506100d1565b5090565b6100eb91905b808211156100cd57600081556001016100d7565b90565b6104dc806100fd6000396000f3006080604052600436106100565763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166341c0e1b5811461005b5780634ac0d66e14610072578063cfae3217146100cb575b600080fd5b34801561006757600080fd5b50610070610155565b005b34801561007e57600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526100709436949293602493928401919081908401838280828437509497506101929650505050505050565b3480156100d757600080fd5b506100e0610382565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561011a578181015183820152602001610102565b50505050905090810190601f1680156101475780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60005473ffffffffffffffffffffffffffffffffffffffff163314156101905760005473ffffffffffffffffffffffffffffffffffffffff16ff5b565b806040518082805190602001908083835b602083106101c25780518252601f1990920191602091820191016101a3565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390206001604051808280546001816001161561010002031660029004801561024c5780601f1061022a57610100808354040283529182019161024c565b820191906000526020600020905b815481529060010190602001808311610238575b505060408051918290038220818352600180546002600019610100838516150201909116049284018390529094507f047dcd1aa8b77b0b943642129c767533eeacd700c7c1eab092b8ce05d2b2faf59350918691819060208201906060830190869080156102fb5780601f106102d0576101008083540402835291602001916102fb565b820191906000526020600020905b8154815290600101906020018083116102de57829003601f168201915b5050838103825284518152845160209182019186019080838360005b8381101561032f578181015183820152602001610317565b50505050905090810190601f16801561035c5780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a3805161037e906001906020840190610418565b5050565b60018054604080516020601f6002600019610100878916150201909516949094049384018190048102820181019092528281526060939092909183018282801561040d5780601f106103e25761010080835404028352916020019161040d565b820191906000526020600020905b8154815290600101906020018083116103f057829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061045957805160ff1916838001178555610486565b82800160010185558215610486579182015b8281111561048657825182559160200191906001019061046b565b50610492929150610496565b5090565b61041591905b80821115610492576000815560010161049c5600a165627a7a723058202360802f45f120f2cd8bf9b7963e38317b72e805b49afc944fdce06a24372fd10029"), 62 | GasLimit: 0xbb8, 63 | }, 64 | } 65 | key, _ := wallett.DefaultTestKey() 66 | tx, _ := wallett.SignTx(originalTx, new(wallett.FrontierSigner), key) 67 | assert.Nil(txFilter.Verify(port.LocalInPortId, tx), "PASS: verify validated message") 68 | assert.Nil(txFilter.Verify(port.RemoteInPortId, tx), "PASS: verify validated message") 69 | 70 | block := &types.Block{} 71 | assert.NotNil(txFilter.Verify(port.LocalInPortId, block), "PASS: verify in validated message") 72 | assert.NotNil(txFilter.Verify(port.RemoteInPortId, block), "PASS: verify in validated message") 73 | } 74 | 75 | var addressA = types.Address{ 76 | 0xb2, 0x6f, 0x2b, 0x34, 0x2a, 0xab, 0x24, 0xbc, 0xf6, 0x3e, 77 | 0xa2, 0x18, 0xc6, 0xa9, 0x27, 0x4d, 0x30, 0xab, 0x9a, 0x15, 78 | } 79 | 80 | var addressB = types.Address{ 81 | 0x5f, 0xd5, 0x56, 0xa1, 0x56, 0x50, 0xcd, 0x19, 0xa2, 0xa, 82 | 0xdd, 0xb1, 0x1c, 0x3f, 0xa4, 0x99, 0x10, 0x9b, 0x98, 0xf9, 83 | } 84 | 85 | var addressA1 = types.Address{ 86 | 0xa9, 0x4f, 0x53, 0x74, 0xfc, 0xab, 0x24, 0xbc, 0xf6, 0x3e, 87 | 0xa2, 0x18, 0xc6, 0xa9, 0x27, 0x4d, 0x30, 0xab, 0x9a, 0x15, 88 | } 89 | 90 | type eventCenter struct { 91 | } 92 | 93 | // subscriber subscribe specified eventType with eventFunc 94 | func (*eventCenter) Subscribe(eventType types.EventType, eventFunc types.EventFunc) types.Subscriber { 95 | return nil 96 | } 97 | 98 | // subscriber unsubscribe specified eventType 99 | func (*eventCenter) UnSubscribe(eventType types.EventType, subscriber types.Subscriber) (err error) { 100 | return nil 101 | } 102 | 103 | // notify subscriber of eventType 104 | func (*eventCenter) Notify(eventType types.EventType, value interface{}) (err error) { 105 | return nil 106 | } 107 | 108 | // notify specified eventFunc 109 | func (*eventCenter) NotifySubscriber(eventFunc types.EventFunc, value interface{}) { 110 | 111 | } 112 | 113 | // notify subscriber traversing all events 114 | func (*eventCenter) NotifyAll() (errs []error) { 115 | return nil 116 | } 117 | 118 | // unsubscrible all event 119 | func (*eventCenter) UnSubscribeAll() { 120 | } 121 | -------------------------------------------------------------------------------- /filter/block/worker_test.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/types" 6 | "github.com/DSiSc/evm-NG" 7 | "github.com/DSiSc/monkey" 8 | "github.com/DSiSc/repository" 9 | "github.com/DSiSc/validator/common" 10 | "github.com/DSiSc/validator/tools" 11 | workerc "github.com/DSiSc/validator/worker/common" 12 | walletc "github.com/DSiSc/wallet/common" 13 | wallett "github.com/DSiSc/wallet/core/types" 14 | "github.com/stretchr/testify/assert" 15 | "math/big" 16 | "reflect" 17 | "testing" 18 | ) 19 | 20 | func TestNewWorker(t *testing.T) { 21 | assert := assert.New(t) 22 | var worker = NewWorker(nil, nil, false) 23 | assert.NotNil(worker) 24 | assert.Nil(worker.block) 25 | assert.Nil(worker.chain) 26 | } 27 | 28 | func TestGetTxsRoot(t *testing.T) { 29 | var trxs = make([]*types.Transaction, 0) 30 | trx := new(types.Transaction) 31 | trxs = append(trxs, trx) 32 | hash := GetTxsRoot(trxs) 33 | assert.NotNil(t, hash) 34 | } 35 | 36 | var addressA = types.Address{ 37 | 0xb2, 0x6f, 0x2b, 0x34, 0x2a, 0xab, 0x24, 0xbc, 0xf6, 0x3e, 38 | 0xa2, 0x18, 0xc6, 0xa9, 0x27, 0x4d, 0x30, 0xab, 0x9a, 0x15, 39 | } 40 | 41 | var addressC = walletc.Address{ 42 | 0xb2, 0x6f, 0x2b, 0x34, 0x2a, 0xab, 0x24, 0xbc, 0xf6, 0x3e, 43 | 0xa2, 0x18, 0xc6, 0xa9, 0x27, 0x4d, 0x30, 0xab, 0x9a, 0x16, 44 | } 45 | 46 | var addressNew = types.Address{ 47 | 0xb2, 0x6f, 0x2b, 0x34, 0x2a, 0xab, 0x24, 0xbc, 0xf6, 0x3e, 48 | 0xa2, 0x18, 0xc6, 0xa9, 0x27, 0x4d, 0x30, 0xab, 0x9a, 0x17, 49 | } 50 | 51 | var addressB = tools.HexToAddress("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b") 52 | 53 | var mockHash = types.Hash{ 54 | 0x1d, 0xcf, 0x7, 0xba, 0xfc, 0x42, 0xb0, 0x8d, 0xfd, 0x23, 0x9c, 0x45, 0xa4, 0xb9, 0x38, 0xd, 55 | 0x8d, 0xfe, 0x5d, 0x6f, 0xa7, 0xdb, 0xd5, 0x50, 0xc9, 0x25, 0xb1, 0xb3, 0x4, 0xdc, 0xc5, 0x1c, 56 | } 57 | 58 | var mockHash1 = types.Hash{ 59 | 0x1e, 0xcf, 0x7, 0xba, 0xfc, 0x42, 0xb0, 0x8d, 0xfd, 0x23, 0x9c, 0x45, 0xa4, 0xb9, 0x38, 0xd, 60 | 0x8d, 0xfe, 0x5d, 0x6f, 0xa7, 0xdb, 0xd5, 0x50, 0xc9, 0x25, 0xb1, 0xb3, 0x4, 0xdc, 0xc5, 0x1c, 61 | } 62 | 63 | func TestWorker_VerifyTrsSignature(t *testing.T) { 64 | key, _ := wallett.DefaultTestKey() 65 | mockBlock := &types.Block{ 66 | Header: &types.Header{ 67 | ChainID: uint64(1), 68 | Height: uint64(1), 69 | }, 70 | } 71 | 72 | mockTrx := &types.Transaction{ 73 | Data: types.TxData{ 74 | AccountNonce: uint64(0), 75 | Price: new(big.Int), 76 | Recipient: &addressA, 77 | From: &addressB, 78 | Amount: new(big.Int), 79 | Payload: addressB[:10], 80 | }, 81 | } 82 | mockTransaction, _ := wallett.SignTx(mockTrx, wallett.NewEIP155Signer(big.NewInt(1)), key) 83 | worker := NewWorker(nil, mockBlock, false) 84 | ok := worker.VerifyTrsSignature(mockTransaction) 85 | assert.Equal(t, true, ok) 86 | 87 | monkey.Patch(wallett.Sender, func(wallett.Signer, *types.Transaction) (walletc.Address, error) { 88 | return addressC, fmt.Errorf("unknown signer") 89 | }) 90 | ok = worker.VerifyTrsSignature(mockTransaction) 91 | assert.Equal(t, false, ok) 92 | 93 | monkey.Patch(wallett.Sender, func(wallett.Signer, *types.Transaction) (walletc.Address, error) { 94 | return addressC, nil 95 | }) 96 | ok = worker.VerifyTrsSignature(mockTransaction) 97 | assert.Equal(t, false, ok) 98 | monkey.UnpatchAll() 99 | } 100 | 101 | func TestWorker_VerifyBlock(t *testing.T) { 102 | assert := assert.New(t) 103 | var Repository *repository.Repository 104 | var mockBlock = &types.Block{ 105 | Header: &types.Header{ 106 | ChainID: uint64(1), 107 | Height: uint64(1), 108 | }, 109 | } 110 | worker := NewWorker(nil, mockBlock, false) 111 | 112 | monkey.PatchInstanceMethod(reflect.TypeOf(Repository), "GetCurrentBlock", func(*repository.Repository) *types.Block { 113 | return &types.Block{ 114 | Header: &types.Header{ 115 | ChainID: uint64(0), 116 | }, 117 | } 118 | }) 119 | err := worker.VerifyBlock() 120 | assert.NotNil(err, "chain id not consistent") 121 | 122 | monkey.PatchInstanceMethod(reflect.TypeOf(Repository), "GetCurrentBlock", func(*repository.Repository) *types.Block { 123 | return &types.Block{ 124 | Header: &types.Header{ 125 | ChainID: uint64(1), 126 | }, 127 | HeaderHash: mockHash, 128 | } 129 | }) 130 | err = worker.VerifyBlock() 131 | assert.NotNil(err, "Block pre block hash not consistent") 132 | 133 | monkey.PatchInstanceMethod(reflect.TypeOf(Repository), "GetCurrentBlock", func(*repository.Repository) *types.Block { 134 | return &types.Block{ 135 | Header: &types.Header{ 136 | ChainID: uint64(1), 137 | }, 138 | } 139 | }) 140 | monkey.PatchInstanceMethod(reflect.TypeOf(Repository), "GetCurrentBlockHeight", func(*repository.Repository) uint64 { 141 | return 1 142 | }) 143 | //mockBlock.Header.ChainID = uint64(0) 144 | err = worker.VerifyBlock() 145 | assert.NotNil(err, "Block height not consistent") 146 | 147 | monkey.PatchInstanceMethod(reflect.TypeOf(Repository), "GetCurrentBlockHeight", func(*repository.Repository) uint64 { 148 | return 0 149 | }) 150 | worker.block.Header.TxRoot = MockHash 151 | err = worker.VerifyBlock() 152 | assert.NotNil(err, "Block txroot hash not consistent") 153 | 154 | monkey.Patch(common.HeaderHash, func(*types.Block) types.Hash { 155 | return MockHash 156 | }) 157 | var tmp types.Hash 158 | worker.block.Header.TxRoot = tmp 159 | worker.block.HeaderHash = mockHash1 160 | err = worker.VerifyBlock() 161 | assert.NotNil(err, "Block header hash not consistent") 162 | 163 | monkey.Patch(common.HeaderHash, func(*types.Block) types.Hash { 164 | var tmp types.Hash 165 | return tmp 166 | }) 167 | worker.block.Header.ReceiptsRoot = MockHash 168 | worker.block.HeaderHash = common.HeaderHash(worker.block) 169 | err = worker.VerifyBlock() 170 | assert.NotNil(err, "Receipts hash not consistent") 171 | 172 | worker.block.Header.ReceiptsRoot = tmp 173 | err = worker.VerifyBlock() 174 | assert.Nil(err) 175 | monkey.UnpatchAll() 176 | 177 | } 178 | 179 | func TestWorker_VerifyTransaction(t *testing.T) { 180 | assert := assert.New(t) 181 | worker := NewWorker(nil, nil, false) 182 | 183 | monkey.Patch(evm.NewEVMContext, func(types.Transaction, *types.Header, *repository.Repository, types.Address) evm.Context { 184 | return evm.Context{ 185 | GasLimit: uint64(65536), 186 | } 187 | }) 188 | monkey.Patch(ApplyTransaction, func(author types.Address, header *types.Header, chain *repository.Repository, tx *types.Transaction, gp *workerc.GasPool) ([]byte, uint64, bool, error, types.Address) { 189 | return addressA[:10], uint64(0), false, fmt.Errorf("Apply failed."), types.Address{} 190 | }) 191 | mockTrx := &types.Transaction{ 192 | Data: types.TxData{ 193 | AccountNonce: uint64(0), 194 | Price: new(big.Int), 195 | Recipient: &addressA, 196 | From: &addressB, 197 | Amount: new(big.Int), 198 | Payload: addressB[:10], 199 | }, 200 | } 201 | receipt, gas, err := worker.VerifyTransaction(addressA, nil, nil, mockTrx, nil) 202 | assert.Equal(err, fmt.Errorf("Apply failed.")) 203 | assert.Nil(receipt) 204 | assert.Equal(uint64(0), gas) 205 | monkey.UnpatchAll() 206 | } 207 | 208 | func TestWorker_GetReceipts(t *testing.T) { 209 | assert := assert.New(t) 210 | worker := NewWorker(nil, nil, false) 211 | receipts := worker.GetReceipts() 212 | assert.Equal(len(receipts), len(worker.receipts)) 213 | } 214 | -------------------------------------------------------------------------------- /filter/block/state_test.go: -------------------------------------------------------------------------------- 1 | package block 2 | 3 | import ( 4 | "encoding/hex" 5 | "encoding/json" 6 | "github.com/DSiSc/craft/types" 7 | "github.com/DSiSc/evm-NG" 8 | "github.com/DSiSc/evm-NG/common/math" 9 | "github.com/DSiSc/monkey" 10 | "github.com/DSiSc/repository" 11 | "github.com/DSiSc/validator/worker/common" 12 | "github.com/stretchr/testify/assert" 13 | "math/big" 14 | "reflect" 15 | "testing" 16 | "time" 17 | ) 18 | 19 | var MockHash = types.Hash{ 20 | 0x1d, 0xcf, 0x7, 0xba, 0xfc, 0x42, 0xb0, 0x8d, 0xfd, 0x23, 0x9c, 0x45, 0xa4, 0xb9, 0x38, 0xd, 21 | 0x8d, 0xfe, 0x5d, 0x6f, 0xa7, 0xdb, 0xd5, 0x50, 0xc9, 0x25, 0xb1, 0xb3, 0x4, 0xdc, 0xc5, 0x1c, 22 | } 23 | 24 | var MockBlock = &types.Block{ 25 | Header: &types.Header{ 26 | ChainID: 1, 27 | PrevBlockHash: MockHash, 28 | StateRoot: MockHash, 29 | TxRoot: MockHash, 30 | ReceiptsRoot: MockHash, 31 | Height: 1, 32 | Timestamp: uint64(time.Date(2018, time.August, 28, 0, 0, 0, 0, time.UTC).Unix()), 33 | MixDigest: MockHash, 34 | }, 35 | Transactions: make([]*types.Transaction, 0), 36 | } 37 | 38 | var author = types.Address{ 39 | 0xb1, 0x6f, 0x2b, 0x34, 0x2a, 0xab, 0x24, 0xbc, 0xf6, 0x3e, 40 | 0xc2, 0x18, 0xc6, 0xa9, 0x27, 0x4d, 0x30, 0xab, 0x9a, 0x15, 41 | } 42 | 43 | var from = &types.Address{ 44 | 0xb1, 0x6f, 0x2b, 0x34, 0x2a, 0xab, 0x24, 0xbc, 0xf6, 0x3e, 45 | 0xa2, 0x18, 0xc6, 0xa9, 0x27, 0x4d, 0x30, 0xab, 0x9a, 0x15, 46 | } 47 | 48 | var to = &types.Address{ 49 | 0xb2, 0x6f, 0x2b, 0x34, 0x2a, 0xab, 0x24, 0xbc, 0xf6, 0x3e, 50 | 0xa2, 0x18, 0xc6, 0xa9, 0x27, 0x4d, 0x30, 0xab, 0x9a, 0x15, 51 | } 52 | 53 | var contractAddress = types.Address{ 54 | 0xb2, 0x6f, 0x2b, 0x34, 0x2a, 0xab, 0x24, 0xbc, 0xf6, 0x3e, 55 | 0xa2, 0x18, 0xc6, 0xa9, 0x27, 0x4d, 0x30, 0xab, 0x9a, 0x16, 56 | } 57 | 58 | var wasmContractAddress = types.Address{ 59 | 0x79, 0x46, 0x28, 0x8, 0xf6, 0xd1, 0xa6, 0x42, 0x81, 0xd, 0x96, 0xa1, 0xfb, 0x67, 0x5c, 0x33, 0xcf, 0x60, 0xc8, 0x65, 60 | } 61 | 62 | func mockTrx() *types.Transaction { 63 | return &types.Transaction{ 64 | Data: types.TxData{ 65 | AccountNonce: 0, 66 | Price: new(big.Int).SetUint64(10), 67 | GasLimit: 100, 68 | Recipient: to, 69 | From: from, 70 | Amount: new(big.Int).SetUint64(50), 71 | Payload: to[:10], 72 | }, 73 | } 74 | } 75 | 76 | var state *StateTransition 77 | 78 | func TestNewStateTransition(t *testing.T) { 79 | bc := &repository.Repository{} 80 | var gp = common.GasPool(6) 81 | state = NewStateTransition(author, MockBlock.Header, bc, mockTrx(), &gp) 82 | assert.NotNil(t, state) 83 | assert.NotNil(t, state.tx) 84 | } 85 | 86 | func TestStateTransition_TransitionDb(t *testing.T) { 87 | // test carets contract 88 | bc := &repository.Repository{} 89 | var gp = common.GasPool(10000) 90 | state = NewStateTransition(author, MockBlock.Header, bc, mockTrx(), &gp) 91 | state.tx.Data.Recipient = nil 92 | var evmd *evm.EVM 93 | monkey.PatchInstanceMethod(reflect.TypeOf(evmd), "Create", func(*evm.EVM, evm.ContractRef, []byte, uint64, *big.Int) ([]byte, types.Address, uint64, error) { 94 | return to[:10], contractAddress, 0, evm.ErrInsufficientBalance 95 | }) 96 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "GetBalance", func(*repository.Repository, types.Address) *big.Int { 97 | return new(big.Int).SetUint64(1000) 98 | }) 99 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "SubBalance", func(*repository.Repository, types.Address, *big.Int) { 100 | return 101 | }) 102 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "GetNonce", func(*repository.Repository, types.Address) uint64 { 103 | return 0 104 | }) 105 | ret, used, ok, err, address := state.TransitionDb() 106 | assert.Equal(t, err, evm.ErrInsufficientBalance) 107 | assert.Equal(t, ok, false) 108 | assert.Equal(t, uint64(0), used) 109 | assert.Equal(t, to[:10], ret) 110 | assert.Equal(t, contractAddress, address) 111 | monkey.UnpatchAll() 112 | } 113 | 114 | func TestStateTransition_TransitionDb1(t *testing.T) { 115 | defer monkey.UnpatchAll() 116 | // test transfer token 117 | bc := &repository.Repository{} 118 | var gp = common.GasPool(10000) 119 | state = NewStateTransition(author, MockBlock.Header, bc, mockTrx(), &gp) 120 | var evmd *evm.EVM 121 | monkey.PatchInstanceMethod(reflect.TypeOf(evmd), "Create", func(*evm.EVM, evm.ContractRef, []byte, uint64, *big.Int) ([]byte, types.Address, uint64, error) { 122 | return to[:10], contractAddress, 0, evm.ErrInsufficientBalance 123 | }) 124 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "GetBalance", func(*repository.Repository, types.Address) *big.Int { 125 | return new(big.Int).SetUint64(1000) 126 | }) 127 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "SubBalance", func(*repository.Repository, types.Address, *big.Int) { 128 | return 129 | }) 130 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "GetNonce", func(*repository.Repository, types.Address) uint64 { 131 | return 0 132 | }) 133 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "SetNonce", func(b *repository.Repository, a types.Address, n uint64) { 134 | assert.Equal(t, uint64(1), n) 135 | return 136 | }) 137 | monkey.PatchInstanceMethod(reflect.TypeOf(evmd), "Call", func(*evm.EVM, evm.ContractRef, types.Address, []byte, uint64, *big.Int) ([]byte, uint64, error) { 138 | return []byte{0}, math.MaxUint64, nil 139 | }) 140 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "GetCode", func(*repository.Repository, types.Address) []byte { 141 | return []byte{} 142 | }) 143 | ret, used, ok, err, _ := state.TransitionDb() 144 | assert.Equal(t, nil, err) 145 | assert.Equal(t, ok, false) 146 | assert.Equal(t, uint64(0), used) 147 | assert.Equal(t, []byte{0}, ret) 148 | 149 | state.nonce = 100 150 | ret, used, ok, err, _ = state.TransitionDb() 151 | assert.NotNil(t, err) 152 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "GetNonce", func(*repository.Repository, types.Address) uint64 { 153 | return 3 154 | }) 155 | ret, used, ok, err, _ = state.TransitionDb() 156 | assert.NotNil(t, err) 157 | } 158 | 159 | // test create wasm contract 160 | func TestStateTransition_TransitionDb2(t *testing.T) { 161 | // test carets contract 162 | bc := &repository.Repository{} 163 | var gp = common.GasPool(10000) 164 | tx := mockTrx() 165 | code, _ := hex.DecodeString("0061736d0100000001070160027f7f017f03020100070801046961646400000a09010700200020016a0b") 166 | tx.Data.Payload = code 167 | state = NewStateTransition(author, MockBlock.Header, bc, tx, &gp) 168 | state.tx.Data.Recipient = nil 169 | var evmd *evm.EVM 170 | monkey.PatchInstanceMethod(reflect.TypeOf(evmd), "Create", func(*evm.EVM, evm.ContractRef, []byte, uint64, *big.Int) ([]byte, types.Address, uint64, error) { 171 | return to[:10], contractAddress, 0, evm.ErrInsufficientBalance 172 | }) 173 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "GetBalance", func(*repository.Repository, types.Address) *big.Int { 174 | return new(big.Int).SetUint64(1000) 175 | }) 176 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "SubBalance", func(*repository.Repository, types.Address, *big.Int) { 177 | return 178 | }) 179 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "GetNonce", func(*repository.Repository, types.Address) uint64 { 180 | return 0 181 | }) 182 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "AddBalance", func(*repository.Repository, types.Address, *big.Int) { 183 | }) 184 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "SetNonce", func(*repository.Repository, types.Address, uint64) { 185 | }) 186 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "CreateAccount", func(*repository.Repository, types.Address) { 187 | }) 188 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "SetCode", func(*repository.Repository, types.Address, []byte) { 189 | }) 190 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "GetCodeHash", func(*repository.Repository, types.Address) types.Hash { 191 | return types.Hash{} 192 | }) 193 | _, used, ok, err, address := state.TransitionDb() 194 | assert.Nil(t, err) 195 | assert.Equal(t, ok, false) 196 | assert.Equal(t, uint64(0), used) 197 | assert.Equal(t, wasmContractAddress, address) 198 | monkey.UnpatchAll() 199 | } 200 | 201 | // test call wasm contract 202 | func TestStateTransition_TransitionDb3(t *testing.T) { 203 | defer monkey.UnpatchAll() 204 | // test carets contract 205 | bc := &repository.Repository{} 206 | var gp = common.GasPool(10000) 207 | tx := mockTrx() 208 | tx.Data.Recipient = &wasmContractAddress 209 | code, _ := hex.DecodeString("0061736d01000000018c808080000260017f017f60027f7f017f028e808080000103656e76066d616c6c6f6300000382808080000101048480808000017000000583808080000100010681808080000007938080800002066d656d6f7279020006696e766f6b6500010a998080800001938080800001017f41021000220241c8d2013b000020020b") 210 | tx.Data.Payload, _ = json.Marshal([]string{"Hi", "Bob"}) 211 | state = NewStateTransition(author, MockBlock.Header, bc, tx, &gp) 212 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "GetBalance", func(*repository.Repository, types.Address) *big.Int { 213 | return new(big.Int).SetUint64(1000) 214 | }) 215 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "SubBalance", func(*repository.Repository, types.Address, *big.Int) { 216 | return 217 | }) 218 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "GetNonce", func(*repository.Repository, types.Address) uint64 { 219 | return 0 220 | }) 221 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "AddBalance", func(*repository.Repository, types.Address, *big.Int) { 222 | }) 223 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "SetNonce", func(*repository.Repository, types.Address, uint64) { 224 | }) 225 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "CreateAccount", func(*repository.Repository, types.Address) { 226 | }) 227 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "GetCode", func(*repository.Repository, types.Address) []byte { 228 | return code 229 | }) 230 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "SetCode", func(*repository.Repository, types.Address, []byte) { 231 | }) 232 | monkey.PatchInstanceMethod(reflect.TypeOf(bc), "GetCodeHash", func(*repository.Repository, types.Address) types.Hash { 233 | return types.Hash{} 234 | }) 235 | ret, used, ok, err, address := state.TransitionDb() 236 | assert.Nil(t, err) 237 | assert.Equal(t, []byte{'H', 'i'}, ret) 238 | assert.Equal(t, ok, false) 239 | assert.Equal(t, uint64(0), used) 240 | assert.Equal(t, types.Address{}, address) 241 | monkey.UnpatchAll() 242 | } 243 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | --------------------------------------------------------------------------------