├── CHANGELOG.md ├── docs └── .gitignore ├── role ├── config │ └── config.go ├── common │ ├── common_test.go │ └── common.go ├── role.go ├── policy │ ├── solo │ │ ├── solo.go │ │ └── solo_test.go │ └── dpos │ │ ├── dpos.go │ │ └── dpos_test.go └── role_test.go ├── participates ├── config │ └── config.go ├── common │ └── common.go ├── policy │ ├── solo │ │ ├── solo_test.go │ │ └── solo.go │ └── dpos │ │ ├── dpos.go │ │ └── dpos_test.go ├── participates.go └── participates_test.go ├── AUTHORS.md ├── .dockerignore ├── vendor ├── github.com │ ├── stretchr │ │ └── testify │ │ │ ├── assert │ │ │ ├── assertion_format.go.tmpl │ │ │ ├── assertion_forward.go.tmpl │ │ │ ├── errors.go │ │ │ ├── forward_assertions.go │ │ │ ├── doc.go │ │ │ └── http_assertions.go │ │ │ └── LICENSE │ ├── DSiSc │ │ ├── producer │ │ │ └── config │ │ │ │ ├── config.json │ │ │ │ └── config.go │ │ └── txpool │ │ │ └── common │ │ │ └── log │ │ │ └── log.go │ ├── davecgh │ │ └── go-spew │ │ │ ├── LICENSE │ │ │ └── spew │ │ │ ├── bypasssafe.go │ │ │ ├── bypass.go │ │ │ ├── spew.go │ │ │ └── doc.go │ └── pmezard │ │ └── go-difflib │ │ └── LICENSE └── vendor.json ├── contrib ├── git │ ├── .gitconfig.tmp │ └── .gitmessage.tmp └── LICENSE-APPLY ├── test └── test_cov.sh ├── .gitattributes ├── scripts ├── unit_test_cov.sh ├── changelog.sh ├── check_spelling.sh ├── install_behave.sh ├── ensure_deps.sh └── check_license.sh ├── dependencies.txt ├── .codecov.yml ├── README.md ├── consensus ├── config │ └── config.go ├── utils │ ├── events_test.go │ ├── events.go │ ├── utils.go │ ├── payloads.go │ ├── utils_test.go │ └── payloads_test.go ├── policy │ ├── pbft │ │ └── tools │ │ │ ├── timer_test.go │ │ │ ├── events.go │ │ │ ├── events_test.go │ │ │ └── timer.go │ ├── bft │ │ ├── bft.go │ │ └── bft_test.go │ ├── fbft │ │ └── fbft.go │ ├── dbft │ │ ├── dbft.go │ │ └── dbft_test.go │ └── solo │ │ ├── solo.go │ │ └── solo_test.go ├── consensus.go ├── consensus_test.go ├── common │ ├── views_test.go │ ├── common.go │ ├── common_test.go │ ├── views.go │ ├── pugin_test.go │ └── plugin.go └── messages │ ├── message.go │ └── message_test.go ├── .gitignore ├── common └── common.go ├── .circleci └── config.yml ├── galaxy.go ├── version └── version.go ├── galaxy_test.go ├── CONTRIBUTING.md └── Makefile /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _build/ 2 | -------------------------------------------------------------------------------- /role/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type RoleConfig struct { 4 | PolicyName string 5 | } 6 | -------------------------------------------------------------------------------- /participates/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type ParticipateConfig struct { 4 | PolicyName string 5 | } 6 | -------------------------------------------------------------------------------- /participates/common/common.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | const ( 4 | DposPolicy = "dpos" 5 | SoloPolicy = "solo" 6 | ) 7 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # Credits 2 | 3 | ## Development Lead 4 | 5 | - walterkangluo [DSiSc](https://github.com/DSiSc) 6 | 7 | ## Contributors 8 | 9 | None yet. Why not be the first? -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl: -------------------------------------------------------------------------------- 1 | {{.CommentFormat}} 2 | func {{.DocInfo.Name}}f(t TestingT, {{.ParamsFormat}}) bool { 3 | if h, ok := t.(tHelper); ok { h.Helper() } 4 | return {{.DocInfo.Name}}(t, {{.ForwardedParamsFormat}}) 5 | } 6 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl: -------------------------------------------------------------------------------- 1 | {{.CommentWithoutT "a"}} 2 | func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool { 3 | if h, ok := a.t.(tHelper); ok { h.Helper() } 4 | return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) 5 | } 6 | -------------------------------------------------------------------------------- /vendor/github.com/DSiSc/producer/config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "producer":{ 3 | "policy":"timer", 4 | "timer":{ 5 | "time": "10" 6 | }, 7 | "voter":{ 8 | "number": "1" 9 | } 10 | }, 11 | "block":{ 12 | "store":"leveldb" 13 | }, 14 | "participates":{ 15 | "policy":"solo" 16 | } 17 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /role/common/common_test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func Test_Role(t *testing.T) { 9 | assert := assert.New(t) 10 | assert.Equal(0, int(Master)) 11 | assert.Equal(1, int(Slave)) 12 | assert.Equal(2, int(Normal)) 13 | assert.Equal(3, int(UnKnown)) 14 | } 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /vendor/github.com/DSiSc/txpool/common/log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | func Info(format string) { 8 | log.Printf("[Info] %s\n", format) 9 | } 10 | 11 | func Warn(format string) { 12 | log.Printf("[Warning] %s\n", format) 13 | } 14 | 15 | func Error(format string) { 16 | log.Printf("[Error] %s\n", format) 17 | } 18 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/errors.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // AnError is an error instance useful for testing. If the code does not care 8 | // about error specifics, and only needs to return the error for example, this 9 | // error should be used to make the test code more readable. 10 | var AnError = errors.New("assert.AnError general error for testing") 11 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/producer:master 6 | github.com/DSiSc/txpool:master 7 | github.com/DSiSc/craft:master 8 | github.com/DSiSc/justitia:master 9 | github.com/DSiSc/blockstore:master 10 | github.com/DSiSc/monkey:master 11 | github.com/DSiSc/repository:master -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # galaxy 2 | 3 | An implemention of consensus. 4 | 5 | [![Build Status](https://circleci.com/gh/DSiSc/galaxy/tree/master.svg?style=shield)](https://circleci.com/gh/DSiSc/galaxy/tree/master) 6 | [![codecov](https://codecov.io/gh/DSiSc/galaxy/branch/master/graph/badge.svg)](https://codecov.io/gh/DSiSc/galaxy) 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 | -------------------------------------------------------------------------------- /consensus/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type ConsensusConfig struct { 4 | PolicyName string 5 | Timeout ConsensusTimeout 6 | EnableEmptyBlock bool 7 | SignVerifySwitch SignatureVerifySwitch 8 | } 9 | 10 | type SignatureVerifySwitch struct { 11 | SyncVerifySignature bool 12 | LocalVerifySignature bool 13 | } 14 | 15 | type ConsensusTimeout struct { 16 | TimeoutToCollectResponseMsg int64 17 | TimeoutToWaitCommitMsg int64 18 | TimeoutToChangeView int64 19 | } 20 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/forward_assertions.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | // Assertions provides assertion methods around the 4 | // TestingT interface. 5 | type Assertions struct { 6 | t TestingT 7 | } 8 | 9 | // New makes a new Assertions object for the specified TestingT. 10 | func New(t TestingT) *Assertions { 11 | return &Assertions{ 12 | t: t, 13 | } 14 | } 15 | 16 | //go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs 17 | -------------------------------------------------------------------------------- /role/common/common.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import "errors" 4 | 5 | type Roler int 6 | 7 | // Role 8 | const ( 9 | Master Roler = iota // Master --> 0 10 | Slave // Slave --> 1 11 | Normal // Normal --> 2, node that not participation in consensus 12 | UnKnown // UnKnown --> 3, node that nobody knows 13 | ) 14 | 15 | const ( 16 | SoloPolicy = "solo" 17 | DposPolicy = "dpos" 18 | ) 19 | 20 | var ( 21 | AssignmentNotBeExecute = errors.New("assignments role has not be executed") 22 | ) 23 | -------------------------------------------------------------------------------- /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\/galaxy\/commit\/\1)/" >> ${CHANGELOG_TEMP} 16 | echo "" >> ${CHANGELOG_TEMP} 17 | cat ${SCRIPT_DIR}/../CHANGELOG.md >> ${CHANGELOG_TEMP} 18 | mv -f ${CHANGELOG_TEMP} CHANGELOG.md 19 | -------------------------------------------------------------------------------- /consensus/utils/events_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | type MockReceive struct { 9 | init bool 10 | } 11 | 12 | func NewMockReceive() Receiver { 13 | return &MockReceive{ 14 | init: true, 15 | } 16 | } 17 | 18 | var signal interface{} 19 | 20 | func (instance *MockReceive) ProcessEvent(e Event) Event { 21 | signal = e 22 | return nil 23 | } 24 | 25 | func TestSendEvent2(t *testing.T) { 26 | receive := NewMockReceive() 27 | assert.Equal(t, true, receive.(*MockReceive).init) 28 | SendEvent(receive, 2) 29 | assert.Equal(t, 2, signal) 30 | } 31 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /common/common.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "github.com/DSiSc/galaxy/consensus" 5 | consensusConfig "github.com/DSiSc/galaxy/consensus/config" 6 | "github.com/DSiSc/galaxy/participates" 7 | "github.com/DSiSc/galaxy/participates/config" 8 | "github.com/DSiSc/galaxy/role" 9 | roleConfig "github.com/DSiSc/galaxy/role/config" 10 | ) 11 | 12 | type GalaxyPlugin struct { 13 | Participates participates.Participates 14 | Role role.Role 15 | Consensus consensus.Consensus 16 | } 17 | 18 | type GalaxyPluginConf struct { 19 | BlockSwitch chan<- interface{} 20 | ParticipateConf config.ParticipateConfig 21 | RoleConf roleConfig.RoleConfig 22 | ConsensusConf consensusConfig.ConsensusConfig 23 | } 24 | -------------------------------------------------------------------------------- /.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/galaxy 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 | -------------------------------------------------------------------------------- /consensus/utils/events.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | // Event is a type meant to clearly convey that the return type or parameter to a function will be supplied to/from an events.Manager 4 | type Event interface{} 5 | 6 | // Receiver is a consumer of events, ProcessEvent will be called serially 7 | // as events arrive 8 | type Receiver interface { 9 | // ProcessEvent delivers an event to the Receiver, if it returns non-nil, the return is the next processed event 10 | ProcessEvent(e Event) Event 11 | } 12 | 13 | // SendEvent performs the event loop on a receiver to completion 14 | func SendEvent(receiver Receiver, event Event) { 15 | next := event 16 | for { 17 | // If an event returns something non-nil, then process it as a new event 18 | next = receiver.ProcessEvent(next) 19 | if next == nil { 20 | break 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2012-2016 Dave Collins 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /participates/policy/solo/solo_test.go: -------------------------------------------------------------------------------- 1 | package solo 2 | 3 | import ( 4 | "github.com/DSiSc/galaxy/participates/common" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | func Test_NewSoloPolicy(t *testing.T) { 10 | asserts := assert.New(t) 11 | policy := NewSoloPolicy() 12 | asserts.NotNil(policy) 13 | asserts.Equal(common.SoloPolicy, policy.name, "they should not be equal") 14 | } 15 | 16 | func Test_PolicyName(t *testing.T) { 17 | asserts := assert.New(t) 18 | policy := NewSoloPolicy() 19 | policyName := policy.PolicyName() 20 | asserts.Equal(common.SoloPolicy, policyName, "they should not be equal") 21 | } 22 | 23 | func Test_GetParticipates(t *testing.T) { 24 | asserts := assert.New(t) 25 | policy := NewSoloPolicy() 26 | address, err := policy.GetParticipates() 27 | asserts.NotNil(address) 28 | asserts.Nil(err) 29 | asserts.Equal(1, len(address), "they should not be equal") 30 | asserts.Equal("127.0.0.1:8080", address[0].Extension.Url) 31 | } 32 | -------------------------------------------------------------------------------- /galaxy.go: -------------------------------------------------------------------------------- 1 | package galaxy 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/log" 6 | "github.com/DSiSc/galaxy/common" 7 | "github.com/DSiSc/galaxy/consensus" 8 | "github.com/DSiSc/galaxy/participates" 9 | "github.com/DSiSc/galaxy/role" 10 | ) 11 | 12 | func NewGalaxyPlugin(conf common.GalaxyPluginConf) (*common.GalaxyPlugin, error) { 13 | participates, err := participates.NewParticipates(conf.ParticipateConf) 14 | if nil != err { 15 | log.Error("Init participates failed.") 16 | return nil, fmt.Errorf("participates init failed") 17 | } 18 | role, err := role.NewRole(conf.RoleConf) 19 | if nil != err { 20 | log.Error("Init role failed.") 21 | return nil, fmt.Errorf("role init failed") 22 | } 23 | consensus, err := consensus.NewConsensus(conf.ConsensusConf, conf.BlockSwitch) 24 | if nil != err { 25 | log.Error("Init consensus failed.") 26 | return nil, fmt.Errorf("consensus init failed") 27 | } 28 | return &common.GalaxyPlugin{ 29 | Participates: participates, 30 | Role: role, 31 | Consensus: consensus, 32 | }, err 33 | } 34 | -------------------------------------------------------------------------------- /participates/participates.go: -------------------------------------------------------------------------------- 1 | package participates 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/log" 6 | "github.com/DSiSc/galaxy/participates/common" 7 | "github.com/DSiSc/galaxy/participates/config" 8 | "github.com/DSiSc/galaxy/participates/policy/dpos" 9 | "github.com/DSiSc/galaxy/participates/policy/solo" 10 | "github.com/DSiSc/validator/tools/account" 11 | ) 12 | 13 | type Participates interface { 14 | PolicyName() string 15 | GetParticipates() ([]account.Account, error) 16 | } 17 | 18 | func NewParticipates(conf config.ParticipateConfig) (Participates, error) { 19 | var err error 20 | var participates Participates 21 | participatesPolicy := conf.PolicyName 22 | switch participatesPolicy { 23 | case common.SoloPolicy: 24 | log.Info("Get participates policy is solo.") 25 | participates = solo.NewSoloPolicy() 26 | case common.DposPolicy: 27 | log.Info("Get participates policy is dpos.") 28 | participates = dpos.NewDPOSPolicy() 29 | default: 30 | log.Error("Now, we only support solo policy participates.") 31 | err = fmt.Errorf("not supported type") 32 | } 33 | return participates, err 34 | } 35 | -------------------------------------------------------------------------------- /role/role.go: -------------------------------------------------------------------------------- 1 | package role 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/log" 6 | "github.com/DSiSc/galaxy/role/common" 7 | "github.com/DSiSc/galaxy/role/config" 8 | "github.com/DSiSc/galaxy/role/policy/dpos" 9 | "github.com/DSiSc/galaxy/role/policy/solo" 10 | "github.com/DSiSc/validator/tools/account" 11 | ) 12 | 13 | type Role interface { 14 | PolicyName() string 15 | RoleAssignments([]account.Account) (map[account.Account]common.Roler, account.Account, error) 16 | ChangeRoleAssignment(map[account.Account]common.Roler, uint64) 17 | GetRoles(account.Account) (common.Roler, error) 18 | } 19 | 20 | func NewRole(conf config.RoleConfig) (Role, error) { 21 | var err error 22 | var role Role 23 | rolePolicy := conf.PolicyName 24 | switch rolePolicy { 25 | case common.SoloPolicy: 26 | log.Info("Get role policy is solo.") 27 | role, err = solo.NewSoloPolicy() 28 | case common.DposPolicy: 29 | log.Info("Get role policy is dpos.") 30 | role, err = dpos.NewDPOSPolicy() 31 | default: 32 | log.Error("Now, we only support solo role policy.") 33 | err = fmt.Errorf("unkonwn policy type") 34 | } 35 | return role, err 36 | } 37 | -------------------------------------------------------------------------------- /participates/participates_test.go: -------------------------------------------------------------------------------- 1 | package participates 2 | 3 | import ( 4 | "github.com/DSiSc/contractsManage/contracts" 5 | "github.com/DSiSc/galaxy/participates/common" 6 | "github.com/DSiSc/galaxy/participates/config" 7 | "github.com/DSiSc/monkey" 8 | "github.com/stretchr/testify/assert" 9 | "testing" 10 | ) 11 | 12 | func mock_conf(policy string) config.ParticipateConfig { 13 | return config.ParticipateConfig{ 14 | PolicyName: policy, 15 | } 16 | } 17 | 18 | func Test_NewParticipates(t *testing.T) { 19 | asserts := assert.New(t) 20 | conf := mock_conf(common.SoloPolicy) 21 | participate, err := NewParticipates(conf) 22 | asserts.NotNil(participate) 23 | asserts.Nil(err) 24 | 25 | conf = mock_conf("random") 26 | participate, err = NewParticipates(conf) 27 | asserts.NotNil(err) 28 | asserts.Nil(participate) 29 | 30 | conf = mock_conf(common.DposPolicy) 31 | monkey.Patch(contracts.NewVotingContract, func() contracts.Voting { 32 | return &contracts.VotingContract{} 33 | }) 34 | participate, err = NewParticipates(conf) 35 | asserts.Nil(err) 36 | asserts.NotNil(participate) 37 | asserts.Equal(common.DposPolicy, participate.PolicyName()) 38 | } 39 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /participates/policy/solo/solo.go: -------------------------------------------------------------------------------- 1 | package solo 2 | 3 | import ( 4 | "github.com/DSiSc/craft/types" 5 | "github.com/DSiSc/galaxy/participates/common" 6 | "github.com/DSiSc/validator/tools/account" 7 | ) 8 | 9 | type SoloPolicy struct { 10 | name string 11 | // number of delegates, update when called GetParticipates 12 | members uint64 13 | participates []account.Account 14 | } 15 | 16 | func NewSoloPolicy() *SoloPolicy { 17 | return &SoloPolicy{name: common.SoloPolicy} 18 | } 19 | 20 | func (instance *SoloPolicy) PolicyName() string { 21 | return instance.name 22 | } 23 | 24 | func (instance *SoloPolicy) GetParticipates() ([]account.Account, error) { 25 | instance.members = uint64(1) 26 | participates := make([]account.Account, 0, 1) 27 | participate := account.Account{ 28 | Address: types.Address{ 29 | 0x33, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 30 | 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d, 31 | }, 32 | Extension: account.AccountExtension{ 33 | Id: 0, 34 | Url: "127.0.0.1:8080", 35 | }, 36 | } 37 | instance.participates = append(participates, participate) 38 | return instance.participates, nil 39 | } 40 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell 2 | 3 | Please consider promoting this project if you find it useful. 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 20 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 21 | OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 22 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /consensus/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/DSiSc/validator/tools/account" 5 | ) 6 | 7 | func Abs(x uint64, y uint64) int64 { 8 | n := int64(x - y) 9 | z := n >> 63 10 | return (n ^ z) - z 11 | } 12 | 13 | func AccountFilter(blacklist []account.Account, accounts []account.Account) []account.Account { 14 | var peer []account.Account 15 | for _, black := range blacklist { 16 | peer = filterAccount(black, accounts) 17 | } 18 | return peer 19 | } 20 | 21 | func filterAccount(black account.Account, accounts []account.Account) []account.Account { 22 | all := make([]account.Account, 0) 23 | for _, account := range accounts { 24 | if black != account { 25 | all = append(all, account) 26 | } 27 | } 28 | return all 29 | } 30 | 31 | func GetAccountById(peers []account.Account, expect uint64) account.Account { 32 | var temp account.Account 33 | for _, peer := range peers { 34 | if peer.Extension.Id == expect { 35 | temp = peer 36 | } 37 | } 38 | return temp 39 | } 40 | 41 | func GetAccountWithMinId(accounts []account.Account) account.Account { 42 | var minNode = accounts[0] 43 | for _, node := range accounts { 44 | if node.Extension.Id < minNode.Extension.Id { 45 | minNode = node 46 | } 47 | } 48 | return minNode 49 | } 50 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /vendor/vendor.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "", 3 | "ignore": "test", 4 | "package": [ 5 | { 6 | "checksumSHA1": "mY/P/6FmbcxXJEly440xmmuv6EA=", 7 | "path": "github.com/DSiSc/producer/config", 8 | "revision": "0185acda058eb1b4bddc18b75a7d6d9170f6b983", 9 | "revisionTime": "2018-08-20T11:03:59Z" 10 | }, 11 | { 12 | "checksumSHA1": "nqqySbxJ7SF9vVHwKfT33nempS8=", 13 | "path": "github.com/DSiSc/txpool/common/log", 14 | "revision": "89aa97de67a8982cac8881684e92d893dfed9fa1", 15 | "revisionTime": "2018-08-20T09:17:10Z" 16 | }, 17 | { 18 | "checksumSHA1": "CSPbwbyzqA6sfORicn4HFtIhF/c=", 19 | "path": "github.com/davecgh/go-spew/spew", 20 | "revision": "8991bc29aa16c548c550c7ff78260e27b9ab7c73", 21 | "revisionTime": "2018-02-21T22:46:20Z" 22 | }, 23 | { 24 | "checksumSHA1": "LuFv4/jlrmFNnDb/5SCSEPAM9vU=", 25 | "path": "github.com/pmezard/go-difflib/difflib", 26 | "revision": "792786c7400a136282c1664665ae0a8db921c6c2", 27 | "revisionTime": "2016-01-10T10:55:54Z" 28 | }, 29 | { 30 | "checksumSHA1": "c6pbpF7eowwO59phRTpF8cQ80Z0=", 31 | "path": "github.com/stretchr/testify/assert", 32 | "revision": "f35b8ab0b5a2cef36673838d662e249dd9c94686", 33 | "revisionTime": "2018-05-06T18:05:49Z" 34 | } 35 | ], 36 | "rootPath": "github.com/DSiSc/galaxy" 37 | } 38 | -------------------------------------------------------------------------------- /consensus/policy/pbft/tools/timer_test.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func MockManager() Manager { 10 | manager := NewManagerImpl() 11 | return manager 12 | } 13 | 14 | func TestNewTimerImpl(t *testing.T) { 15 | asserts := assert.New(t) 16 | mr := MockManager() 17 | timer := NewTimerImpl(mr) 18 | temp := timer.(*timerImpl) 19 | asserts.NotNil(temp.manager) 20 | asserts.Equal(mr, temp.manager) 21 | } 22 | 23 | func TestTimerImpl_Halt(t *testing.T) { 24 | asserts := assert.New(t) 25 | mr := MockManager() 26 | timer := NewTimerImpl(mr) 27 | temp := timer.(*timerImpl) 28 | temp.Halt() 29 | // if the channel is closed, isClose is false 30 | _, isClose := <-temp.threaded.exit 31 | asserts.Equal(false, isClose) 32 | } 33 | 34 | func TestTimerImpl_Reset(t *testing.T) { 35 | mr := MockManager() 36 | timer := NewTimerImpl(mr) 37 | temp := timer.(*timerImpl) 38 | temp.Reset(time.Duration(2), func() {}) 39 | } 40 | 41 | func TestTimerImpl_SoftReset(t *testing.T) { 42 | mr := MockManager() 43 | timer := NewTimerImpl(mr) 44 | temp := timer.(*timerImpl) 45 | temp.SoftReset(time.Duration(2), func() {}) 46 | } 47 | 48 | func TestTimerImpl_Stop(t *testing.T) { 49 | mr := MockManager() 50 | timer := NewTimerImpl(mr) 51 | time.Sleep(1 * time.Second) 52 | temp := timer.(*timerImpl) 53 | temp.Reset(time.Duration(1), func() {}) 54 | time.Sleep(2 * time.Second) 55 | temp.Stop() 56 | } 57 | -------------------------------------------------------------------------------- /vendor/github.com/pmezard/go-difflib/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Patrick Mezard 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | The names of its contributors may not be used to endorse or promote 14 | products derived from this software without specific prior written 15 | permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 18 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 20 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 23 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/doc.go: -------------------------------------------------------------------------------- 1 | // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. 2 | // 3 | // Example Usage 4 | // 5 | // The following is a complete example using assert in a standard test function: 6 | // import ( 7 | // "testing" 8 | // "github.com/stretchr/testify/assert" 9 | // ) 10 | // 11 | // func TestSomething(t *testing.T) { 12 | // 13 | // var a string = "Hello" 14 | // var b string = "Hello" 15 | // 16 | // assert.Equal(t, a, b, "The two words should be the same.") 17 | // 18 | // } 19 | // 20 | // if you assert many times, use the format below: 21 | // 22 | // import ( 23 | // "testing" 24 | // "github.com/stretchr/testify/assert" 25 | // ) 26 | // 27 | // func TestSomething(t *testing.T) { 28 | // assert := assert.New(t) 29 | // 30 | // var a string = "Hello" 31 | // var b string = "Hello" 32 | // 33 | // assert.Equal(a, b, "The two words should be the same.") 34 | // } 35 | // 36 | // Assertions 37 | // 38 | // Assertions allow you to easily write test code, and are global funcs in the `assert` package. 39 | // All assertion functions take, as the first argument, the `*testing.T` object provided by the 40 | // testing framework. This allows the assertion funcs to write the failings and other details to 41 | // the correct place. 42 | // 43 | // Every assertion function also takes an optional string message as the final argument, 44 | // allowing custom error messages to be appended to the message the assertion method outputs. 45 | package assert 46 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /consensus/consensus.go: -------------------------------------------------------------------------------- 1 | package consensus 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/log" 6 | "github.com/DSiSc/craft/types" 7 | "github.com/DSiSc/galaxy/consensus/common" 8 | "github.com/DSiSc/galaxy/consensus/config" 9 | "github.com/DSiSc/galaxy/consensus/policy/bft" 10 | "github.com/DSiSc/galaxy/consensus/policy/dbft" 11 | "github.com/DSiSc/galaxy/consensus/policy/fbft" 12 | "github.com/DSiSc/galaxy/consensus/policy/solo" 13 | "github.com/DSiSc/validator/tools/account" 14 | ) 15 | 16 | type Consensus interface { 17 | PolicyName() string 18 | Initialization(account.Account, account.Account, []account.Account, types.EventCenter, bool) 19 | ToConsensus(p *common.Proposal) error 20 | GetConsensusResult() common.ConsensusResult 21 | Online() 22 | Start() 23 | Halt() 24 | } 25 | 26 | func NewConsensus(conf config.ConsensusConfig, blockSwitch chan<- interface{}) (Consensus, error) { 27 | var err error 28 | var consensus Consensus 29 | switch conf.PolicyName { 30 | case common.SoloPolicy: 31 | log.Info("Get consensus policy is solo.") 32 | consensus, err = solo.NewSoloPolicy(blockSwitch, conf.EnableEmptyBlock, conf.SignVerifySwitch) 33 | case common.BftPolicy: 34 | log.Info("Get consensus policy is bft.") 35 | consensus, err = bft.NewBFTPolicy(conf.Timeout) 36 | case common.FbftPolicy: 37 | log.Info("Get consensus policy is fbft.") 38 | consensus, err = fbft.NewFBFTPolicy(conf.Timeout, blockSwitch, conf.EnableEmptyBlock, conf.SignVerifySwitch) 39 | case common.DbftPolicy: 40 | log.Info("Get consensus policy is dbft.") 41 | consensus, err = dbft.NewDBFTPolicy(conf.Timeout) 42 | default: 43 | err = fmt.Errorf("unsupport consensus type %v", conf.PolicyName) 44 | } 45 | return consensus, err 46 | } 47 | -------------------------------------------------------------------------------- /consensus/utils/payloads.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/DSiSc/craft/log" 5 | "github.com/DSiSc/craft/types" 6 | "github.com/DSiSc/repository" 7 | "github.com/DSiSc/validator/tools/account" 8 | "github.com/DSiSc/validator/tools/signature" 9 | "github.com/DSiSc/validator/worker" 10 | ) 11 | 12 | func VerifyPayload(payload *types.Block, enableSignVerify bool) (types.Receipts, error) { 13 | blockStore, err := repository.NewRepositoryByBlockHash(payload.Header.PrevBlockHash) 14 | if nil != err { 15 | log.Error("Get NewRepositoryByBlockHash failed.") 16 | return nil, err 17 | } 18 | return VerifyPayloadUseExistedRepository(blockStore, payload, enableSignVerify) 19 | } 20 | 21 | func VerifyPayloadUseExistedRepository(bc *repository.Repository, payload *types.Block, enableSignVerify bool) (types.Receipts, error) { 22 | worker := worker.NewWorker(bc, payload, enableSignVerify) 23 | err := worker.VerifyBlock() 24 | if err != nil { 25 | log.Error("The block %d verified failed with err %v.", payload.Header.Height, err) 26 | return nil, err 27 | } 28 | 29 | return worker.GetReceipts(), nil 30 | } 31 | 32 | func SignPayload(account account.Account, digest types.Hash) ([]byte, error) { 33 | sign, err := signature.Sign(&account, digest[:]) 34 | if nil != err { 35 | log.Error("archive signature occur error %v.", err) 36 | return nil, err 37 | } 38 | log.Debug("archive signature for %x successfully with sign %x.", digest, sign) 39 | return sign, nil 40 | } 41 | 42 | func SignatureVerify(account account.Account, sign []byte, digest types.Hash) bool { 43 | address, err := signature.Verify(digest, sign) 44 | if nil != err { 45 | log.Error("verify sign %v failed with err %s which expect from %x", sign, err, account.Address) 46 | } 47 | return account.Address == address 48 | } 49 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/bypasssafe.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 Dave Collins 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | // NOTE: Due to the following build constraints, this file will only be compiled 16 | // when the code is running on Google App Engine, compiled by GopherJS, or 17 | // "-tags safe" is added to the go build command line. The "disableunsafe" 18 | // tag is deprecated and thus should not be used. 19 | // +build js appengine safe disableunsafe !go1.4 20 | 21 | package spew 22 | 23 | import "reflect" 24 | 25 | const ( 26 | // UnsafeDisabled is a build-time constant which specifies whether or 27 | // not access to the unsafe package is available. 28 | UnsafeDisabled = true 29 | ) 30 | 31 | // unsafeReflectValue typically converts the passed reflect.Value into a one 32 | // that bypasses the typical safety restrictions preventing access to 33 | // unaddressable and unexported data. However, doing this relies on access to 34 | // the unsafe package. This is a stub version which simply returns the passed 35 | // reflect.Value when the unsafe package is not available. 36 | func unsafeReflectValue(v reflect.Value) reflect.Value { 37 | return v 38 | } 39 | -------------------------------------------------------------------------------- /consensus/consensus_test.go: -------------------------------------------------------------------------------- 1 | package consensus 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/types" 6 | "github.com/DSiSc/galaxy/consensus/common" 7 | "github.com/DSiSc/galaxy/consensus/config" 8 | "github.com/DSiSc/validator/tools/account" 9 | "github.com/stretchr/testify/assert" 10 | "reflect" 11 | "testing" 12 | ) 13 | 14 | func mockConf(policy string) config.ConsensusConfig { 15 | return config.ConsensusConfig{ 16 | PolicyName: policy, 17 | } 18 | } 19 | 20 | var mockAccount = account.Account{ 21 | Address: types.Address{0x33, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 22 | 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 23 | Extension: account.AccountExtension{ 24 | Id: 0, 25 | Url: "172.0.0.1:8080", 26 | }, 27 | } 28 | 29 | func Test_NewConsensus(t *testing.T) { 30 | asserts := assert.New(t) 31 | conf := mockConf("solo") 32 | consensus, err := NewConsensus(conf, nil) 33 | asserts.Nil(err) 34 | asserts.NotNil(consensus) 35 | asserts.Equal("solo", consensus.PolicyName()) 36 | 37 | p := reflect.TypeOf(consensus) 38 | method, exist := p.MethodByName("PolicyName") 39 | asserts.NotNil(method) 40 | asserts.True(exist) 41 | 42 | method, exist = p.MethodByName("ToConsensus") 43 | asserts.NotNil(method) 44 | asserts.True(exist) 45 | 46 | conf = mockConf(common.BftPolicy) 47 | consensus, err = NewConsensus(conf, nil) 48 | asserts.Equal(common.BftPolicy, consensus.PolicyName()) 49 | 50 | conf = mockConf(common.FbftPolicy) 51 | consensus, err = NewConsensus(conf, nil) 52 | asserts.Equal(common.FbftPolicy, consensus.PolicyName()) 53 | 54 | conf = mockConf(common.DbftPolicy) 55 | consensus, err = NewConsensus(conf, nil) 56 | asserts.Equal(common.DbftPolicy, consensus.PolicyName()) 57 | 58 | policyName := "Nil" 59 | conf = mockConf(policyName) 60 | consensus, err = NewConsensus(conf, nil) 61 | asserts.Equal(err, fmt.Errorf("unsupport consensus type %v", policyName)) 62 | } 63 | -------------------------------------------------------------------------------- /consensus/utils/utils_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/validator/tools/account" 6 | "github.com/stretchr/testify/assert" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func TestAccountFilter(t *testing.T) { 12 | var exist = false 13 | for _, account := range mockAccounts { 14 | if account == mockAccounts[1] { 15 | exist = true 16 | break 17 | } 18 | } 19 | assert.Equal(t, true, exist) 20 | 21 | var blackList = make([]account.Account, 0) 22 | blackList = append(blackList, mockAccounts[1]) 23 | peers := AccountFilter([]account.Account{mockAccounts[1]}, mockAccounts) 24 | assert.Equal(t, len(mockAccounts)-1, len(peers)) 25 | exist = false 26 | for _, account := range peers { 27 | if account == mockAccounts[1] { 28 | exist = true 29 | break 30 | } 31 | } 32 | assert.Equal(t, false, exist) 33 | } 34 | 35 | func TestGetAccountById(t *testing.T) { 36 | account := GetAccountById(mockAccounts, mockAccounts[1].Extension.Id) 37 | assert.Equal(t, account, mockAccounts[1]) 38 | } 39 | 40 | func TestAbs(t *testing.T) { 41 | var x = uint64(2) 42 | var y = uint64(4) 43 | z := int64(x - y) 44 | assert.Equal(t, int64(-2), z) 45 | z = Abs(x, y) 46 | assert.Equal(t, int64(2), z) 47 | } 48 | 49 | func TestGetAccountWithMinId(t *testing.T) { 50 | var nodes []account.Account 51 | nodes = mockAccounts 52 | minNode := GetAccountWithMinId(nodes) 53 | assert.Equal(t, mockAccounts[0], minNode) 54 | timer := time.NewTimer(10 * time.Second) 55 | _, _, timeSecond := time.Now().Clock() 56 | fmt.Printf("receive timer and time %v.\n", timeSecond) 57 | timer.Reset(5 * time.Second) 58 | index := 0 59 | for { 60 | select { 61 | case <-timer.C: 62 | _, _, timeSecond := time.Now().Clock() 63 | fmt.Printf("receive timer and time %v.\n", timeSecond) 64 | timer.Reset(3 * time.Second) 65 | index = index + 1 66 | if index == 3 { 67 | timer.Stop() 68 | return 69 | } 70 | default: 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /galaxy_test.go: -------------------------------------------------------------------------------- 1 | package galaxy 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/galaxy/common" 6 | "github.com/DSiSc/galaxy/consensus" 7 | consensusConfig "github.com/DSiSc/galaxy/consensus/config" 8 | "github.com/DSiSc/galaxy/participates" 9 | "github.com/DSiSc/galaxy/participates/config" 10 | "github.com/DSiSc/galaxy/role" 11 | roleConfig "github.com/DSiSc/galaxy/role/config" 12 | "github.com/DSiSc/monkey" 13 | "github.com/stretchr/testify/assert" 14 | "testing" 15 | ) 16 | 17 | func TestNewGalaxyPlugin(t *testing.T) { 18 | monkey.Patch(participates.NewParticipates, func(config.ParticipateConfig) (participates.Participates, error) { 19 | return nil, fmt.Errorf("error of participate") 20 | }) 21 | conf := common.GalaxyPluginConf{ 22 | BlockSwitch: make(chan<- interface{}), 23 | } 24 | plugin, err := NewGalaxyPlugin(conf) 25 | assert.Nil(t, plugin) 26 | assert.Equal(t, err, fmt.Errorf("participates init failed")) 27 | 28 | monkey.Patch(participates.NewParticipates, func(config.ParticipateConfig) (participates.Participates, error) { 29 | return nil, nil 30 | }) 31 | 32 | monkey.Patch(role.NewRole, func(roleConfig.RoleConfig) (role.Role, error) { 33 | return nil, fmt.Errorf("role init failed") 34 | }) 35 | plugin, err = NewGalaxyPlugin(conf) 36 | assert.Nil(t, plugin) 37 | assert.Equal(t, err, fmt.Errorf("role init failed")) 38 | 39 | monkey.Patch(role.NewRole, func(roleConfig.RoleConfig) (role.Role, error) { 40 | return nil, nil 41 | }) 42 | monkey.Patch(consensus.NewConsensus, func(consensusConfig.ConsensusConfig, chan<- interface{}) (consensus.Consensus, error) { 43 | return nil, fmt.Errorf("consensus init failed") 44 | }) 45 | plugin, err = NewGalaxyPlugin(conf) 46 | assert.Nil(t, plugin) 47 | assert.Equal(t, err, fmt.Errorf("consensus init failed")) 48 | 49 | monkey.Patch(consensus.NewConsensus, func(consensusConfig.ConsensusConfig, chan<- interface{}) (consensus.Consensus, error) { 50 | return nil, nil 51 | }) 52 | plugin, err = NewGalaxyPlugin(conf) 53 | assert.Nil(t, err) 54 | 55 | monkey.UnpatchAll() 56 | } 57 | -------------------------------------------------------------------------------- /role/policy/solo/solo.go: -------------------------------------------------------------------------------- 1 | package solo 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/log" 6 | "github.com/DSiSc/galaxy/role/common" 7 | "github.com/DSiSc/validator/tools/account" 8 | ) 9 | 10 | type SoloPolicy struct { 11 | name string 12 | assignments map[account.Account]common.Roler 13 | participates []account.Account 14 | } 15 | 16 | func NewSoloPolicy() (*SoloPolicy, error) { 17 | soloPolicy := &SoloPolicy{ 18 | name: common.SoloPolicy, 19 | } 20 | return soloPolicy, nil 21 | } 22 | 23 | func (instance *SoloPolicy) RoleAssignments(accounts []account.Account) (map[account.Account]common.Roler, account.Account, error) { 24 | participates := len(accounts) 25 | if 1 != participates { 26 | log.Error("solo role policy only support one participate.") 27 | return nil, account.Account{}, fmt.Errorf("more than one participate") 28 | } 29 | instance.participates = accounts 30 | instance.assignments = make(map[account.Account]common.Roler, participates) 31 | instance.assignments[instance.participates[0]] = common.Master 32 | return instance.assignments, instance.participates[0], nil 33 | } 34 | 35 | func (instance *SoloPolicy) GetRoles(address account.Account) (common.Roler, error) { 36 | if 0 == len(instance.assignments) { 37 | log.Error("RoleAssignments must be called before.") 38 | return common.UnKnown, common.AssignmentNotBeExecute 39 | } 40 | if role, ok := instance.assignments[address]; !ok { 41 | log.Error("wrong address which nobody knows in solo policy") 42 | return common.UnKnown, fmt.Errorf("wrong address") 43 | } else { 44 | return role, nil 45 | } 46 | } 47 | 48 | func (instance *SoloPolicy) PolicyName() string { 49 | return instance.name 50 | } 51 | 52 | func (instance *SoloPolicy) ChangeRoleAssignment(assignments map[account.Account]common.Roler, master uint64) { 53 | for account, _ := range assignments { 54 | if account.Extension.Id == master { 55 | assignments[account] = common.Master 56 | continue 57 | } 58 | assignments[account] = common.Slave 59 | } 60 | instance.assignments = assignments 61 | } 62 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /participates/policy/dpos/dpos.go: -------------------------------------------------------------------------------- 1 | package dpos 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/contractsManage/contracts" 6 | "github.com/DSiSc/craft/log" 7 | "github.com/DSiSc/galaxy/participates/common" 8 | "github.com/DSiSc/validator/tools/account" 9 | ) 10 | 11 | type DPOSPolicy struct { 12 | name string 13 | contract contracts.Voting 14 | // number of delegates, update when called GetParticipates 15 | members uint64 16 | participates []account.Account 17 | } 18 | 19 | func NewDPOSPolicy() *DPOSPolicy { 20 | voting := contracts.NewVotingContract() 21 | return &DPOSPolicy{ 22 | name: common.DposPolicy, 23 | contract: voting, 24 | } 25 | } 26 | 27 | func (instance *DPOSPolicy) PolicyName() string { 28 | return instance.name 29 | } 30 | 31 | // Get the top ranking of count from voting result. 32 | func (instance *DPOSPolicy) getDelegatesByCount(count uint64) ([]account.Account, error) { 33 | // TODO: Get accounts by voting result 34 | var accounts = make([]account.Account, 0) 35 | nodeList, err := instance.contract.GetNodeList(count) 36 | if nil != err { 37 | log.Error("get node list failed with %v", err) 38 | return accounts, fmt.Errorf("get node list failed with %v", err) 39 | } 40 | for _, node := range nodeList { 41 | account := account.Account{ 42 | Address: node.Address, 43 | Extension: account.AccountExtension{ 44 | Id: node.Id, 45 | Url: node.Url, 46 | }, 47 | } 48 | accounts = append(accounts, account) 49 | } 50 | instance.participates = accounts 51 | return instance.participates, nil 52 | } 53 | 54 | func (instance *DPOSPolicy) getDelegates() ([]account.Account, error) { 55 | instance.members = instance.contract.NodeNumber() 56 | delegates, err := instance.getDelegatesByCount(instance.members) 57 | if nil != err { 58 | log.Error("get delegates failed with err %s.", err) 59 | return nil, err 60 | } 61 | return delegates, nil 62 | } 63 | 64 | func (instance *DPOSPolicy) GetParticipates() ([]account.Account, error) { 65 | participates, err := instance.getDelegates() 66 | if nil != err { 67 | log.Error("Get delegates failed with error %v.", err) 68 | } else { 69 | instance.participates = participates 70 | } 71 | return participates, err 72 | } 73 | -------------------------------------------------------------------------------- /consensus/common/views_test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | func TestNewViewChange(t *testing.T) { 10 | viewChange := NewViewChange() 11 | assert.NotNil(t, viewChange) 12 | assert.Equal(t, DefaultViewNum, viewChange.currentView) 13 | assert.Equal(t, DefaultWalterLevel, viewChange.GetWalterLevel()) 14 | 15 | viewNum := viewChange.GetCurrentViewNum() 16 | assert.Equal(t, DefaultViewNum, viewNum) 17 | 18 | mockCurrentViewNum := uint64(10) 19 | viewChange.SetCurrentViewNum(mockCurrentViewNum) 20 | assert.Equal(t, mockCurrentViewNum, viewChange.GetCurrentViewNum()) 21 | } 22 | 23 | func TestNewRequests(t *testing.T) { 24 | mockToChange := uint8(2) 25 | request := NewRequests(mockToChange) 26 | assert.NotNil(t, request) 27 | assert.NotNil(t, Viewing, request.GetViewRequestState()) 28 | assert.NotNil(t, mockToChange, request.toChange) 29 | assert.NotNil(t, 0, len(request.nodes)) 30 | } 31 | 32 | func TestViewChange_AddViewRequest(t *testing.T) { 33 | viewChange := NewViewChange() 34 | mockViewNum := uint64(1) 35 | mockToChange := uint8(2) 36 | change, err := viewChange.AddViewRequest(mockViewNum, mockToChange) 37 | assert.Nil(t, err) 38 | assert.NotNil(t, change) 39 | 40 | change, err = viewChange.AddViewRequest(mockViewNum, mockToChange) 41 | assert.Nil(t, err) 42 | assert.NotNil(t, change) 43 | 44 | state := change.ReceiveViewRequestByAccount(mockAccounts[0]) 45 | assert.Equal(t, Viewing, state) 46 | 47 | state = change.ReceiveViewRequestByAccount(mockAccounts[1]) 48 | assert.Equal(t, ViewEnd, state) 49 | 50 | mockViewNum = uint64(2) 51 | change, err = viewChange.AddViewRequest(mockViewNum, mockToChange) 52 | assert.Nil(t, change) 53 | assert.NotNil(t, err) 54 | expect := fmt.Errorf("diff of current view %d and request view %d beyond walter level %d", 55 | DefaultViewNum, mockViewNum, DefaultWalterLevel) 56 | assert.Equal(t, expect, err) 57 | 58 | mockViewNum = uint64(1) 59 | viewRequest := viewChange.GetRequestByViewNum(mockViewNum) 60 | assert.NotNil(t, viewRequest) 61 | assert.Equal(t, ViewEnd, viewRequest.state) 62 | assert.Equal(t, 2, len(viewRequest.GetReceivedAccounts())) 63 | viewChange.RemoveRequest() 64 | viewRequest = viewChange.GetRequestByViewNum(mockViewNum) 65 | assert.Nil(t, viewRequest) 66 | } 67 | -------------------------------------------------------------------------------- /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/galaxy/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 | galaxy could always use more documentation, whether as part of the 32 | official galaxy 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/galaxy/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 `galaxy` for local development. 49 | 50 | 1. Fork the `galaxy` repo on GitHub. 51 | 2. Clone your fork locally:: 52 | 53 | $ git clone git@github.com:your_name_here/galaxy.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 | -------------------------------------------------------------------------------- /vendor/github.com/DSiSc/producer/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/DSiSc/txpool/common/log" 6 | "io/ioutil" 7 | "path/filepath" 8 | "runtime" 9 | "strings" 10 | ) 11 | 12 | var ConfigName = "config.json" 13 | var DefaultDataDir = "./config" 14 | 15 | type Config struct { 16 | filePath string 17 | maps map[string]interface{} 18 | } 19 | 20 | func New(path string) Config { 21 | return Config{filePath: path} 22 | } 23 | 24 | // Resturn absolute path of config.json 25 | func ConfigAbsPath() string { 26 | _, file, _, ok := runtime.Caller(1) 27 | if !ok { 28 | log.Error("Get config path failed.") 29 | return file 30 | } 31 | keyString := "/github.com/DSiSc/" 32 | index := strings.LastIndex(file, keyString) 33 | confAbsPath := strings.Join([]string{file[:index+len(keyString)], "producer/config/config.json"}, "") 34 | return confAbsPath 35 | } 36 | 37 | func DBAbsPath() string { 38 | _, file, _, ok := runtime.Caller(1) 39 | if !ok { 40 | log.Error("Get config path failed.") 41 | return file 42 | } 43 | keyString := "/github.com/DSiSc/producer/" 44 | index := strings.LastIndex(file, keyString) 45 | confAbsPath := strings.Join([]string{file[:index+len(keyString)], "config/data"}, "") 46 | return confAbsPath 47 | } 48 | 49 | // Read the given json file. 50 | func (config *Config) read() { 51 | if !filepath.IsAbs(config.filePath) { 52 | filePath, err := filepath.Abs(config.filePath) 53 | if err != nil { 54 | panic(err) 55 | } 56 | config.filePath = filePath 57 | } 58 | 59 | bts, err := ioutil.ReadFile(config.filePath) 60 | 61 | if err != nil { 62 | panic(err) 63 | } 64 | 65 | err = json.Unmarshal(bts, &config.maps) 66 | 67 | if err != nil { 68 | panic(err) 69 | } 70 | } 71 | 72 | // If we want to get item in a stucture, which like this: 73 | //{ 74 | // "classs": { 75 | // "student":{ 76 | // "name": "john" 77 | // } 78 | // } 79 | //} 80 | // { class: {}} 81 | // You can get it by call Get("class.student.name") 82 | func (config *Config) GetConfigItem(name string) interface{} { 83 | if config.maps == nil { 84 | config.read() 85 | } 86 | 87 | if config.maps == nil { 88 | return nil 89 | } 90 | 91 | keys := strings.Split(name, ".") 92 | length := len(keys) 93 | if length == 1 { 94 | return config.maps[name] 95 | } 96 | 97 | var ret interface{} 98 | for i := 0; i < length; i++ { 99 | if i == 0 { 100 | ret = config.maps[keys[i]] 101 | if ret == nil { 102 | return nil 103 | } 104 | } else { 105 | if m, ok := ret.(map[string]interface{}); ok { 106 | ret = m[keys[i]] 107 | } else { 108 | if length == i-1 { 109 | return ret 110 | } 111 | return nil 112 | } 113 | } 114 | } 115 | return ret 116 | } 117 | -------------------------------------------------------------------------------- /role/role_test.go: -------------------------------------------------------------------------------- 1 | package role 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/types" 6 | "github.com/DSiSc/galaxy/role/common" 7 | "github.com/DSiSc/galaxy/role/config" 8 | "github.com/DSiSc/validator/tools/account" 9 | "github.com/stretchr/testify/assert" 10 | "reflect" 11 | "testing" 12 | ) 13 | 14 | var mockAccounts = []account.Account{ 15 | account.Account{ 16 | Address: types.Address{0x33, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 17 | 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 18 | Extension: account.AccountExtension{ 19 | Id: 0, 20 | Url: "172.0.0.1:8080", 21 | }, 22 | }, 23 | account.Account{ 24 | Address: types.Address{0x34, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 25 | 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 26 | Extension: account.AccountExtension{ 27 | Id: 1, 28 | Url: "172.0.0.1:8081"}, 29 | }, 30 | } 31 | 32 | func mock_solo_conf() config.RoleConfig { 33 | return config.RoleConfig{ 34 | PolicyName: "solo", 35 | } 36 | } 37 | 38 | func mock_dpos_conf() config.RoleConfig { 39 | return config.RoleConfig{ 40 | PolicyName: "dpos", 41 | } 42 | } 43 | 44 | func Test_NewRole(t *testing.T) { 45 | asserts := assert.New(t) 46 | conf := mock_solo_conf() 47 | role, err := NewRole(conf) 48 | asserts.Nil(err) 49 | asserts.NotNil(role) 50 | asserts.Equal(common.SoloPolicy, role.PolicyName()) 51 | 52 | p := reflect.TypeOf(role) 53 | method, exist := p.MethodByName("PolicyName") 54 | asserts.NotNil(method) 55 | asserts.True(exist) 56 | 57 | method, exist = p.MethodByName("RoleAssignments") 58 | asserts.NotNil(method) 59 | asserts.True(exist) 60 | 61 | method, exist = p.MethodByName("GetRoles") 62 | asserts.NotNil(method) 63 | asserts.True(exist) 64 | 65 | role, err = NewRole(mock_dpos_conf()) 66 | asserts.Nil(err) 67 | asserts.NotNil(role) 68 | asserts.Equal(common.DposPolicy, role.PolicyName()) 69 | 70 | fakeConf := config.RoleConfig{ 71 | PolicyName: "unknown", 72 | } 73 | role, err = NewRole(fakeConf) 74 | asserts.NotNil(err) 75 | asserts.Equal(fmt.Errorf("unkonwn policy type"), err) 76 | asserts.Nil(role) 77 | } 78 | 79 | func changeAssign(assignments map[account.Account]common.Roler, master uint64) { 80 | for account, _ := range assignments { 81 | if account.Extension.Id == master { 82 | assignments[account] = common.Master 83 | continue 84 | } 85 | assignments[account] = common.Slave 86 | } 87 | } 88 | 89 | func TestNewRole(t *testing.T) { 90 | var assignment map[account.Account]common.Roler 91 | assignment = make(map[account.Account]common.Roler) 92 | assignment[mockAccounts[0]] = common.Master 93 | assignment[mockAccounts[1]] = common.Slave 94 | 95 | changeAssign(assignment, 1) 96 | assert.Equal(t, common.Slave, assignment[mockAccounts[0]]) 97 | assert.Equal(t, common.Master, assignment[mockAccounts[1]]) 98 | } 99 | -------------------------------------------------------------------------------- /consensus/common/common.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "errors" 5 | "github.com/DSiSc/craft/config" 6 | "github.com/DSiSc/craft/rlp" 7 | "github.com/DSiSc/craft/types" 8 | "github.com/DSiSc/crypto-suite/crypto/sha3" 9 | "github.com/DSiSc/validator/tools/account" 10 | "hash" 11 | ) 12 | 13 | type Version uint64 14 | 15 | // Base proposal 16 | type Proposal struct { 17 | Block *types.Block 18 | Timestamp int64 19 | } 20 | 21 | // BFTRequest that with bft policy 22 | type Request struct { 23 | Id uint64 24 | Payload *Proposal 25 | Status ConsensusStatus 26 | } 27 | 28 | type ConsensusStatus uint8 29 | 30 | const ( 31 | Proposing ConsensusStatus = iota // Proposing --> 0 prepare to launch a proposal 32 | Propose // Propose --> 1 propose for a proposal 33 | Approve // Approve --> 2 response of participate which accept the proposal 34 | Reject // Reject --> 3 response of participate which reject the proposal 35 | Committed // Committed --> 4 proposal has been accepted by participates with consensus policy 36 | ) 37 | 38 | const ( 39 | SoloPolicy = "solo" 40 | SoloConsensusNum = uint8(1) 41 | BftPolicy = "bft" 42 | FbftPolicy = "fbft" 43 | DbftPolicy = "dbft" 44 | ) 45 | 46 | const ( 47 | MaxBufferLen = 1024 * 256 48 | DefaultViewNum = uint64(0) 49 | DefaultWalterLevel = int64(1) 50 | DefaultBlockHeight = uint64(0) 51 | ) 52 | 53 | var ( 54 | ErrorsNewRepositoryByBlockHash = errors.New("get block chain by hash failed") 55 | ) 56 | 57 | type ViewStatus string 58 | 59 | const ViewChanging ViewStatus = "ViewChanging" 60 | const ViewNormal ViewStatus = "ViewNormal" 61 | 62 | type ViewRequestState string 63 | 64 | const Viewing ViewRequestState = "Viewing" 65 | const ViewEnd ViewRequestState = "ViewEnd" 66 | 67 | type OnlineState string 68 | 69 | const GoOnline OnlineState = "GoOnline" 70 | const Online OnlineState = "Online" 71 | 72 | type ConsensusResult struct { 73 | View uint64 74 | Participate []account.Account 75 | Master account.Account 76 | } 77 | 78 | type MessageSignal uint8 79 | 80 | const ( 81 | _ = MessageSignal(iota) 82 | ReceiveResponseSignal 83 | ) 84 | 85 | func HashAlg() hash.Hash { 86 | var alg string 87 | if value, ok := config.GlobalConfig.Load(config.HashAlgName); ok { 88 | alg = value.(string) 89 | } else { 90 | alg = "SHA256" 91 | } 92 | return sha3.NewHashByAlgName(alg) 93 | } 94 | 95 | func rlpHash(x interface{}) (h types.Hash) { 96 | hw := HashAlg() 97 | rlp.Encode(hw, x) 98 | hw.Sum(h[:0]) 99 | return h 100 | } 101 | 102 | func HeaderHash(block *types.Block) types.Hash { 103 | //var defaultHash types.Hash 104 | if !(block.HeaderHash == types.Hash{}) { 105 | var hash types.Hash 106 | copy(hash[:], block.HeaderHash[:]) 107 | return hash 108 | } 109 | return rlpHash(block.Header) 110 | } 111 | -------------------------------------------------------------------------------- /role/policy/solo/solo_test.go: -------------------------------------------------------------------------------- 1 | package solo 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/types" 6 | "github.com/DSiSc/galaxy/participates/config" 7 | "github.com/DSiSc/galaxy/role/common" 8 | "github.com/DSiSc/validator/tools/account" 9 | "github.com/stretchr/testify/assert" 10 | "testing" 11 | ) 12 | 13 | var mockAccounts = []account.Account{ 14 | account.Account{ 15 | Address: types.Address{0x33, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 16 | 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 17 | Extension: account.AccountExtension{ 18 | Id: 0, 19 | Url: "172.0.0.1:8080", 20 | }, 21 | }, 22 | account.Account{ 23 | Address: types.Address{0x34, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 24 | 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 25 | Extension: account.AccountExtension{ 26 | Id: 1, 27 | Url: "172.0.0.1:8081"}, 28 | }, 29 | } 30 | 31 | func Test_NewSoloPolicy(t *testing.T) { 32 | asserts := assert.New(t) 33 | policy, err := NewSoloPolicy() 34 | asserts.Nil(err) 35 | asserts.NotNil(policy) 36 | asserts.Equal(common.SoloPolicy, policy.name) 37 | asserts.Equal(0, len(policy.participates)) 38 | } 39 | 40 | func mock_conf(policy string) config.ParticipateConfig { 41 | return config.ParticipateConfig{ 42 | PolicyName: policy, 43 | } 44 | } 45 | 46 | func Test_RoleAssignments(t *testing.T) { 47 | asserts := assert.New(t) 48 | 49 | policy, err := NewSoloPolicy() 50 | asserts.Nil(err) 51 | asserts.NotNil(policy) 52 | 53 | roles, master, errs := policy.RoleAssignments(mockAccounts[:1]) 54 | asserts.Nil(errs) 55 | asserts.NotNil(roles) 56 | asserts.Equal(1, len(roles)) 57 | asserts.Equal(common.Master, roles[mockAccounts[0]]) 58 | asserts.Equal(mockAccounts[0], master) 59 | 60 | policy.participates = mockAccounts 61 | roles, _, errs = policy.RoleAssignments(mockAccounts) 62 | asserts.Nil(roles) 63 | asserts.Equal(fmt.Errorf("more than one participate"), errs) 64 | } 65 | 66 | func TestSoloPolicy_GetRoles(t *testing.T) { 67 | asserts := assert.New(t) 68 | 69 | policy, err := NewSoloPolicy() 70 | asserts.Nil(err) 71 | asserts.NotNil(policy) 72 | fmt.Println() 73 | roles, master, err := policy.RoleAssignments(mockAccounts[:1]) 74 | asserts.Nil(err) 75 | asserts.NotNil(roles) 76 | asserts.Equal(mockAccounts[0], master) 77 | 78 | role, err := policy.GetRoles(mockAccounts[0]) 79 | asserts.Nil(err) 80 | asserts.Equal(common.Master, role) 81 | 82 | policy.assignments = make(map[account.Account]common.Roler, 0) 83 | role, err = policy.GetRoles(mockAccounts[0]) 84 | asserts.Equal(common.AssignmentNotBeExecute, err) 85 | asserts.Equal(common.UnKnown, role) 86 | } 87 | 88 | func TestSoloPolicy_ChangeRoleAssignment(t *testing.T) { 89 | asserts := assert.New(t) 90 | 91 | policy, err := NewSoloPolicy() 92 | asserts.Nil(err) 93 | asserts.NotNil(policy) 94 | roles, master, err := policy.RoleAssignments(mockAccounts[:1]) 95 | asserts.Equal(common.Master, roles[mockAccounts[0]]) 96 | asserts.Equal(mockAccounts[0], master) 97 | 98 | asserts.Nil(err) 99 | asserts.NotNil(roles) 100 | policy.ChangeRoleAssignment(roles, uint64(1)) 101 | asserts.Equal(common.Slave, roles[mockAccounts[0]]) 102 | } 103 | -------------------------------------------------------------------------------- /role/policy/dpos/dpos.go: -------------------------------------------------------------------------------- 1 | package dpos 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/log" 6 | "github.com/DSiSc/galaxy/role/common" 7 | "github.com/DSiSc/repository" 8 | "github.com/DSiSc/validator/tools/account" 9 | ) 10 | 11 | type DPOSPolicy struct { 12 | name string 13 | participates []account.Account 14 | assignments map[account.Account]common.Roler 15 | } 16 | 17 | func NewDPOSPolicy() (*DPOSPolicy, error) { 18 | policy := &DPOSPolicy{ 19 | name: common.DposPolicy, 20 | } 21 | return policy, nil 22 | } 23 | 24 | func (instance *DPOSPolicy) RoleAssignments(participates []account.Account) (map[account.Account]common.Roler, account.Account, error) { 25 | block, ok := repository.NewLatestStateRepository() 26 | if nil != ok { 27 | log.Error("Get NewLatestStateRepository failed.") 28 | return nil, account.Account{}, fmt.Errorf("get NewLatestStateRepository failed") 29 | } 30 | instance.participates = participates 31 | delegates := len(instance.participates) 32 | instance.assignments = make(map[account.Account]common.Roler, delegates) 33 | currentBlockHeight := block.GetCurrentBlock().Header.Height 34 | masterId := (currentBlockHeight + 1) % uint64(delegates) 35 | // masterIndex := currentBlockHeight % uint64(delegates) 36 | var master account.Account 37 | for _, delegate := range instance.participates { 38 | if delegate.Extension.Id == masterId { 39 | instance.assignments[delegate] = common.Master 40 | master = delegate 41 | } else { 42 | instance.assignments[delegate] = common.Slave 43 | } 44 | } 45 | return instance.assignments, master, nil 46 | } 47 | 48 | func (instance *DPOSPolicy) GetRoles(account account.Account) (common.Roler, error) { 49 | if 0 == len(instance.assignments) { 50 | log.Error("role assignment has not been executed.") 51 | return common.UnKnown, common.AssignmentNotBeExecute 52 | } 53 | if role, ok := instance.assignments[account]; !ok { 54 | log.Error("account %x is not a delegate, please confirm.", account) 55 | // TODO: verify normal node or unknown 56 | return common.UnKnown, fmt.Errorf("accont not a delegate") 57 | } else { 58 | log.Info("account %x role is %v.", account, role) 59 | return role, nil 60 | } 61 | } 62 | 63 | func (instance *DPOSPolicy) PolicyName() string { 64 | return instance.name 65 | } 66 | 67 | func (instance *DPOSPolicy) AppointRole(master account.Account) error { 68 | if _, ok := instance.assignments[master]; !ok { 69 | log.Error("account %x has not assign role, please confirm.", master) 70 | return fmt.Errorf("appoint account is not a delegate") 71 | } 72 | var preMaster account.Account 73 | var exist bool = false 74 | for delegate, role := range instance.assignments { 75 | if common.Master == role { 76 | preMaster = delegate 77 | exist = true 78 | break 79 | } 80 | } 81 | if !exist { 82 | log.Error("no master in delegates, please confirm.") 83 | return fmt.Errorf("no master exist in current delegates") 84 | } 85 | instance.assignments[master] = common.Master 86 | instance.assignments[preMaster] = common.Slave 87 | return nil 88 | } 89 | 90 | func (instance *DPOSPolicy) ChangeRoleAssignment(assignments map[account.Account]common.Roler, master uint64) { 91 | for account, _ := range assignments { 92 | if account.Extension.Id == master { 93 | assignments[account] = common.Master 94 | continue 95 | } 96 | assignments[account] = common.Slave 97 | } 98 | instance.assignments = assignments 99 | } 100 | -------------------------------------------------------------------------------- /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/galaxy:' 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 galaxy ${VERSION}" 58 | @echo "GOPATH=${GOPATH}" 59 | go build -v -ldflags "-X github.com/DSiSc/galaxy/version.GitCommit=${GIT_COMMIT}${GIT_DIRTY} -X github.com/DSiSc/galaxy/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 | -------------------------------------------------------------------------------- /consensus/common/common_test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "github.com/DSiSc/craft/types" 5 | "github.com/DSiSc/validator/tools/account" 6 | "github.com/stretchr/testify/assert" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | var mockAccounts = []account.Account{ 12 | account.Account{ 13 | Address: types.Address{0x33, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 14 | 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 15 | Extension: account.AccountExtension{ 16 | Id: 0, 17 | Url: "172.0.0.1:8080", 18 | }, 19 | }, 20 | account.Account{ 21 | Address: types.Address{0x34, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 22 | 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 23 | Extension: account.AccountExtension{ 24 | Id: 1, 25 | Url: "172.0.0.1:8081"}, 26 | }, 27 | account.Account{ 28 | Address: types.Address{0x35, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 29 | Extension: account.AccountExtension{ 30 | Id: 2, 31 | Url: "172.0.0.1:8082", 32 | }, 33 | }, 34 | 35 | account.Account{ 36 | Address: types.Address{0x36, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 37 | Extension: account.AccountExtension{ 38 | Id: 3, 39 | Url: "172.0.0.1:8083", 40 | }, 41 | }, 42 | } 43 | 44 | var mockHash = types.Hash{ 45 | 0xbd, 0x79, 0x1d, 0x4a, 0xf9, 0x64, 0x8f, 0xc3, 0x7f, 0x94, 0xeb, 0x36, 0x53, 0x19, 0xf6, 0xd0, 46 | 0xa9, 0x78, 0x9f, 0x9c, 0x22, 0x47, 0x2c, 0xa7, 0xa6, 0x12, 0xa9, 0xca, 0x4, 0x13, 0xc1, 0x4, 47 | } 48 | 49 | var mockSignset = [][]byte{ 50 | {0x33, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 51 | {0x34, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 52 | {0x35, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 53 | {0x36, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 54 | {0x37, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 55 | } 56 | 57 | func Test_Role(t *testing.T) { 58 | asserts := assert.New(t) 59 | asserts.Equal(0, int(Proposing)) 60 | asserts.Equal(1, int(Propose)) 61 | asserts.Equal(2, int(Approve)) 62 | asserts.Equal(3, int(Reject)) 63 | asserts.Equal(4, int(Committed)) 64 | } 65 | 66 | var MockHash = types.Hash{ 67 | 0x1d, 0xcf, 0x7, 0xba, 0xfc, 0x42, 0xb0, 0x8d, 0xfd, 0x23, 0x9c, 0x45, 0xa4, 0xb9, 0x38, 0xd, 68 | 0x8d, 0xfe, 0x5d, 0x6f, 0xa7, 0xdb, 0xd5, 0x50, 0xc9, 0x25, 0xb1, 0xb3, 0x4, 0xdc, 0xc5, 0x1c, 69 | } 70 | 71 | var MockHeaderHash = types.Hash{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} 72 | 73 | func MockBlock() *types.Block { 74 | return &types.Block{ 75 | Header: &types.Header{ 76 | ChainID: 1, 77 | PrevBlockHash: MockHash, 78 | StateRoot: MockHash, 79 | TxRoot: MockHash, 80 | ReceiptsRoot: MockHash, 81 | Height: 1, 82 | Timestamp: uint64(time.Date(2018, time.August, 28, 0, 0, 0, 0, time.UTC).Unix()), 83 | }, 84 | Transactions: make([]*types.Transaction, 0), 85 | } 86 | } 87 | 88 | func TestBlockHash(t *testing.T) { 89 | block := MockBlock() 90 | headerHash := HeaderHash(block) 91 | assert.Equal(t, MockHeaderHash, headerHash) 92 | block.HeaderHash = HeaderHash(block) 93 | headerHash = HeaderHash(block) 94 | assert.Equal(t, MockHeaderHash, headerHash) 95 | } 96 | -------------------------------------------------------------------------------- /consensus/policy/bft/bft.go: -------------------------------------------------------------------------------- 1 | package bft 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/log" 6 | "github.com/DSiSc/craft/types" 7 | "github.com/DSiSc/galaxy/consensus/common" 8 | "github.com/DSiSc/galaxy/consensus/config" 9 | "github.com/DSiSc/galaxy/consensus/messages" 10 | "github.com/DSiSc/galaxy/consensus/utils" 11 | "github.com/DSiSc/validator/tools/account" 12 | "time" 13 | ) 14 | 15 | type BFTPolicy struct { 16 | name string 17 | // local account 18 | account account.Account 19 | bftCore *bftCore 20 | timeout time.Duration 21 | result chan *messages.ConsensusResult 22 | } 23 | 24 | func NewBFTPolicy(timeout config.ConsensusTimeout) (*BFTPolicy, error) { 25 | policy := &BFTPolicy{ 26 | name: common.BftPolicy, 27 | timeout: time.Duration(timeout.TimeoutToChangeView), 28 | result: make(chan *messages.ConsensusResult), 29 | } 30 | policy.bftCore = NewBFTCore(policy.result) 31 | return policy, nil 32 | } 33 | 34 | func (instance *BFTPolicy) Initialization(local account.Account, master account.Account, peers []account.Account, events types.EventCenter, onLine bool) { 35 | if onLine { 36 | log.Debug("online first time.") 37 | } 38 | instance.account = local 39 | instance.bftCore.local = local 40 | instance.bftCore.master = master 41 | instance.bftCore.commit = false 42 | instance.bftCore.peers = peers 43 | instance.bftCore.eventCenter = events 44 | instance.bftCore.tolerance = uint8((len(peers) - 1) / 3) 45 | instance.bftCore.signature = &signData{ 46 | signatures: make([][]byte, 0), 47 | signMap: make(map[account.Account][]byte), 48 | } 49 | return 50 | } 51 | 52 | func (instance *BFTPolicy) PolicyName() string { 53 | return instance.name 54 | } 55 | 56 | func (instance *BFTPolicy) Prepare(account account.Account) { 57 | instance.account = account 58 | } 59 | 60 | func (instance *BFTPolicy) Start() { 61 | log.Info("start bft policy service.") 62 | instance.bftCore.Start(instance.account) 63 | } 64 | 65 | func (instance *BFTPolicy) commit(block *types.Block, result bool) { 66 | commit := &messages.Commit{ 67 | Account: instance.account, 68 | Timestamp: time.Now().Unix(), 69 | Digest: block.Header.MixDigest, 70 | Signatures: block.Header.SigData, 71 | BlockHash: block.HeaderHash, 72 | Result: result, 73 | } 74 | instance.bftCore.SendCommit(commit, block) 75 | } 76 | 77 | func (instance *BFTPolicy) ToConsensus(p *common.Proposal) error { 78 | var err error 79 | var result = false 80 | request := &messages.Request{ 81 | Timestamp: p.Timestamp, 82 | Payload: p.Block, 83 | } 84 | timer := time.NewTimer(time.Second * instance.timeout) 85 | go utils.SendEvent(instance.bftCore, request) 86 | select { 87 | case consensusResult := <-instance.result: 88 | if nil != consensusResult.Result { 89 | log.Error("consensus for %x failed with error %v.", p.Block.Header.MixDigest, consensusResult.Result) 90 | err = consensusResult.Result 91 | } else { 92 | p.Block.Header.SigData = consensusResult.Signatures 93 | p.Block.HeaderHash = common.HeaderHash(p.Block) 94 | result = true 95 | log.Info("consensus for %x successfully with signature %x.", p.Block.Header.MixDigest, consensusResult.Signatures) 96 | } 97 | go instance.commit(p.Block, result) 98 | case <-timer.C: 99 | log.Error("consensus for %x timeout in %d seconds.", p.Block.Header.MixDigest, instance.timeout) 100 | err = fmt.Errorf("timeout for consensus") 101 | go instance.commit(p.Block, result) 102 | } 103 | return err 104 | } 105 | 106 | func (instance *BFTPolicy) Halt() { 107 | return 108 | } 109 | 110 | func (instance *BFTPolicy) GetConsensusResult() common.ConsensusResult { 111 | return common.ConsensusResult{ 112 | View: uint64(0), 113 | Participate: instance.bftCore.peers, 114 | Master: instance.bftCore.master, 115 | } 116 | } 117 | 118 | func (instance *BFTPolicy) Online() { 119 | return 120 | } 121 | -------------------------------------------------------------------------------- /consensus/policy/pbft/tools/events.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/log" 6 | ) 7 | 8 | // threaded holds an exit channel to allow threads to break from a select 9 | type threaded struct { 10 | exit chan struct{} 11 | } 12 | 13 | // Event is a type meant to clearly convey that the return type or parameter to 14 | // a function will be supplied to/from an events.Manager 15 | type Event interface{} 16 | 17 | // Receiver is a consumer of events, ProcessEvent will be called serially 18 | // as events arrive 19 | type Receiver interface { 20 | // ProcessEvent delivers an event to the Receiver, if it returns non-nil, the return is the next processed event 21 | ProcessEvent(e Event) Event 22 | } 23 | 24 | // Manager provides a serialized interface for submitting events to 25 | // a Receiver on the other side of the queue 26 | type Manager interface { 27 | Inject(Event) // A temporary interface to allow the event manager thread to skip the queue 28 | Queue() chan<- Event // Get a write-only reference to the queue, to submit events 29 | SetReceiver(Receiver) // Set the target to route events to 30 | Start() // Starts the Manager thread 31 | Halt() // Stops the Manager thread 32 | } 33 | 34 | // managerImpl is an implementation of Manger 35 | type managerImpl struct { 36 | threaded 37 | receiver Receiver 38 | events chan Event 39 | } 40 | 41 | // NewManagerImpl creates an instance of managerImpl 42 | func NewManagerImpl() Manager { 43 | return &managerImpl{ 44 | events: make(chan Event), 45 | threaded: threaded{make(chan struct{})}, 46 | } 47 | } 48 | 49 | // Inject can only safely be called by the managerImpl thread itself, it skips the queue 50 | func (em *managerImpl) Inject(event Event) { 51 | if em.receiver != nil { 52 | SendEvent(em.receiver, event) 53 | } 54 | } 55 | 56 | // SendEvent performs the event loop on a receiver to completion 57 | func SendEvent(receiver Receiver, event Event) { 58 | next := event 59 | for { 60 | // If an event returns something non-nil, then process it as a new event 61 | next = receiver.ProcessEvent(next) 62 | if next == nil { 63 | fmt.Println("Ending.") 64 | break 65 | } 66 | } 67 | } 68 | 69 | // queue returns a write only reference to the event queue 70 | func (em *managerImpl) Queue() chan<- Event { 71 | return em.events 72 | } 73 | 74 | // SetReceiver sets the destination for events 75 | func (em *managerImpl) SetReceiver(receiver Receiver) { 76 | em.receiver = receiver 77 | } 78 | 79 | // Start creates the go routine necessary to deliver events 80 | func (em *managerImpl) Start() { 81 | go em.eventLoop() 82 | } 83 | 84 | func (em *managerImpl) Halt() { 85 | select { 86 | case <-em.threaded.exit: 87 | log.Warn("Attempted to halt a threaded object twice") 88 | default: 89 | close(em.threaded.exit) 90 | } 91 | } 92 | 93 | // eventLoop is where the event thread loops, delivering events 94 | func (em *managerImpl) eventLoop() { 95 | for { 96 | select { 97 | case next := <-em.events: 98 | em.Inject(next) 99 | case <-em.exit: 100 | log.Debug("eventLoop told to exit") 101 | return 102 | } 103 | } 104 | } 105 | 106 | // TimerFactoryImpl implements the TimerFactory 107 | type timerFactoryImpl struct { 108 | manager Manager // The Manager to use in constructing the event timers 109 | } 110 | 111 | // NewTimerFactoryImpl creates a new TimerFactory for the given Manager 112 | func NewTimerFactoryImpl(manager Manager) TimerFactory { 113 | return &timerFactoryImpl{manager: manager} 114 | } 115 | 116 | // CreateTimer creates a new timer which deliver events to the Manager for this factory 117 | func (etf *timerFactoryImpl) CreateTimer() Timer { 118 | return newTimerImpl(etf.manager) 119 | } 120 | 121 | // newTimer creates a new instance of timerImpl 122 | func newTimerImpl(manager Manager) Timer { 123 | et := &timerImpl{ 124 | startChan: make(chan *timerStart), 125 | stopChan: make(chan struct{}), 126 | threaded: threaded{make(chan struct{})}, 127 | manager: manager, 128 | } 129 | go et.loop() 130 | return et 131 | } 132 | -------------------------------------------------------------------------------- /consensus/common/views.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/log" 6 | "github.com/DSiSc/galaxy/consensus/utils" 7 | "github.com/DSiSc/validator/tools/account" 8 | "sync" 9 | ) 10 | 11 | type ViewChange struct { 12 | lock sync.RWMutex 13 | currentView uint64 14 | walterLevel int64 15 | viewRequests map[uint64]*ViewRequests 16 | } 17 | 18 | type ViewRequests struct { 19 | lock sync.RWMutex 20 | toChange uint8 21 | state ViewRequestState 22 | index map[account.Account]bool 23 | nodes []account.Account 24 | } 25 | 26 | func NewRequests(toChange uint8) *ViewRequests { 27 | return &ViewRequests{ 28 | state: Viewing, 29 | toChange: toChange, 30 | index: make(map[account.Account]bool), 31 | nodes: make([]account.Account, 0), 32 | } 33 | } 34 | 35 | func NewViewChange() *ViewChange { 36 | return &ViewChange{ 37 | currentView: DefaultViewNum, 38 | walterLevel: DefaultWalterLevel, 39 | viewRequests: make(map[uint64]*ViewRequests), 40 | } 41 | } 42 | 43 | func (instance *ViewChange) GetCurrentViewNum() uint64 { 44 | instance.lock.RLock() 45 | defer instance.lock.RUnlock() 46 | return instance.currentView 47 | } 48 | 49 | func (instance *ViewChange) SetCurrentViewNum(newViewNum uint64) { 50 | instance.lock.RLock() 51 | instance.currentView = newViewNum 52 | instance.lock.RUnlock() 53 | } 54 | 55 | func (instance *ViewChange) GetWalterLevel() int64 { 56 | instance.lock.RLock() 57 | defer instance.lock.RUnlock() 58 | return instance.walterLevel 59 | } 60 | 61 | func (instance *ViewChange) AddViewRequest(viewNum uint64, toChange uint8) (*ViewRequests, error) { 62 | instance.lock.Lock() 63 | defer instance.lock.Unlock() 64 | if utils.Abs(instance.currentView, viewNum) > instance.walterLevel { 65 | log.Error("diff of current view %d and request %d beyond walter level %d.", 66 | instance.currentView, viewNum, instance.walterLevel) 67 | return nil, fmt.Errorf("diff of current view %d and request view %d beyond walter level %d", 68 | instance.currentView, viewNum, instance.walterLevel) 69 | } 70 | if _, ok := instance.viewRequests[viewNum]; !ok { 71 | log.Info("add view change number %d.", viewNum) 72 | instance.viewRequests[viewNum] = NewRequests(toChange) 73 | return instance.viewRequests[viewNum], nil 74 | } 75 | log.Warn("view sets for %d has exist and state is %v.", viewNum, instance.viewRequests[viewNum].state) 76 | return instance.viewRequests[viewNum], nil 77 | } 78 | 79 | func (instance *ViewChange) RemoveRequest() { 80 | instance.lock.Lock() 81 | defer instance.lock.Unlock() 82 | for key, val := range instance.viewRequests { 83 | if val.state == ViewEnd { 84 | log.Info("remove view change %d.", val.toChange) 85 | delete(instance.viewRequests, key) 86 | } 87 | } 88 | return 89 | } 90 | 91 | func (instance *ViewChange) GetRequestByViewNum(viewNum uint64) *ViewRequests { 92 | instance.lock.RLock() 93 | defer instance.lock.RUnlock() 94 | return instance.viewRequests[viewNum] 95 | } 96 | 97 | func (instance *ViewRequests) ReceiveViewRequestByAccount(account account.Account) ViewRequestState { 98 | instance.lock.Lock() 99 | defer instance.lock.Unlock() 100 | if _, ok := instance.index[account]; !ok { 101 | log.Info("add %x view request.", account.Address) 102 | instance.index[account] = true 103 | instance.nodes = append(instance.nodes, account) 104 | if uint8(len(instance.nodes)) >= instance.toChange { 105 | log.Info("request has reach to change view situation which need less than %d, now received is %d.", len(instance.nodes), instance.toChange) 106 | instance.state = ViewEnd 107 | } 108 | } else { 109 | log.Warn("has receive %x view request.", account.Address) 110 | } 111 | return instance.state 112 | } 113 | 114 | func (instance *ViewRequests) GetViewRequestState() ViewRequestState { 115 | instance.lock.RLock() 116 | defer instance.lock.RUnlock() 117 | return instance.state 118 | } 119 | 120 | func (instance *ViewRequests) GetReceivedAccounts() []account.Account { 121 | instance.lock.RLock() 122 | defer instance.lock.RUnlock() 123 | return instance.nodes 124 | } 125 | -------------------------------------------------------------------------------- /consensus/policy/pbft/tools/events_test.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | type mockEvent struct { 9 | info string 10 | } 11 | 12 | type mockReceiver struct { 13 | processEventImpl func(event Event) Event 14 | } 15 | 16 | func (mr *mockReceiver) ProcessEvent(event Event) Event { 17 | if mr.processEventImpl != nil { 18 | return mr.processEventImpl(event) 19 | } 20 | return nil 21 | } 22 | 23 | func newMockManager(processEvent func(event Event) Event) Manager { 24 | manager := NewManagerImpl() 25 | manager.SetReceiver(&mockReceiver{ 26 | processEventImpl: processEvent, 27 | }) 28 | return manager 29 | } 30 | 31 | // Starts an event timer, waits for the event to be delivered 32 | func TestEventTimerStart(t *testing.T) { 33 | events := make(chan Event) 34 | mr := newMockManager(func(event Event) Event { 35 | events <- event 36 | return nil 37 | }) 38 | mr.Start() 39 | defer mr.Halt() 40 | timer := newTimerImpl(mr) 41 | defer timer.Halt() 42 | me := &mockEvent{} 43 | timer.Reset(time.Millisecond, me) 44 | 45 | select { 46 | case e := <-events: 47 | if e != me { 48 | t.Fatalf("Received wrong output from event timer") 49 | } 50 | case <-time.After(time.Second): 51 | t.Fatalf("Timed out waiting for event to fire") 52 | } 53 | } 54 | 55 | // Starts an event timer, resets it twice, expects second output 56 | func TestEventTimerHardReset(t *testing.T) { 57 | events := make(chan Event) 58 | mr := newMockManager(func(event Event) Event { 59 | events <- event 60 | return nil 61 | }) 62 | timer := newTimerImpl(mr) 63 | defer timer.Halt() 64 | me1 := &mockEvent{"one"} 65 | me2 := &mockEvent{"two"} 66 | timer.Reset(time.Millisecond, me1) 67 | timer.Reset(time.Millisecond, me2) 68 | 69 | mr.Start() 70 | defer mr.Halt() 71 | 72 | select { 73 | case e := <-events: 74 | if e != me2 { 75 | t.Fatalf("Received wrong output (%v) from event timer", e) 76 | } 77 | case <-time.After(time.Second): 78 | t.Fatalf("Timed out waiting for event to fire") 79 | } 80 | } 81 | 82 | // Starts an event timer, soft resets it twice, expects first output 83 | func TestEventTimerSoftReset(t *testing.T) { 84 | events := make(chan Event) 85 | mr := newMockManager(func(event Event) Event { 86 | events <- event 87 | return nil 88 | }) 89 | timer := newTimerImpl(mr) 90 | defer timer.Halt() 91 | me1 := &mockEvent{"one"} 92 | me2 := &mockEvent{"two"} 93 | timer.SoftReset(time.Millisecond, me1) 94 | timer.SoftReset(time.Millisecond, me2) 95 | 96 | mr.Start() 97 | defer mr.Halt() 98 | 99 | select { 100 | case e := <-events: 101 | if e != me1 { 102 | t.Fatalf("Received wrong output (%v) from event timer", e) 103 | } 104 | case <-time.After(time.Second): 105 | t.Fatalf("Timed out waiting for event to fire") 106 | } 107 | } 108 | 109 | // Starts an event timer, then stops it before delivery is possible, should not receive event 110 | func TestEventTimerStop(t *testing.T) { 111 | events := make(chan Event) 112 | mr := newMockManager(func(event Event) Event { 113 | events <- event 114 | return nil 115 | }) 116 | timer := newTimerImpl(mr) 117 | defer timer.Halt() 118 | me := &mockEvent{} 119 | timer.Reset(time.Millisecond, me) 120 | time.Sleep(100 * time.Millisecond) // Allow the timer to fire 121 | timer.Stop() 122 | 123 | mr.Start() 124 | defer mr.Halt() 125 | 126 | select { 127 | case <-events: 128 | t.Fatalf("Received event output from event timer") 129 | case <-time.After(100 * time.Millisecond): 130 | // All good 131 | } 132 | } 133 | 134 | // Replies to an event with a different event, should process both 135 | func TestEventManagerLoop(t *testing.T) { 136 | success := make(chan struct{}) 137 | m2 := &mockEvent{} 138 | mr := newMockManager(func(event Event) Event { 139 | if event != m2 { 140 | return m2 141 | } 142 | success <- struct{}{} 143 | return nil 144 | }) 145 | mr.Start() 146 | defer mr.Halt() 147 | 148 | mr.Queue() <- &mockEvent{} 149 | 150 | select { 151 | case <-success: 152 | // All good 153 | case <-time.After(2 * time.Second): 154 | t.Fatalf("Did not succeed processing second event") 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /participates/policy/dpos/dpos_test.go: -------------------------------------------------------------------------------- 1 | package dpos 2 | 3 | import ( 4 | "github.com/DSiSc/contractsManage/contracts" 5 | "github.com/DSiSc/craft/types" 6 | "github.com/DSiSc/galaxy/participates/common" 7 | "github.com/DSiSc/monkey" 8 | "github.com/DSiSc/validator/tools/account" 9 | "github.com/stretchr/testify/assert" 10 | "reflect" 11 | "testing" 12 | ) 13 | 14 | var delegates uint64 = 4 15 | 16 | var accounts = []account.Account{ 17 | account.Account{ 18 | Address: types.Address{0x33, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 19 | Extension: account.AccountExtension{ 20 | Id: 0, 21 | Url: "192.168.176.145:8080", 22 | }, 23 | }, 24 | account.Account{ 25 | Address: types.Address{0x34, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 26 | Extension: account.AccountExtension{ 27 | Id: 1, 28 | Url: "192.168.176.146:8080", 29 | }, 30 | }, 31 | account.Account{ 32 | Address: types.Address{0x35, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 33 | Extension: account.AccountExtension{ 34 | Id: 2, 35 | Url: "192.168.176.147:8080", 36 | }, 37 | }, 38 | account.Account{ 39 | Address: types.Address{0x36, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 40 | Extension: account.AccountExtension{ 41 | Id: 3, 42 | Url: "192.168.176.148:8080", 43 | }, 44 | }, 45 | } 46 | 47 | var mockNodes = []contracts.NodeInfo{ 48 | contracts.NodeInfo{ 49 | Address: types.Address{0x35, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 50 | Id: 0, 51 | Url: "192.168.176.145:8080", 52 | }, 53 | contracts.NodeInfo{ 54 | Address: types.Address{0x36, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 55 | Id: 1, 56 | Url: "192.168.176.146:8080", 57 | }, 58 | contracts.NodeInfo{ 59 | Address: types.Address{0x37, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 60 | Id: 2, 61 | Url: "192.168.176.147:8080", 62 | }, 63 | contracts.NodeInfo{ 64 | Address: types.Address{0x38, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 65 | Id: 3, 66 | Url: "192.168.176.148:8080", 67 | }, 68 | } 69 | 70 | func TestDPOSPolicy_PolicyName(t *testing.T) { 71 | asserts := assert.New(t) 72 | monkey.Patch(contracts.NewVotingContract, func() contracts.Voting { 73 | return &contracts.VotingContract{} 74 | }) 75 | dpos := NewDPOSPolicy() 76 | asserts.NotNil(dpos) 77 | asserts.Equal(common.DposPolicy, dpos.PolicyName()) 78 | asserts.Equal(uint64(0), dpos.members) 79 | asserts.Equal(uint64(0), uint64(len(dpos.participates))) 80 | } 81 | 82 | func TestNewDPOSPolicy(t *testing.T) { 83 | asserts := assert.New(t) 84 | monkey.Patch(contracts.NewVotingContract, func() contracts.Voting { 85 | return &contracts.VotingContract{} 86 | }) 87 | dpos := NewDPOSPolicy() 88 | asserts.NotNil(dpos) 89 | asserts.Equal(dpos.name, common.DposPolicy) 90 | } 91 | 92 | func TestDPOSPolicy_GetParticipates(t *testing.T) { 93 | asserts := assert.New(t) 94 | var contract *contracts.VotingContract 95 | monkey.Patch(contracts.NewVotingContract, func() contracts.Voting { 96 | return contract 97 | }) 98 | monkey.PatchInstanceMethod(reflect.TypeOf(contract), "NodeNumber", func(*contracts.VotingContract) uint64 { 99 | return uint64(4) 100 | }) 101 | monkey.PatchInstanceMethod(reflect.TypeOf(contract), "GetNodeList", func(*contracts.VotingContract, uint64) ([]contracts.NodeInfo, error) { 102 | return mockNodes, nil 103 | }) 104 | dpos := NewDPOSPolicy() 105 | asserts.NotNil(dpos) 106 | participates, err := dpos.GetParticipates() 107 | asserts.Nil(err) 108 | asserts.Equal(delegates, dpos.members) 109 | asserts.Equal(dpos.members, uint64(len(dpos.participates))) 110 | asserts.Equal(participates, dpos.participates) 111 | } 112 | -------------------------------------------------------------------------------- /consensus/utils/payloads_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/types" 6 | "github.com/DSiSc/monkey" 7 | "github.com/DSiSc/repository" 8 | "github.com/DSiSc/validator/tools/account" 9 | "github.com/DSiSc/validator/tools/signature" 10 | "github.com/DSiSc/validator/worker" 11 | "github.com/stretchr/testify/assert" 12 | "reflect" 13 | "testing" 14 | ) 15 | 16 | var mockAccounts = []account.Account{ 17 | account.Account{ 18 | Address: types.Address{0x33, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 19 | 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 20 | Extension: account.AccountExtension{ 21 | Id: 0, 22 | Url: "172.0.0.1:8080", 23 | }, 24 | }, 25 | account.Account{ 26 | Address: types.Address{0x34, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 27 | 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 28 | Extension: account.AccountExtension{ 29 | Id: 1, 30 | Url: "172.0.0.1:8081"}, 31 | }, 32 | account.Account{ 33 | Address: types.Address{0x35, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 34 | Extension: account.AccountExtension{ 35 | Id: 2, 36 | Url: "172.0.0.1:8082", 37 | }, 38 | }, 39 | 40 | account.Account{ 41 | Address: types.Address{0x36, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 42 | Extension: account.AccountExtension{ 43 | Id: 3, 44 | Url: "172.0.0.1:8083", 45 | }, 46 | }, 47 | } 48 | 49 | var mockHash = types.Hash{ 50 | 0xbd, 0x79, 0x1d, 0x4a, 0xf9, 0x64, 0x8f, 0xc3, 0x7f, 0x94, 0xeb, 0x36, 0x53, 0x19, 0xf6, 0xd0, 51 | 0xa9, 0x78, 0x9f, 0x9c, 0x22, 0x47, 0x2c, 0xa7, 0xa6, 0x12, 0xa9, 0xca, 0x4, 0x13, 0xc1, 0x4, 52 | } 53 | 54 | var mockSignset = [][]byte{ 55 | {0x33, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 56 | {0x34, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 57 | {0x35, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 58 | {0x36, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 59 | {0x37, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 60 | } 61 | 62 | func TestVerifyPayload(t *testing.T) { 63 | block := &types.Block{ 64 | Header: &types.Header{ 65 | Height: 1, 66 | }, 67 | } 68 | receipt, err := VerifyPayload(block, false) 69 | assert.NotNil(t, err) 70 | assert.Nil(t, receipt) 71 | 72 | var b *repository.Repository 73 | monkey.Patch(repository.NewRepositoryByBlockHash, func(types.Hash) (*repository.Repository, error) { 74 | return b, nil 75 | }) 76 | var w *worker.Worker 77 | monkey.PatchInstanceMethod(reflect.TypeOf(w), "VerifyBlock", func(*worker.Worker) error { 78 | return fmt.Errorf("verify block failed") 79 | }) 80 | receipt, err = VerifyPayload(block, false) 81 | assert.Nil(t, receipt) 82 | assert.Equal(t, fmt.Errorf("verify block failed"), err) 83 | 84 | monkey.PatchInstanceMethod(reflect.TypeOf(w), "VerifyBlock", func(*worker.Worker) error { 85 | return nil 86 | }) 87 | _, err = VerifyPayload(block, false) 88 | assert.Nil(t, err) 89 | monkey.UnpatchAll() 90 | } 91 | 92 | var fakeSignature = []byte{ 93 | 0x33, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 94 | 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d, 95 | } 96 | 97 | func TestSignPayload(t *testing.T) { 98 | monkey.Patch(signature.Sign, func(signature.Signer, []byte) ([]byte, error) { 99 | return make([]byte, 0), fmt.Errorf("sign error") 100 | }) 101 | signatures, err := SignPayload(mockAccounts[0], mockHash) 102 | assert.Equal(t, 0, len(signatures)) 103 | assert.Equal(t, fmt.Errorf("sign error"), err) 104 | 105 | monkey.Patch(signature.Sign, func(signature.Signer, []byte) ([]byte, error) { 106 | return fakeSignature, nil 107 | }) 108 | signatures, err = SignPayload(mockAccounts[0], mockHash) 109 | assert.Nil(t, err) 110 | assert.Equal(t, fakeSignature, signatures) 111 | } 112 | -------------------------------------------------------------------------------- /consensus/policy/fbft/fbft.go: -------------------------------------------------------------------------------- 1 | package fbft 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/DSiSc/craft/log" 7 | "github.com/DSiSc/craft/types" 8 | "github.com/DSiSc/galaxy/consensus/common" 9 | "github.com/DSiSc/galaxy/consensus/config" 10 | "github.com/DSiSc/galaxy/consensus/messages" 11 | "github.com/DSiSc/galaxy/consensus/utils" 12 | "github.com/DSiSc/validator/tools/account" 13 | "math/rand" 14 | "sync/atomic" 15 | "time" 16 | ) 17 | 18 | type FBFTPolicy struct { 19 | local account.Account 20 | name string 21 | core *fbftCore 22 | // time to reach consensus 23 | timeout config.ConsensusTimeout 24 | isProcessing int32 25 | } 26 | 27 | func NewFBFTPolicy(timeout config.ConsensusTimeout, blockSwitch chan<- interface{}, emptyBlock bool, signVerify config.SignatureVerifySwitch) (*FBFTPolicy, error) { 28 | policy := &FBFTPolicy{ 29 | name: common.FbftPolicy, 30 | timeout: timeout, 31 | } 32 | policy.core = NewFBFTCore(blockSwitch, timeout, emptyBlock, signVerify) 33 | return policy, nil 34 | } 35 | 36 | func (instance *FBFTPolicy) Initialization(local account.Account, master account.Account, peers []account.Account, events types.EventCenter, onLine bool) { 37 | instance.local = local 38 | instance.core.nodes.Store(&nodesInfo{ 39 | local: local, 40 | master: master, 41 | peers: peers, 42 | }) 43 | instance.core.eventCenter = events 44 | instance.core.tolerance.Store(uint8((len(peers) - 1) / 3)) 45 | log.Debug("start timeout master with view num %d.", instance.core.viewChange.GetCurrentViewNum()) 46 | if !onLine && local.Address != master.Address { 47 | randDuration := rand.Int63n(5) * 1000 48 | go instance.core.waitMasterTimeout(time.Duration(instance.timeout.TimeoutToChangeView+randDuration) * time.Millisecond) 49 | } 50 | } 51 | 52 | func (instance *FBFTPolicy) PolicyName() string { 53 | return instance.name 54 | } 55 | 56 | func (instance *FBFTPolicy) Prepare(account account.Account) { 57 | instance.local = account 58 | 59 | originNodes := instance.core.nodes.Load().(*nodesInfo) 60 | newNodes := originNodes.clone() 61 | newNodes.local = account 62 | instance.core.nodes.Store(newNodes) 63 | } 64 | 65 | func (instance *FBFTPolicy) Start() { 66 | instance.core.Start() 67 | } 68 | 69 | func (instance *FBFTPolicy) ToConsensus(p *common.Proposal) error { 70 | if !atomic.CompareAndSwapInt32(&instance.isProcessing, 0, 1) { 71 | log.Warn("previous round have not finished") 72 | return errors.New("previous round have not finished") 73 | } 74 | defer atomic.StoreInt32(&instance.isProcessing, 0) 75 | var err error 76 | var result bool 77 | request := &messages.Request{ 78 | Account: instance.local, 79 | Timestamp: p.Timestamp, 80 | Payload: p.Block, 81 | } 82 | timeToCollectResponseMsg := time.NewTimer(time.Duration(instance.timeout.TimeoutToWaitCommitMsg) * time.Millisecond) 83 | utils.SendEvent(instance.core, request) 84 | select { 85 | case consensusResult := <-instance.core.result: 86 | if nil != consensusResult.Result { 87 | log.Error("consensus for %x failed with error %v.", p.Block.Header.MixDigest, consensusResult.Result) 88 | err = consensusResult.Result 89 | } else { 90 | p.Block.Header.SigData = consensusResult.Signatures 91 | p.Block.HeaderHash = common.HeaderHash(p.Block) 92 | result = true 93 | log.Info("consensus for %x successfully with signature %x.", p.Block.Header.MixDigest, consensusResult.Signatures) 94 | } 95 | timeToCollectResponseMsg.Stop() 96 | instance.core.tryToCommit(p.Block, result) 97 | return err 98 | case <-timeToCollectResponseMsg.C: 99 | log.Error("consensus for digest %x timeout in %d seconds.", p.Block.Header.MixDigest, instance.timeout.TimeoutToCollectResponseMsg) 100 | instance.core.tryToCommit(p.Block, false) 101 | return fmt.Errorf("timeout for consensus") 102 | } 103 | } 104 | 105 | func (instance *FBFTPolicy) Halt() { 106 | return 107 | } 108 | 109 | func (instance *FBFTPolicy) GetConsensusResult() common.ConsensusResult { 110 | nodes := instance.core.nodes.Load().(*nodesInfo) 111 | log.Debug("now local is %d.", nodes.local.Extension.Id) 112 | return common.ConsensusResult{ 113 | View: instance.core.viewChange.GetCurrentViewNum(), 114 | Participate: nodes.peers, 115 | Master: nodes.master, 116 | } 117 | } 118 | 119 | func (instance *FBFTPolicy) Online() { 120 | instance.core.sendOnlineRequest() 121 | } 122 | -------------------------------------------------------------------------------- /consensus/policy/pbft/tools/timer.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "github.com/DSiSc/craft/log" 5 | "time" 6 | ) 7 | 8 | // Timer is an interface for managing time driven events 9 | // the special contract Timer gives which a traditional golang 10 | // timer does not, is that if the event thread calls stop, or reset 11 | // then even if the timer has already fired, the event will not be 12 | // delivered to the event queue 13 | type Timer interface { 14 | SoftReset(duration time.Duration, event Event) // start a new countdown, only if one is not already started 15 | Reset(duration time.Duration, event Event) // start a new countdown, clear any pending events 16 | Stop() // stop the countdown, clear any pending events 17 | Halt() // Stops the Timer thread 18 | } 19 | 20 | // TimerFactory abstracts the creation of Timers, as they may 21 | // need to be mocked for testing 22 | type TimerFactory interface { 23 | CreateTimer() Timer // Creates an Timer which is stopped 24 | } 25 | 26 | // timerStart is used to deliver the start request to the eventTimer thread 27 | type timerStart struct { 28 | hard bool // Whether to reset the timer if it is running 29 | event Event // What event to push onto the event queue 30 | duration time.Duration // How long to wait before sending the event 31 | } 32 | 33 | // timerImpl is an implementation of Timer 34 | type timerImpl struct { 35 | threaded // Gives us the exit chan 36 | timerChan <-chan time.Time // When non-nil, counts down to preparing to do the event 37 | startChan chan *timerStart // Channel to deliver the timer start events to the service go routine 38 | stopChan chan struct{} // Channel to deliver the timer stop events to the service go routine 39 | manager Manager // The event manager to deliver the event to after timer expiration 40 | } 41 | 42 | // newTimer creates a new instance of timerImpl 43 | func NewTimerImpl(manager Manager) Timer { 44 | et := &timerImpl{ 45 | startChan: make(chan *timerStart), 46 | stopChan: make(chan struct{}), 47 | threaded: threaded{make(chan struct{})}, 48 | manager: manager, 49 | } 50 | go et.loop() 51 | return et 52 | } 53 | 54 | // loop is where the timer thread lives, looping 55 | func (et *timerImpl) loop() { 56 | var eventDestChan chan<- Event 57 | var event Event 58 | 59 | for { 60 | // A little state machine, relying on the fact that nil channels will block on read/write indefinitely 61 | select { 62 | case start := <-et.startChan: 63 | if et.timerChan != nil { 64 | if start.hard { 65 | log.Debug("Resetting a running timer") 66 | } else { 67 | continue 68 | } 69 | } 70 | log.Debug("Starting timer") 71 | et.timerChan = time.After(start.duration) 72 | if eventDestChan != nil { 73 | log.Debug("Timer cleared pending event") 74 | } 75 | event = start.event 76 | eventDestChan = nil 77 | case <-et.stopChan: 78 | if et.timerChan == nil && eventDestChan == nil { 79 | log.Debug("Attempting to stop an unfired idle timer") 80 | } 81 | et.timerChan = nil 82 | log.Debug("Stopping timer") 83 | if eventDestChan != nil { 84 | log.Debug("Timer cleared pending event") 85 | } 86 | eventDestChan = nil 87 | event = nil 88 | case <-et.timerChan: 89 | log.Debug("Event timer fired") 90 | et.timerChan = nil 91 | eventDestChan = et.manager.Queue() 92 | case eventDestChan <- event: 93 | log.Debug("Timer event delivered") 94 | eventDestChan = nil 95 | case <-et.exit: 96 | log.Debug("Halting timer") 97 | return 98 | } 99 | } 100 | } 101 | 102 | // softReset tells the timer to start a new countdown, only if it is not currently counting down 103 | // this will not clear any pending events 104 | func (et *timerImpl) SoftReset(timeout time.Duration, event Event) { 105 | et.startChan <- &timerStart{ 106 | duration: timeout, 107 | event: event, 108 | hard: false, 109 | } 110 | } 111 | 112 | // reset tells the timer to start counting down from a new timeout, this also clears any pending events 113 | func (et *timerImpl) Reset(timeout time.Duration, event Event) { 114 | et.startChan <- &timerStart{ 115 | duration: timeout, 116 | event: event, 117 | hard: true, 118 | } 119 | } 120 | 121 | // stop tells the timer to stop, and not to deliver any pending events 122 | func (et *timerImpl) Stop() { 123 | et.stopChan <- struct{}{} 124 | } 125 | 126 | func (et *timerImpl) Halt() { 127 | select { 128 | case <-et.threaded.exit: 129 | log.Warn("Attempted to halt a threaded object twice") 130 | default: 131 | close(et.threaded.exit) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /consensus/common/pugin_test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/types" 6 | "github.com/DSiSc/validator/tools/account" 7 | "github.com/stretchr/testify/assert" 8 | "testing" 9 | ) 10 | 11 | func TestNewConsensusMap(t *testing.T) { 12 | plugin := NewConsensusPlugin() 13 | latestHeight := plugin.GetLatestBlockHeight() 14 | assert.Equal(t, uint64(0), latestHeight) 15 | content, err := plugin.GetContentByHash(mockHash) 16 | assert.Equal(t, fmt.Errorf("content %x not exist, please confirm", mockHash), err) 17 | assert.Nil(t, content) 18 | block := &types.Block{ 19 | Header: &types.Header{ 20 | Height: uint64(1), 21 | }, 22 | } 23 | plugin.Add(mockHash, block) 24 | plugin.Add(mockHash, block) 25 | content, err = plugin.GetContentByHash(mockHash) 26 | assert.Nil(t, err) 27 | assert.NotNil(t, content) 28 | 29 | content, err = plugin.GetContentByHash(mockHash) 30 | assert.Nil(t, err) 31 | assert.NotNil(t, content) 32 | assert.NotNil(t, Initial, content.State()) 33 | err = content.SetState(ToConsensus) 34 | assert.Equal(t, err, fmt.Errorf("can not move state from %v to %v", Initial, ToConsensus)) 35 | assert.NotNil(t, Initial, content.State()) 36 | 37 | err = content.SetState(InConsensus) 38 | assert.Nil(t, err) 39 | assert.NotNil(t, InConsensus, content.State()) 40 | 41 | signatures := content.Signatures() 42 | assert.Equal(t, 0, len(signatures)) 43 | 44 | ok := content.AddSignature(mockAccounts[0], mockSignset[0]) 45 | assert.Equal(t, true, ok) 46 | 47 | ok = content.AddSignature(mockAccounts[0], mockSignset[0]) 48 | assert.Equal(t, false, ok) 49 | 50 | sign, ok := content.GetSignByAccount(mockAccounts[1]) 51 | assert.Equal(t, false, ok) 52 | 53 | sign, ok = content.GetSignByAccount(mockAccounts[0]) 54 | assert.Equal(t, true, ok) 55 | assert.Equal(t, mockSignset[0], sign) 56 | 57 | signMap := content.GetSignMap() 58 | assert.Equal(t, 1, len(signMap)) 59 | assert.Equal(t, mockSignset[0], signMap[mockAccounts[0]]) 60 | 61 | contents := content.GetContentPayload() 62 | assert.NotNil(t, contents) 63 | assert.Equal(t, block, contents) 64 | 65 | plugin.SetLatestBlockHeight(uint64(1)) 66 | latestHeight = plugin.GetLatestBlockHeight() 67 | assert.Equal(t, uint64(1), latestHeight) 68 | 69 | plugin.Remove(mockHash) 70 | content, err = plugin.GetContentByHash(mockHash) 71 | assert.Equal(t, fmt.Errorf("content %x not exist, please confirm", mockHash), err) 72 | assert.Nil(t, content) 73 | } 74 | 75 | func TestNewResponseNodes(t *testing.T) { 76 | walterLevel := 2 77 | responses := NewResponseNodes(walterLevel, mockAccounts[0]) 78 | state := responses.GetResponseState() 79 | assert.Equal(t, GoOnline, state) 80 | 81 | accounts, state := responses.AddResponseNodes(mockAccounts[0]) 82 | assert.Equal(t, 1, len(accounts)) 83 | assert.Equal(t, GoOnline, state) 84 | 85 | accounts, state = responses.AddResponseNodes(mockAccounts[1]) 86 | assert.Equal(t, 2, len(accounts)) 87 | assert.Equal(t, Online, state) 88 | } 89 | 90 | func TestNewOnlineWizard(t *testing.T) { 91 | wizard := NewOnlineWizard() 92 | assert.NotNil(t, wizard) 93 | 94 | walterLevel := 3 95 | blockHeight := uint64(1) 96 | viewNum := uint64(1) 97 | accounts, state := wizard.AddOnlineResponse(blockHeight, mockAccounts[:2], walterLevel, mockAccounts[0], viewNum) 98 | assert.Equal(t, len(mockAccounts[:2]), len(accounts)) 99 | assert.Equal(t, GoOnline, state) 100 | 101 | state = wizard.GetCurrentState() 102 | assert.Equal(t, GoOnline, state) 103 | 104 | state = wizard.GetCurrentStateByHeight(blockHeight) 105 | assert.Equal(t, GoOnline, state) 106 | 107 | currentHeight := wizard.GetCurrentHeight() 108 | assert.Equal(t, blockHeight, currentHeight) 109 | 110 | master := wizard.GetMasterByBlockHeight(blockHeight) 111 | assert.Equal(t, mockAccounts[0], master) 112 | 113 | state = wizard.GetResponseNodesStateByBlockHeight(uint64(2)) 114 | assert.Equal(t, GoOnline, state) 115 | 116 | state = wizard.GetResponseNodesStateByBlockHeight(uint64(1)) 117 | assert.Equal(t, GoOnline, state) 118 | 119 | accounts, state = wizard.AddOnlineResponse(blockHeight+1, mockAccounts[2:3], walterLevel, mockAccounts[2], viewNum) 120 | assert.Equal(t, len(mockAccounts[2:3]), len(accounts)) 121 | assert.Equal(t, GoOnline, state) 122 | 123 | currentHeight = wizard.GetCurrentHeight() 124 | assert.Equal(t, blockHeight+1, currentHeight) 125 | 126 | master = wizard.GetMasterByBlockHeight(currentHeight) 127 | assert.Equal(t, mockAccounts[2], master) 128 | 129 | accounts, state = wizard.AddOnlineResponse(blockHeight+1, []account.Account{mockAccounts[3]}, walterLevel, mockAccounts[3], viewNum+1) 130 | assert.Equal(t, len(mockAccounts[2:]), len(accounts)) 131 | assert.Equal(t, GoOnline, state) 132 | } 133 | -------------------------------------------------------------------------------- /consensus/policy/dbft/dbft.go: -------------------------------------------------------------------------------- 1 | package dbft 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/log" 6 | "github.com/DSiSc/craft/types" 7 | "github.com/DSiSc/galaxy/consensus/common" 8 | "github.com/DSiSc/galaxy/consensus/config" 9 | "github.com/DSiSc/galaxy/consensus/messages" 10 | "github.com/DSiSc/galaxy/consensus/utils" 11 | "github.com/DSiSc/validator/tools/account" 12 | "time" 13 | ) 14 | 15 | type DBFTPolicy struct { 16 | name string 17 | // local account 18 | account account.Account 19 | core *dbftCore 20 | timeout time.Duration 21 | result chan *messages.ConsensusResult 22 | } 23 | 24 | func NewDBFTPolicy(timeout config.ConsensusTimeout) (*DBFTPolicy, error) { 25 | policy := &DBFTPolicy{ 26 | name: common.DbftPolicy, 27 | timeout: time.Duration(timeout.TimeoutToChangeView), 28 | result: make(chan *messages.ConsensusResult), 29 | } 30 | policy.core = NewDBFTCore(policy.result) 31 | return policy, nil 32 | } 33 | 34 | func (instance *DBFTPolicy) Initialization(local account.Account, master account.Account, peers []account.Account, events types.EventCenter, onLine bool) { 35 | instance.account = local 36 | instance.core.local = instance.account 37 | instance.core.master = master 38 | instance.core.commit = false 39 | instance.core.peers = peers 40 | instance.core.eventCenter = events 41 | instance.core.tolerance = uint8((len(peers) - 1) / 3) 42 | instance.core.signature = &signData{ 43 | signatures: make([][]byte, 0), 44 | signMap: make(map[account.Account][]byte), 45 | } 46 | if !onLine { 47 | // Add timer 48 | timer := time.NewTimer(30 * time.Second) 49 | instance.core.masterTimeout = timer 50 | go instance.core.waitMasterTimeOut(timer) 51 | } 52 | return 53 | } 54 | 55 | func (instance *DBFTPolicy) waitMasterTimeOut(timer *time.Timer) { 56 | for { 57 | select { 58 | case <-timer.C: 59 | log.Info("wait master timeout, so change view begin.") 60 | viewChangeReqMsg := messages.Message{ 61 | MessageType: messages.ViewChangeMessageReqType, 62 | PayLoad: &messages.ViewChangeReqMessage{ 63 | ViewChange: &messages.ViewChangeReq{ 64 | Nodes: []account.Account{instance.core.local}, 65 | Timestamp: time.Now().Unix(), 66 | ViewNum: instance.core.views.viewNum + 1, 67 | }, 68 | }, 69 | } 70 | msgRaw, err := messages.EncodeMessage(viewChangeReqMsg) 71 | if nil != err { 72 | log.Error("marshal proposal msg failed with %v.", err) 73 | return 74 | } 75 | messages.BroadcastPeers(msgRaw, viewChangeReqMsg.MessageType, types.Hash{}, instance.core.peers) 76 | return 77 | } 78 | } 79 | } 80 | 81 | func (instance *DBFTPolicy) PolicyName() string { 82 | return instance.name 83 | } 84 | 85 | func (instance *DBFTPolicy) Start() { 86 | log.Info("start dbft policy service.") 87 | instance.core.Start(instance.account) 88 | } 89 | 90 | func (instance *DBFTPolicy) Prepare(account account.Account) { 91 | log.Info("prepare dbft policy service.") 92 | instance.account = account 93 | } 94 | 95 | func (instance *DBFTPolicy) commit(block *types.Block, result bool) { 96 | commit := &messages.Commit{ 97 | Account: instance.account, 98 | Timestamp: time.Now().Unix(), 99 | Digest: block.Header.MixDigest, 100 | Signatures: block.Header.SigData, 101 | BlockHash: block.HeaderHash, 102 | Result: result, 103 | } 104 | instance.core.SendCommit(commit, block) 105 | } 106 | 107 | func (instance *DBFTPolicy) ToConsensus(p *common.Proposal) error { 108 | var err error 109 | var result = false 110 | request := &messages.Request{ 111 | Timestamp: p.Timestamp, 112 | Payload: p.Block, 113 | } 114 | timer := time.NewTimer(time.Second * instance.timeout) 115 | go utils.SendEvent(instance.core, request) 116 | select { 117 | case consensusResult := <-instance.result: 118 | if nil != consensusResult.Result { 119 | log.Error("consensus for %x failed with error %v.", p.Block.Header.MixDigest, consensusResult.Result) 120 | err = consensusResult.Result 121 | } else { 122 | p.Block.Header.SigData = consensusResult.Signatures 123 | p.Block.HeaderHash = common.HeaderHash(p.Block) 124 | result = true 125 | log.Info("consensus for %x successfully with signature %x.", p.Block.Header.MixDigest, consensusResult.Signatures) 126 | } 127 | go instance.commit(p.Block, result) 128 | timer.Stop() 129 | case <-timer.C: 130 | log.Error("consensus for %x timeout in %d seconds.", p.Block.Header.MixDigest, instance.timeout) 131 | err = fmt.Errorf("timeout for consensus") 132 | go instance.commit(p.Block, result) 133 | timer.Stop() 134 | } 135 | return err 136 | } 137 | 138 | func (instance *DBFTPolicy) Halt() { 139 | return 140 | } 141 | 142 | func (instance *DBFTPolicy) GetConsensusResult() common.ConsensusResult { 143 | return common.ConsensusResult{ 144 | View: instance.core.views.viewNum, 145 | Participate: instance.core.peers, 146 | Master: instance.core.master, 147 | } 148 | } 149 | 150 | func (instance *DBFTPolicy) Online() { 151 | return 152 | } 153 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/bypass.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 Dave Collins 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | // NOTE: Due to the following build constraints, this file will only be compiled 16 | // when the code is not running on Google App Engine, compiled by GopherJS, and 17 | // "-tags safe" is not added to the go build command line. The "disableunsafe" 18 | // tag is deprecated and thus should not be used. 19 | // Go versions prior to 1.4 are disabled because they use a different layout 20 | // for interfaces which make the implementation of unsafeReflectValue more complex. 21 | // +build !js,!appengine,!safe,!disableunsafe,go1.4 22 | 23 | package spew 24 | 25 | import ( 26 | "reflect" 27 | "unsafe" 28 | ) 29 | 30 | const ( 31 | // UnsafeDisabled is a build-time constant which specifies whether or 32 | // not access to the unsafe package is available. 33 | UnsafeDisabled = false 34 | 35 | // ptrSize is the size of a pointer on the current arch. 36 | ptrSize = unsafe.Sizeof((*byte)(nil)) 37 | ) 38 | 39 | type flag uintptr 40 | 41 | var ( 42 | // flagRO indicates whether the value field of a reflect.Value 43 | // is read-only. 44 | flagRO flag 45 | 46 | // flagAddr indicates whether the address of the reflect.Value's 47 | // value may be taken. 48 | flagAddr flag 49 | ) 50 | 51 | // flagKindMask holds the bits that make up the kind 52 | // part of the flags field. In all the supported versions, 53 | // it is in the lower 5 bits. 54 | const flagKindMask = flag(0x1f) 55 | 56 | // Different versions of Go have used different 57 | // bit layouts for the flags type. This table 58 | // records the known combinations. 59 | var okFlags = []struct { 60 | ro, addr flag 61 | }{{ 62 | // From Go 1.4 to 1.5 63 | ro: 1 << 5, 64 | addr: 1 << 7, 65 | }, { 66 | // Up to Go tip. 67 | ro: 1<<5 | 1<<6, 68 | addr: 1 << 8, 69 | }} 70 | 71 | var flagValOffset = func() uintptr { 72 | field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") 73 | if !ok { 74 | panic("reflect.Value has no flag field") 75 | } 76 | return field.Offset 77 | }() 78 | 79 | // flagField returns a pointer to the flag field of a reflect.Value. 80 | func flagField(v *reflect.Value) *flag { 81 | return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) 82 | } 83 | 84 | // unsafeReflectValue converts the passed reflect.Value into a one that bypasses 85 | // the typical safety restrictions preventing access to unaddressable and 86 | // unexported data. It works by digging the raw pointer to the underlying 87 | // value out of the protected value and generating a new unprotected (unsafe) 88 | // reflect.Value to it. 89 | // 90 | // This allows us to check for implementations of the Stringer and error 91 | // interfaces to be used for pretty printing ordinarily unaddressable and 92 | // inaccessible values such as unexported struct fields. 93 | func unsafeReflectValue(v reflect.Value) reflect.Value { 94 | if !v.IsValid() || (v.CanInterface() && v.CanAddr()) { 95 | return v 96 | } 97 | flagFieldPtr := flagField(&v) 98 | *flagFieldPtr &^= flagRO 99 | *flagFieldPtr |= flagAddr 100 | return v 101 | } 102 | 103 | // Sanity checks against future reflect package changes 104 | // to the type or semantics of the Value.flag field. 105 | func init() { 106 | field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") 107 | if !ok { 108 | panic("reflect.Value has no flag field") 109 | } 110 | if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { 111 | panic("reflect.Value flag field has changed kind") 112 | } 113 | type t0 int 114 | var t struct { 115 | A t0 116 | // t0 will have flagEmbedRO set. 117 | t0 118 | // a will have flagStickyRO set 119 | a t0 120 | } 121 | vA := reflect.ValueOf(t).FieldByName("A") 122 | va := reflect.ValueOf(t).FieldByName("a") 123 | vt0 := reflect.ValueOf(t).FieldByName("t0") 124 | 125 | // Infer flagRO from the difference between the flags 126 | // for the (otherwise identical) fields in t. 127 | flagPublic := *flagField(&vA) 128 | flagWithRO := *flagField(&va) | *flagField(&vt0) 129 | flagRO = flagPublic ^ flagWithRO 130 | 131 | // Infer flagAddr from the difference between a value 132 | // taken from a pointer and not. 133 | vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A") 134 | flagNoPtr := *flagField(&vA) 135 | flagPtr := *flagField(&vPtrA) 136 | flagAddr = flagNoPtr ^ flagPtr 137 | 138 | // Check that the inferred flags tally with one of the known versions. 139 | for _, f := range okFlags { 140 | if flagRO == f.ro && flagAddr == f.addr { 141 | return 142 | } 143 | } 144 | panic("reflect.Value read-only flag has changed semantics") 145 | } 146 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/http_assertions.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httptest" 7 | "net/url" 8 | "strings" 9 | ) 10 | 11 | // httpCode is a helper that returns HTTP code of the response. It returns -1 and 12 | // an error if building a new request fails. 13 | func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) { 14 | w := httptest.NewRecorder() 15 | req, err := http.NewRequest(method, url, nil) 16 | if err != nil { 17 | return -1, err 18 | } 19 | req.URL.RawQuery = values.Encode() 20 | handler(w, req) 21 | return w.Code, nil 22 | } 23 | 24 | // HTTPSuccess asserts that a specified handler returns a success status code. 25 | // 26 | // assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) 27 | // 28 | // Returns whether the assertion was successful (true) or not (false). 29 | func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { 30 | if h, ok := t.(tHelper); ok { 31 | h.Helper() 32 | } 33 | code, err := httpCode(handler, method, url, values) 34 | if err != nil { 35 | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) 36 | return false 37 | } 38 | 39 | isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent 40 | if !isSuccessCode { 41 | Fail(t, fmt.Sprintf("Expected HTTP success status code for %q but received %d", url+"?"+values.Encode(), code)) 42 | } 43 | 44 | return isSuccessCode 45 | } 46 | 47 | // HTTPRedirect asserts that a specified handler returns a redirect status code. 48 | // 49 | // assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} 50 | // 51 | // Returns whether the assertion was successful (true) or not (false). 52 | func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { 53 | if h, ok := t.(tHelper); ok { 54 | h.Helper() 55 | } 56 | code, err := httpCode(handler, method, url, values) 57 | if err != nil { 58 | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) 59 | return false 60 | } 61 | 62 | isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect 63 | if !isRedirectCode { 64 | Fail(t, fmt.Sprintf("Expected HTTP redirect status code for %q but received %d", url+"?"+values.Encode(), code)) 65 | } 66 | 67 | return isRedirectCode 68 | } 69 | 70 | // HTTPError asserts that a specified handler returns an error status code. 71 | // 72 | // assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} 73 | // 74 | // Returns whether the assertion was successful (true) or not (false). 75 | func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { 76 | if h, ok := t.(tHelper); ok { 77 | h.Helper() 78 | } 79 | code, err := httpCode(handler, method, url, values) 80 | if err != nil { 81 | Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) 82 | return false 83 | } 84 | 85 | isErrorCode := code >= http.StatusBadRequest 86 | if !isErrorCode { 87 | Fail(t, fmt.Sprintf("Expected HTTP error status code for %q but received %d", url+"?"+values.Encode(), code)) 88 | } 89 | 90 | return isErrorCode 91 | } 92 | 93 | // HTTPBody is a helper that returns HTTP body of the response. It returns 94 | // empty string if building a new request fails. 95 | func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { 96 | w := httptest.NewRecorder() 97 | req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) 98 | if err != nil { 99 | return "" 100 | } 101 | handler(w, req) 102 | return w.Body.String() 103 | } 104 | 105 | // HTTPBodyContains asserts that a specified handler returns a 106 | // body that contains a string. 107 | // 108 | // assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") 109 | // 110 | // Returns whether the assertion was successful (true) or not (false). 111 | func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { 112 | if h, ok := t.(tHelper); ok { 113 | h.Helper() 114 | } 115 | body := HTTPBody(handler, method, url, values) 116 | 117 | contains := strings.Contains(body, fmt.Sprint(str)) 118 | if !contains { 119 | Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) 120 | } 121 | 122 | return contains 123 | } 124 | 125 | // HTTPBodyNotContains asserts that a specified handler returns a 126 | // body that does not contain a string. 127 | // 128 | // assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") 129 | // 130 | // Returns whether the assertion was successful (true) or not (false). 131 | func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { 132 | if h, ok := t.(tHelper); ok { 133 | h.Helper() 134 | } 135 | body := HTTPBody(handler, method, url, values) 136 | 137 | contains := strings.Contains(body, fmt.Sprint(str)) 138 | if contains { 139 | Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) 140 | } 141 | 142 | return !contains 143 | } 144 | -------------------------------------------------------------------------------- /consensus/policy/bft/bft_test.go: -------------------------------------------------------------------------------- 1 | package bft 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/log" 6 | "github.com/DSiSc/craft/types" 7 | "github.com/DSiSc/galaxy/consensus/common" 8 | consensusConfig "github.com/DSiSc/galaxy/consensus/config" 9 | "github.com/DSiSc/galaxy/consensus/messages" 10 | tools "github.com/DSiSc/galaxy/consensus/utils" 11 | "github.com/DSiSc/galaxy/participates/config" 12 | commonr "github.com/DSiSc/galaxy/role/common" 13 | "github.com/DSiSc/monkey" 14 | "github.com/DSiSc/validator/tools/account" 15 | "github.com/stretchr/testify/assert" 16 | "reflect" 17 | "testing" 18 | "time" 19 | ) 20 | 21 | func mock_conf(policy string) config.ParticipateConfig { 22 | return config.ParticipateConfig{ 23 | PolicyName: policy, 24 | } 25 | } 26 | 27 | var timeout = consensusConfig.ConsensusTimeout{ 28 | TimeoutToChangeView: int64(10000), 29 | } 30 | 31 | func TestNewBFTPolicy(t *testing.T) { 32 | bft, err := NewBFTPolicy(timeout) 33 | assert.NotNil(t, bft) 34 | assert.Nil(t, err) 35 | assert.Equal(t, common.BftPolicy, bft.name) 36 | assert.NotNil(t, bft.bftCore) 37 | } 38 | 39 | func TestBFTPolicy_PolicyName(t *testing.T) { 40 | bft, _ := NewBFTPolicy(timeout) 41 | assert.Equal(t, common.BftPolicy, bft.name) 42 | assert.Equal(t, bft.name, bft.PolicyName()) 43 | } 44 | 45 | func mockRoleAssignment(master account.Account, accounts []account.Account) map[account.Account]commonr.Roler { 46 | delegates := len(accounts) 47 | assignments := make(map[account.Account]commonr.Roler, delegates) 48 | for _, delegate := range accounts { 49 | if delegate == master { 50 | assignments[delegate] = commonr.Master 51 | } else { 52 | assignments[delegate] = commonr.Slave 53 | } 54 | } 55 | return assignments 56 | } 57 | 58 | func TestBFTPolicy_Initialization(t *testing.T) { 59 | bft, err := NewBFTPolicy(timeout) 60 | assert.NotNil(t, bft) 61 | assert.Nil(t, err) 62 | 63 | bft.Initialization(mockAccounts[0], mockAccounts[3], mockAccounts, nil, true) 64 | assert.Equal(t, bft.bftCore.peers, mockAccounts) 65 | assert.Equal(t, bft.bftCore.tolerance, uint8((len(mockAccounts)-1)/3)) 66 | assert.Equal(t, bft.bftCore.master, mockAccounts[3]) 67 | assert.Equal(t, 0, len(bft.bftCore.validator)) 68 | assert.Equal(t, 0, len(bft.bftCore.payloads)) 69 | assert.NotNil(t, bft.bftCore.signature) 70 | assert.Equal(t, 0, len(bft.bftCore.signature.signMap)) 71 | assert.Equal(t, 0, len(bft.bftCore.signature.signatures)) 72 | } 73 | 74 | func TestBFTPolicy_Start(t *testing.T) { 75 | bft, _ := NewBFTPolicy(timeout) 76 | bft.Initialization(mockAccounts[0], mockAccounts[0], make([]account.Account, 0), nil, true) 77 | var b *bftCore 78 | monkey.PatchInstanceMethod(reflect.TypeOf(b), "Start", func(*bftCore, account.Account) { 79 | log.Info("pass it.") 80 | return 81 | }) 82 | bft.Start() 83 | monkey.UnpatchInstanceMethod(reflect.TypeOf(b), "Start") 84 | } 85 | 86 | var mockConsensusResult = &messages.ConsensusResult{ 87 | Signatures: mockSignset, 88 | Result: nil, 89 | } 90 | 91 | func TestBFTPolicy_ToConsensus(t *testing.T) { 92 | bft, err := NewBFTPolicy(timeout) 93 | assert.NotNil(t, bft) 94 | assert.Nil(t, err) 95 | bft.account = mockAccounts[0] 96 | bft.bftCore.local = mockAccounts[0] 97 | bft.bftCore.peers = mockAccounts 98 | monkey.Patch(tools.SendEvent, func(tools.Receiver, tools.Event) { 99 | bft.result <- mockConsensusResult 100 | }) 101 | var b *bftCore 102 | monkey.PatchInstanceMethod(reflect.TypeOf(b), "SendCommit", func(*bftCore, *messages.Commit, *types.Block) { 103 | return 104 | }) 105 | proposal := &common.Proposal{ 106 | Block: &types.Block{ 107 | Header: &types.Header{ 108 | Height: 0, 109 | }, 110 | }, 111 | } 112 | assert.Equal(t, 0, len(proposal.Block.Header.SigData)) 113 | err = bft.ToConsensus(proposal) 114 | assert.Nil(t, err) 115 | 116 | bft.timeout = time.Duration(2) 117 | monkey.Patch(tools.SendEvent, func(tools.Receiver, tools.Event) { 118 | return 119 | }) 120 | monkey.PatchInstanceMethod(reflect.TypeOf(b), "SendCommit", func(*bftCore, *messages.Commit, *types.Block) { 121 | return 122 | }) 123 | err = bft.ToConsensus(proposal) 124 | assert.Equal(t, fmt.Errorf("timeout for consensus"), err) 125 | } 126 | 127 | var MockHash = types.Hash{ 128 | 0x1d, 0xcf, 0x7, 0xba, 0xfc, 0x42, 0xb0, 0x8d, 0xfd, 0x23, 0x9c, 0x45, 0xa4, 0xb9, 0x38, 0xd, 129 | 0x8d, 0xfe, 0x5d, 0x6f, 0xa7, 0xdb, 0xd5, 0x50, 0xc9, 0x25, 0xb1, 0xb3, 0x4, 0xdc, 0xc5, 0x1c, 130 | } 131 | 132 | func TestBFTPolicy_commit(t *testing.T) { 133 | mockAccount := account.Account{ 134 | Address: types.Address{0x33, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 135 | 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 136 | Extension: account.AccountExtension{ 137 | Id: 0, 138 | Url: "127.0.0.1:8080", 139 | }, 140 | } 141 | bft, err := NewBFTPolicy(timeout) 142 | bft.account = mockAccount 143 | bft.bftCore.local = mockAccount 144 | go bft.Start() 145 | assert.NotNil(t, bft) 146 | assert.Nil(t, err) 147 | block := &types.Block{ 148 | Header: &types.Header{ 149 | ChainID: 1, 150 | PrevBlockHash: MockHash, 151 | StateRoot: MockHash, 152 | TxRoot: MockHash, 153 | ReceiptsRoot: MockHash, 154 | Height: 1, 155 | Timestamp: uint64(time.Now().Unix()), 156 | SigData: mockSignset[:4], 157 | }, 158 | Transactions: make([]*types.Transaction, 0), 159 | } 160 | bft.bftCore.peers = append(bft.bftCore.peers, mockAccount) 161 | bft.commit(block, true) 162 | } 163 | 164 | func TestFBFTPolicy_GetConsensusResult(t *testing.T) { 165 | bft, err := NewBFTPolicy(timeout) 166 | assert.Nil(t, err) 167 | bft.Initialization(mockAccounts[0], mockAccounts[0], mockAccounts, nil, false) 168 | result := bft.GetConsensusResult() 169 | assert.Equal(t, uint64(0), result.View) 170 | assert.Equal(t, mockAccounts[0], result.Master) 171 | assert.Equal(t, len(mockAccounts), len(result.Participate)) 172 | } 173 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/spew.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 Dave Collins 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | package spew 18 | 19 | import ( 20 | "fmt" 21 | "io" 22 | ) 23 | 24 | // Errorf is a wrapper for fmt.Errorf that treats each argument as if it were 25 | // passed with a default Formatter interface returned by NewFormatter. It 26 | // returns the formatted string as a value that satisfies error. See 27 | // NewFormatter for formatting details. 28 | // 29 | // This function is shorthand for the following syntax: 30 | // 31 | // fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 32 | func Errorf(format string, a ...interface{}) (err error) { 33 | return fmt.Errorf(format, convertArgs(a)...) 34 | } 35 | 36 | // Fprint is a wrapper for fmt.Fprint that treats each argument as if it were 37 | // passed with a default Formatter interface returned by NewFormatter. It 38 | // returns the number of bytes written and any write error encountered. See 39 | // NewFormatter for formatting details. 40 | // 41 | // This function is shorthand for the following syntax: 42 | // 43 | // fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b)) 44 | func Fprint(w io.Writer, a ...interface{}) (n int, err error) { 45 | return fmt.Fprint(w, convertArgs(a)...) 46 | } 47 | 48 | // Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were 49 | // passed with a default Formatter interface returned by NewFormatter. It 50 | // returns the number of bytes written and any write error encountered. See 51 | // NewFormatter for formatting details. 52 | // 53 | // This function is shorthand for the following syntax: 54 | // 55 | // fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b)) 56 | func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { 57 | return fmt.Fprintf(w, format, convertArgs(a)...) 58 | } 59 | 60 | // Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it 61 | // passed with a default Formatter interface returned by NewFormatter. See 62 | // NewFormatter for formatting details. 63 | // 64 | // This function is shorthand for the following syntax: 65 | // 66 | // fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b)) 67 | func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { 68 | return fmt.Fprintln(w, convertArgs(a)...) 69 | } 70 | 71 | // Print is a wrapper for fmt.Print that treats each argument as if it were 72 | // passed with a default Formatter interface returned by NewFormatter. It 73 | // returns the number of bytes written and any write error encountered. See 74 | // NewFormatter for formatting details. 75 | // 76 | // This function is shorthand for the following syntax: 77 | // 78 | // fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b)) 79 | func Print(a ...interface{}) (n int, err error) { 80 | return fmt.Print(convertArgs(a)...) 81 | } 82 | 83 | // Printf is a wrapper for fmt.Printf that treats each argument as if it were 84 | // passed with a default Formatter interface returned by NewFormatter. It 85 | // returns the number of bytes written and any write error encountered. See 86 | // NewFormatter for formatting details. 87 | // 88 | // This function is shorthand for the following syntax: 89 | // 90 | // fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 91 | func Printf(format string, a ...interface{}) (n int, err error) { 92 | return fmt.Printf(format, convertArgs(a)...) 93 | } 94 | 95 | // Println is a wrapper for fmt.Println that treats each argument as if it were 96 | // passed with a default Formatter interface returned by NewFormatter. It 97 | // returns the number of bytes written and any write error encountered. See 98 | // NewFormatter for formatting details. 99 | // 100 | // This function is shorthand for the following syntax: 101 | // 102 | // fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b)) 103 | func Println(a ...interface{}) (n int, err error) { 104 | return fmt.Println(convertArgs(a)...) 105 | } 106 | 107 | // Sprint is a wrapper for fmt.Sprint that treats each argument as if it were 108 | // passed with a default Formatter interface returned by NewFormatter. It 109 | // returns the resulting string. See NewFormatter for formatting details. 110 | // 111 | // This function is shorthand for the following syntax: 112 | // 113 | // fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b)) 114 | func Sprint(a ...interface{}) string { 115 | return fmt.Sprint(convertArgs(a)...) 116 | } 117 | 118 | // Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were 119 | // passed with a default Formatter interface returned by NewFormatter. It 120 | // returns the resulting string. See NewFormatter for formatting details. 121 | // 122 | // This function is shorthand for the following syntax: 123 | // 124 | // fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 125 | func Sprintf(format string, a ...interface{}) string { 126 | return fmt.Sprintf(format, convertArgs(a)...) 127 | } 128 | 129 | // Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it 130 | // were passed with a default Formatter interface returned by NewFormatter. It 131 | // returns the resulting string. See NewFormatter for formatting details. 132 | // 133 | // This function is shorthand for the following syntax: 134 | // 135 | // fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b)) 136 | func Sprintln(a ...interface{}) string { 137 | return fmt.Sprintln(convertArgs(a)...) 138 | } 139 | 140 | // convertArgs accepts a slice of arguments and returns a slice of the same 141 | // length with each argument converted to a default spew Formatter interface. 142 | func convertArgs(args []interface{}) (formatters []interface{}) { 143 | formatters = make([]interface{}, len(args)) 144 | for index, arg := range args { 145 | formatters[index] = NewFormatter(arg) 146 | } 147 | return formatters 148 | } 149 | -------------------------------------------------------------------------------- /consensus/policy/solo/solo.go: -------------------------------------------------------------------------------- 1 | package solo 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/log" 6 | "github.com/DSiSc/craft/types" 7 | "github.com/DSiSc/galaxy/consensus/common" 8 | "github.com/DSiSc/galaxy/consensus/config" 9 | "github.com/DSiSc/validator" 10 | "github.com/DSiSc/validator/tools/account" 11 | "github.com/DSiSc/validator/tools/signature" 12 | "math" 13 | ) 14 | 15 | type SoloPolicy struct { 16 | name string 17 | local account.Account 18 | tolerance uint8 19 | version common.Version 20 | peers []account.Account 21 | master account.Account 22 | eventCenter types.EventCenter 23 | blockSwitch chan<- interface{} 24 | enableEmptyBlock bool 25 | enableLocalSignatureVerify bool 26 | } 27 | 28 | // SoloProposal that with solo policy 29 | type SoloProposal struct { 30 | proposal *common.Proposal 31 | version common.Version 32 | status common.ConsensusStatus 33 | } 34 | 35 | func NewSoloPolicy(blkSwitch chan<- interface{}, enable bool, signVerify config.SignatureVerifySwitch) (*SoloPolicy, error) { 36 | policy := &SoloPolicy{ 37 | name: common.SoloPolicy, 38 | tolerance: common.SoloConsensusNum, 39 | blockSwitch: blkSwitch, 40 | enableEmptyBlock: enable, 41 | enableLocalSignatureVerify: signVerify.LocalVerifySignature, 42 | } 43 | return policy, nil 44 | } 45 | 46 | func (instance *SoloPolicy) PolicyName() string { 47 | return instance.name 48 | } 49 | 50 | func (instance *SoloPolicy) Prepare(account account.Account) { 51 | instance.local = account 52 | } 53 | 54 | func (instance *SoloPolicy) Start() { 55 | log.Info("Start solo policy service.") 56 | return 57 | } 58 | 59 | func (instance *SoloPolicy) Halt() { 60 | log.Warn("Stop solo policy service.") 61 | return 62 | } 63 | 64 | func (instance *SoloPolicy) Initialization(local account.Account, master account.Account, accounts []account.Account, event types.EventCenter, onLine bool) { 65 | if onLine { 66 | log.Debug("online first time.") 67 | } 68 | if common.SoloConsensusNum != uint8(len(accounts)) { 69 | panic("solo policy only support one participate") 70 | } 71 | instance.local = local 72 | instance.master = master 73 | instance.peers = accounts 74 | instance.eventCenter = event 75 | return 76 | } 77 | 78 | func (instance *SoloPolicy) toSoloProposal(p *common.Proposal) *SoloProposal { 79 | if instance.version == math.MaxUint64 { 80 | instance.version = 0 81 | } 82 | return &SoloProposal{ 83 | proposal: p, 84 | version: instance.version + 1, 85 | status: common.Proposing, 86 | } 87 | } 88 | 89 | // to get consensus 90 | func (instance *SoloPolicy) ToConsensus(p *common.Proposal) error { 91 | var err error 92 | proposal := instance.toSoloProposal(p) 93 | err = instance.prepareConsensus(proposal) 94 | if err != nil { 95 | log.Error("Prepare proposal failed.") 96 | instance.eventCenter.Notify(types.EventConsensusFailed, nil) 97 | return fmt.Errorf("prepare proposal failed") 98 | } 99 | ok := instance.toConsensus(proposal) 100 | if ok == false { 101 | log.Error("Local verify failed.") 102 | instance.eventCenter.Notify(types.EventConsensusFailed, nil) 103 | return fmt.Errorf("local verify failed") 104 | } 105 | // verify num of sign 106 | signData := proposal.proposal.Block.Header.SigData 107 | var validSign = make(map[types.Address][]byte) 108 | for _, value := range signData { 109 | signAddress, err := signature.Verify(p.Block.Header.MixDigest, value) 110 | if err != nil { 111 | log.Error("Invalid signature is %x.", value) 112 | continue 113 | } 114 | validSign[signAddress] = value 115 | } 116 | if uint8(len(validSign)) < common.SoloConsensusNum { 117 | log.Error("Not enough valid signature which is %d.", len(validSign)) 118 | instance.eventCenter.Notify(types.EventConsensusFailed, nil) 119 | return fmt.Errorf("not enough valid signature") 120 | } 121 | if _, ok := validSign[instance.local.Address]; !ok { 122 | log.Error("absence self signature.") 123 | instance.eventCenter.Notify(types.EventConsensusFailed, nil) 124 | return fmt.Errorf("absence self signature") 125 | } 126 | err = instance.submitConsensus(proposal) 127 | if err != nil { 128 | log.Error("Submit proposal failed.") 129 | instance.eventCenter.Notify(types.EventConsensusFailed, nil) 130 | return fmt.Errorf("submit proposal failed") 131 | } 132 | if proposal.status != common.Committed { 133 | log.Error("Not to consensus.") 134 | instance.eventCenter.Notify(types.EventConsensusFailed, nil) 135 | return fmt.Errorf("consensus status fault") 136 | } 137 | instance.version = proposal.version 138 | p.Block.HeaderHash = common.HeaderHash(p.Block) 139 | instance.commitBlock(p.Block) 140 | return err 141 | } 142 | 143 | func (instance *SoloPolicy) commitBlock(block *types.Block) { 144 | log.Info("send block to block switch.") 145 | if !instance.enableEmptyBlock && 0 == len(block.Transactions) { 146 | log.Warn("block without transaction.") 147 | instance.eventCenter.Notify(types.EventBlockWithoutTxs, nil) 148 | return 149 | } 150 | instance.blockSwitch <- block 151 | } 152 | 153 | func (instance *SoloPolicy) prepareConsensus(p *SoloProposal) error { 154 | if p.version <= instance.version { 155 | log.Error("Proposal version segment less than version which has confirmed.") 156 | return fmt.Errorf("proposal version less than confirmed") 157 | } 158 | if p.status != common.Proposing { 159 | log.Error("Proposal status must be Proposal befor submit consensus.") 160 | return fmt.Errorf("proposal status must be in proposal") 161 | } 162 | p.status = common.Propose 163 | return nil 164 | } 165 | 166 | func (instance *SoloPolicy) submitConsensus(p *SoloProposal) error { 167 | if p.status != common.Propose { 168 | log.Error("Proposal status must be Propose to submit consensus.") 169 | return fmt.Errorf("proposal status must be Propose") 170 | } 171 | p.status = common.Committed 172 | return nil 173 | } 174 | 175 | func (instance *SoloPolicy) toConsensus(p *SoloProposal) bool { 176 | if uint8(len(instance.peers)) != common.SoloConsensusNum { 177 | log.Error("Solo participates invalid.") 178 | return false 179 | } 180 | 181 | local := instance.peers[0] 182 | validators := validator.NewValidator(&local) 183 | _, ok := validators.ValidateBlock(p.proposal.Block, instance.enableLocalSignatureVerify) 184 | if nil != ok { 185 | log.Error("Validator verify failed.") 186 | return false 187 | } 188 | log.Info("Consensus reached for block %d.", p.proposal.Block.Header.Height) 189 | return true 190 | } 191 | 192 | func (instance *SoloPolicy) GetConsensusResult() common.ConsensusResult { 193 | return common.ConsensusResult{ 194 | View: uint64(0), 195 | Participate: instance.peers, 196 | Master: instance.master, 197 | } 198 | } 199 | 200 | func (instance *SoloPolicy) Online() { 201 | instance.eventCenter.Notify(types.EventOnline, nil) 202 | return 203 | } 204 | -------------------------------------------------------------------------------- /role/policy/dpos/dpos_test.go: -------------------------------------------------------------------------------- 1 | package dpos 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/contractsManage/contracts" 6 | "github.com/DSiSc/craft/types" 7 | "github.com/DSiSc/galaxy/participates" 8 | "github.com/DSiSc/galaxy/participates/config" 9 | "github.com/DSiSc/galaxy/role/common" 10 | "github.com/DSiSc/monkey" 11 | "github.com/DSiSc/repository" 12 | "github.com/DSiSc/validator/tools/account" 13 | "github.com/stretchr/testify/assert" 14 | "reflect" 15 | "testing" 16 | ) 17 | 18 | var mockAccounts = []account.Account{ 19 | account.Account{ 20 | Address: types.Address{0x33, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 21 | 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 22 | Extension: account.AccountExtension{ 23 | Id: 0, 24 | Url: "127.0.0.1:8080", 25 | }, 26 | }, 27 | account.Account{ 28 | Address: types.Address{0x34, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 29 | 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 30 | Extension: account.AccountExtension{ 31 | Id: 1, 32 | Url: "127.0.0.1:8081"}, 33 | }, 34 | account.Account{ 35 | Address: types.Address{0x35, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 36 | Extension: account.AccountExtension{ 37 | Id: 2, 38 | Url: "127.0.0.1:8082", 39 | }, 40 | }, 41 | 42 | account.Account{ 43 | Address: types.Address{0x36, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 44 | Extension: account.AccountExtension{ 45 | Id: 3, 46 | Url: "127.0.0.1:8083", 47 | }, 48 | }, 49 | } 50 | 51 | func TestNewDPOSPolicy(t *testing.T) { 52 | policy, err := NewDPOSPolicy() 53 | assert.Nil(t, err) 54 | assert.NotNil(t, policy) 55 | assert.Equal(t, common.DposPolicy, policy.name) 56 | } 57 | 58 | func TestDPOSPolicy_PolicyName(t *testing.T) { 59 | policy, _ := NewDPOSPolicy() 60 | assert.Equal(t, common.DposPolicy, policy.name) 61 | assert.Equal(t, policy.name, policy.PolicyName()) 62 | } 63 | 64 | var participateConf = config.ParticipateConfig{ 65 | PolicyName: common.DposPolicy, 66 | } 67 | 68 | func TestDPOSPolicy_RoleAssignments(t *testing.T) { 69 | dposPolicy, err := NewDPOSPolicy() 70 | assert.NotNil(t, dposPolicy) 71 | assert.Nil(t, err) 72 | 73 | assignment, master, err := dposPolicy.RoleAssignments(mockAccounts) 74 | assert.Nil(t, assignment) 75 | assert.Equal(t, fmt.Errorf("get NewLatestStateRepository failed"), err) 76 | 77 | monkey.Patch(repository.NewLatestStateRepository, func() (*repository.Repository, error) { 78 | var temp repository.Repository 79 | return &temp, nil 80 | }) 81 | var b *repository.Repository 82 | var height = uint64(0) 83 | monkey.PatchInstanceMethod(reflect.TypeOf(b), "GetCurrentBlock", func(*repository.Repository) *types.Block { 84 | return &types.Block{ 85 | Header: &types.Header{ 86 | Height: height, 87 | }, 88 | } 89 | }) 90 | assignment, master, err3 := dposPolicy.RoleAssignments(mockAccounts) 91 | assert.NotNil(t, assignment) 92 | assert.Nil(t, err3) 93 | address := mockAccounts[height+1] 94 | assert.Equal(t, common.Master, assignment[address]) 95 | assert.Equal(t, address, master) 96 | assert.Equal(t, common.Slave, assignment[mockAccounts[height]]) 97 | monkey.UnpatchAll() 98 | } 99 | 100 | func TestDPOSPolicy_AppointRole(t *testing.T) { 101 | monkey.Patch(contracts.NewVotingContract, func() contracts.Voting { 102 | return &contracts.VotingContract{} 103 | }) 104 | participate, err := participates.NewParticipates(participateConf) 105 | assert.NotNil(t, participate) 106 | assert.Nil(t, err) 107 | 108 | dposPolicy, err1 := NewDPOSPolicy() 109 | assert.NotNil(t, dposPolicy) 110 | assert.Nil(t, err1) 111 | 112 | monkey.Patch(repository.NewLatestStateRepository, func() (*repository.Repository, error) { 113 | var temp repository.Repository 114 | return &temp, nil 115 | }) 116 | var b *repository.Repository 117 | monkey.PatchInstanceMethod(reflect.TypeOf(b), "GetCurrentBlock", func(*repository.Repository) *types.Block { 118 | return &types.Block{ 119 | Header: &types.Header{ 120 | Height: 0, 121 | }, 122 | } 123 | }) 124 | assignment, _, err3 := dposPolicy.RoleAssignments(mockAccounts) 125 | assert.NotNil(t, assignment) 126 | assert.Nil(t, err3) 127 | 128 | account0 := mockAccounts[0] 129 | assert.Equal(t, common.Slave, dposPolicy.assignments[account0]) 130 | 131 | dposPolicy.AppointRole(account0) 132 | assert.Equal(t, common.Master, dposPolicy.assignments[account0]) 133 | 134 | dposPolicy.assignments[account0] = common.Slave 135 | err = dposPolicy.AppointRole(account0) 136 | assert.Equal(t, fmt.Errorf("no master exist in current delegates"), err) 137 | 138 | var fakeAccount account.Account 139 | err = dposPolicy.AppointRole(fakeAccount) 140 | assert.Equal(t, fmt.Errorf("appoint account is not a delegate"), err) 141 | monkey.UnpatchAll() 142 | } 143 | 144 | func TestDPOSPolicy_GetRoles(t *testing.T) { 145 | monkey.Patch(contracts.NewVotingContract, func() contracts.Voting { 146 | return &contracts.VotingContract{} 147 | }) 148 | participate, err := participates.NewParticipates(participateConf) 149 | assert.NotNil(t, participate) 150 | assert.Nil(t, err) 151 | 152 | dposPolicy, err := NewDPOSPolicy() 153 | assert.NotNil(t, dposPolicy) 154 | assert.Nil(t, err) 155 | 156 | monkey.Patch(repository.NewLatestStateRepository, func() (*repository.Repository, error) { 157 | var temp repository.Repository 158 | return &temp, nil 159 | }) 160 | var b *repository.Repository 161 | monkey.PatchInstanceMethod(reflect.TypeOf(b), "GetCurrentBlock", func(*repository.Repository) *types.Block { 162 | return &types.Block{ 163 | Header: &types.Header{ 164 | Height: 0, 165 | }, 166 | } 167 | }) 168 | assignment, _, err := dposPolicy.RoleAssignments(mockAccounts) 169 | assert.NotNil(t, assignment) 170 | assert.Nil(t, err) 171 | 172 | dposPolicy.assignments = make(map[account.Account]common.Roler) 173 | account0 := mockAccounts[0] 174 | role, err := dposPolicy.GetRoles(account0) 175 | assert.Equal(t, common.UnKnown, role) 176 | assert.Equal(t, common.AssignmentNotBeExecute, err) 177 | 178 | dposPolicy.assignments = assignment 179 | role, err = dposPolicy.GetRoles(account0) 180 | assert.Nil(t, err) 181 | assert.Equal(t, common.Slave, role) 182 | 183 | account1 := mockAccounts[1] 184 | role, err = dposPolicy.GetRoles(account1) 185 | assert.Equal(t, common.Master, role) 186 | 187 | var fakeAccount account.Account 188 | role, err = dposPolicy.GetRoles(fakeAccount) 189 | assert.Equal(t, common.UnKnown, role) 190 | monkey.UnpatchAll() 191 | } 192 | 193 | func TestSoloPolicy_ChangeRoleAssignment(t *testing.T) { 194 | asserts := assert.New(t) 195 | monkey.Patch(contracts.NewVotingContract, func() contracts.Voting { 196 | return &contracts.VotingContract{} 197 | }) 198 | participate, err := participates.NewParticipates(participateConf) 199 | asserts.NotNil(participate) 200 | asserts.Nil(err) 201 | 202 | dposPolicy, err := NewDPOSPolicy() 203 | asserts.NotNil(dposPolicy) 204 | asserts.Nil(err) 205 | 206 | monkey.Patch(repository.NewLatestStateRepository, func() (*repository.Repository, error) { 207 | var temp repository.Repository 208 | return &temp, nil 209 | }) 210 | var b *repository.Repository 211 | monkey.PatchInstanceMethod(reflect.TypeOf(b), "GetCurrentBlock", func(*repository.Repository) *types.Block { 212 | return &types.Block{ 213 | Header: &types.Header{ 214 | Height: 0, 215 | }, 216 | } 217 | }) 218 | dposPolicy.participates = mockAccounts 219 | assignment, _, err := dposPolicy.RoleAssignments(dposPolicy.participates) 220 | asserts.NotNil(assignment) 221 | asserts.Nil(err) 222 | asserts.Equal(common.Master, assignment[mockAccounts[1]]) 223 | 224 | dposPolicy.ChangeRoleAssignment(assignment, uint64(0)) 225 | asserts.Equal(common.Master, assignment[mockAccounts[0]]) 226 | asserts.Equal(common.Slave, assignment[mockAccounts[1]]) 227 | } 228 | -------------------------------------------------------------------------------- /consensus/policy/dbft/dbft_test.go: -------------------------------------------------------------------------------- 1 | package dbft 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/log" 6 | "github.com/DSiSc/craft/types" 7 | "github.com/DSiSc/galaxy/consensus/common" 8 | consensusConfig "github.com/DSiSc/galaxy/consensus/config" 9 | "github.com/DSiSc/galaxy/consensus/messages" 10 | "github.com/DSiSc/galaxy/consensus/utils" 11 | "github.com/DSiSc/galaxy/participates/config" 12 | roleCommon "github.com/DSiSc/galaxy/role/common" 13 | "github.com/DSiSc/monkey" 14 | "github.com/DSiSc/validator/tools/account" 15 | "github.com/stretchr/testify/assert" 16 | "reflect" 17 | "testing" 18 | "time" 19 | ) 20 | 21 | var mockAccounts = []account.Account{ 22 | account.Account{ 23 | Address: types.Address{0x33, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 24 | 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 25 | Extension: account.AccountExtension{ 26 | Id: 0, 27 | Url: "127.0.0.1:8080", 28 | }, 29 | }, 30 | account.Account{ 31 | Address: types.Address{0x34, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 32 | 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 33 | Extension: account.AccountExtension{ 34 | Id: 1, 35 | Url: "127.0.0.1:8081"}, 36 | }, 37 | account.Account{ 38 | Address: types.Address{0x35, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 39 | Extension: account.AccountExtension{ 40 | Id: 2, 41 | Url: "127.0.0.1:8082", 42 | }, 43 | }, 44 | 45 | account.Account{ 46 | Address: types.Address{0x36, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 47 | Extension: account.AccountExtension{ 48 | Id: 3, 49 | Url: "127.0.0.1:8083", 50 | }, 51 | }, 52 | } 53 | 54 | var mockHash = types.Hash{ 55 | 0xbd, 0x79, 0x1d, 0x4a, 0xf9, 0x64, 0x8f, 0xc3, 0x7f, 0x94, 0xeb, 0x36, 0x53, 0x19, 0xf6, 0xd0, 56 | 0xa9, 0x78, 0x9f, 0x9c, 0x22, 0x47, 0x2c, 0xa7, 0xa6, 0x12, 0xa9, 0xca, 0x4, 0x13, 0xc1, 0x4, 57 | } 58 | 59 | var sigChannel = make(chan *messages.ConsensusResult) 60 | 61 | func TestNewDBFTCore(t *testing.T) { 62 | dbft := NewDBFTCore(sigChannel) 63 | assert.NotNil(t, dbft) 64 | } 65 | 66 | var mockSignset = [][]byte{ 67 | {0x33, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 68 | {0x34, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 69 | {0x35, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 70 | {0x36, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 71 | {0x37, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 72 | } 73 | 74 | func mock_conf(policy string) config.ParticipateConfig { 75 | return config.ParticipateConfig{ 76 | PolicyName: policy, 77 | } 78 | } 79 | 80 | var timeout = consensusConfig.ConsensusTimeout{ 81 | TimeoutToChangeView: int64(10000), 82 | } 83 | 84 | func TestNewBFTPolicy(t *testing.T) { 85 | dbft, err := NewDBFTPolicy(timeout) 86 | assert.NotNil(t, dbft) 87 | assert.Nil(t, err) 88 | assert.Equal(t, common.DbftPolicy, dbft.name) 89 | assert.NotNil(t, dbft.core) 90 | assert.Equal(t, mockAccounts[0].Extension.Id, dbft.core.local.Extension.Id) 91 | } 92 | 93 | func TestBFTPolicy_PolicyName(t *testing.T) { 94 | dbft, _ := NewDBFTPolicy(timeout) 95 | assert.Equal(t, common.DbftPolicy, dbft.name) 96 | assert.Equal(t, dbft.name, dbft.PolicyName()) 97 | assert.Equal(t, mockAccounts[0].Extension.Id, dbft.core.local.Extension.Id) 98 | } 99 | 100 | func mockRoleAssignment(master account.Account, accounts []account.Account) map[account.Account]roleCommon.Roler { 101 | delegates := len(accounts) 102 | assignments := make(map[account.Account]roleCommon.Roler, delegates) 103 | for _, delegate := range accounts { 104 | if delegate == master { 105 | assignments[delegate] = roleCommon.Master 106 | } else { 107 | assignments[delegate] = roleCommon.Slave 108 | } 109 | } 110 | return assignments 111 | } 112 | 113 | func TestBFTPolicy_Initialization(t *testing.T) { 114 | dbft, err := NewDBFTPolicy(timeout) 115 | assert.NotNil(t, dbft) 116 | assert.Nil(t, err) 117 | dbft.Initialization(mockAccounts[3], mockAccounts[3], mockAccounts, nil, true) 118 | assert.Equal(t, dbft.core.peers, mockAccounts) 119 | assert.Equal(t, dbft.core.tolerance, uint8((len(mockAccounts)-1)/3)) 120 | assert.Equal(t, dbft.core.master, mockAccounts[3]) 121 | assert.Equal(t, 0, len(dbft.core.validator)) 122 | assert.Equal(t, 0, len(dbft.core.payloads)) 123 | assert.NotNil(t, dbft.core.signature) 124 | assert.Equal(t, 0, len(dbft.core.signature.signMap)) 125 | assert.Equal(t, 0, len(dbft.core.signature.signatures)) 126 | } 127 | 128 | func TestBFTPolicy_Start(t *testing.T) { 129 | dbft, _ := NewDBFTPolicy(timeout) 130 | var b *dbftCore 131 | monkey.PatchInstanceMethod(reflect.TypeOf(b), "Start", func(*dbftCore, account.Account) { 132 | log.Info("pass it.") 133 | return 134 | }) 135 | dbft.Start() 136 | monkey.UnpatchInstanceMethod(reflect.TypeOf(b), "Start") 137 | } 138 | 139 | var mockConsensusResult = &messages.ConsensusResult{ 140 | Signatures: mockSignset, 141 | Result: nil, 142 | } 143 | 144 | func TestBFTPolicy_ToConsensus(t *testing.T) { 145 | dbft, err := NewDBFTPolicy(timeout) 146 | assert.NotNil(t, dbft) 147 | assert.Nil(t, err) 148 | dbft.core.peers = mockAccounts 149 | monkey.Patch(utils.SendEvent, func(utils.Receiver, utils.Event) { 150 | dbft.result <- mockConsensusResult 151 | }) 152 | var b *dbftCore 153 | monkey.PatchInstanceMethod(reflect.TypeOf(b), "SendCommit", func(*dbftCore, *messages.Commit, *types.Block) { 154 | return 155 | }) 156 | proposal := &common.Proposal{ 157 | Block: &types.Block{ 158 | Header: &types.Header{ 159 | Height: 0, 160 | }, 161 | }, 162 | } 163 | assert.Equal(t, 0, len(proposal.Block.Header.SigData)) 164 | err = dbft.ToConsensus(proposal) 165 | assert.Nil(t, err) 166 | 167 | dbft.timeout = time.Duration(2) 168 | monkey.Patch(utils.SendEvent, func(utils.Receiver, utils.Event) { 169 | return 170 | }) 171 | monkey.PatchInstanceMethod(reflect.TypeOf(b), "SendCommit", func(*dbftCore, *messages.Commit, *types.Block) { 172 | return 173 | }) 174 | err = dbft.ToConsensus(proposal) 175 | assert.Equal(t, fmt.Errorf("timeout for consensus"), err) 176 | } 177 | 178 | var MockHash = types.Hash{ 179 | 0x1d, 0xcf, 0x7, 0xba, 0xfc, 0x42, 0xb0, 0x8d, 0xfd, 0x23, 0x9c, 0x45, 0xa4, 0xb9, 0x38, 0xd, 180 | 0x8d, 0xfe, 0x5d, 0x6f, 0xa7, 0xdb, 0xd5, 0x50, 0xc9, 0x25, 0xb1, 0xb3, 0x4, 0xdc, 0xc5, 0x1c, 181 | } 182 | 183 | func TestBFTPolicy_commit(t *testing.T) { 184 | mockAccount := account.Account{ 185 | Address: types.Address{0x33, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 186 | 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 187 | Extension: account.AccountExtension{ 188 | Id: 0, 189 | Url: "127.0.0.1:8080", 190 | }, 191 | } 192 | dbft, err := NewDBFTPolicy(timeout) 193 | go dbft.Start() 194 | assert.NotNil(t, dbft) 195 | assert.Nil(t, err) 196 | block := &types.Block{ 197 | Header: &types.Header{ 198 | ChainID: 1, 199 | PrevBlockHash: MockHash, 200 | StateRoot: MockHash, 201 | TxRoot: MockHash, 202 | ReceiptsRoot: MockHash, 203 | Height: 1, 204 | Timestamp: uint64(time.Now().Unix()), 205 | SigData: mockSignset[:4], 206 | }, 207 | Transactions: make([]*types.Transaction, 0), 208 | } 209 | dbft.core.peers = append(dbft.core.peers, mockAccount) 210 | dbft.commit(block, true) 211 | } 212 | 213 | func TestDBFTPolicy_GetConsensusResult(t *testing.T) { 214 | dbft, err := NewDBFTPolicy(timeout) 215 | assert.Nil(t, err) 216 | 217 | dbft.Initialization(mockAccounts[0], mockAccounts[0], mockAccounts, nil, false) 218 | dbft.core.views.viewNum = 1 219 | result := dbft.GetConsensusResult() 220 | assert.Equal(t, mockAccounts[0], result.Master) 221 | assert.Equal(t, len(mockAccounts), len(result.Participate)) 222 | assert.Equal(t, uint64(1), result.View) 223 | } 224 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 Dave Collins 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /* 18 | Package spew implements a deep pretty printer for Go data structures to aid in 19 | debugging. 20 | 21 | A quick overview of the additional features spew provides over the built-in 22 | printing facilities for Go data types are as follows: 23 | 24 | * Pointers are dereferenced and followed 25 | * Circular data structures are detected and handled properly 26 | * Custom Stringer/error interfaces are optionally invoked, including 27 | on unexported types 28 | * Custom types which only implement the Stringer/error interfaces via 29 | a pointer receiver are optionally invoked when passing non-pointer 30 | variables 31 | * Byte arrays and slices are dumped like the hexdump -C command which 32 | includes offsets, byte values in hex, and ASCII output (only when using 33 | Dump style) 34 | 35 | There are two different approaches spew allows for dumping Go data structures: 36 | 37 | * Dump style which prints with newlines, customizable indentation, 38 | and additional debug information such as types and all pointer addresses 39 | used to indirect to the final value 40 | * A custom Formatter interface that integrates cleanly with the standard fmt 41 | package and replaces %v, %+v, %#v, and %#+v to provide inline printing 42 | similar to the default %v while providing the additional functionality 43 | outlined above and passing unsupported format verbs such as %x and %q 44 | along to fmt 45 | 46 | Quick Start 47 | 48 | This section demonstrates how to quickly get started with spew. See the 49 | sections below for further details on formatting and configuration options. 50 | 51 | To dump a variable with full newlines, indentation, type, and pointer 52 | information use Dump, Fdump, or Sdump: 53 | spew.Dump(myVar1, myVar2, ...) 54 | spew.Fdump(someWriter, myVar1, myVar2, ...) 55 | str := spew.Sdump(myVar1, myVar2, ...) 56 | 57 | Alternatively, if you would prefer to use format strings with a compacted inline 58 | printing style, use the convenience wrappers Printf, Fprintf, etc with 59 | %v (most compact), %+v (adds pointer addresses), %#v (adds types), or 60 | %#+v (adds types and pointer addresses): 61 | spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) 62 | spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) 63 | spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) 64 | spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) 65 | 66 | Configuration Options 67 | 68 | Configuration of spew is handled by fields in the ConfigState type. For 69 | convenience, all of the top-level functions use a global state available 70 | via the spew.Config global. 71 | 72 | It is also possible to create a ConfigState instance that provides methods 73 | equivalent to the top-level functions. This allows concurrent configuration 74 | options. See the ConfigState documentation for more details. 75 | 76 | The following configuration options are available: 77 | * Indent 78 | String to use for each indentation level for Dump functions. 79 | It is a single space by default. A popular alternative is "\t". 80 | 81 | * MaxDepth 82 | Maximum number of levels to descend into nested data structures. 83 | There is no limit by default. 84 | 85 | * DisableMethods 86 | Disables invocation of error and Stringer interface methods. 87 | Method invocation is enabled by default. 88 | 89 | * DisablePointerMethods 90 | Disables invocation of error and Stringer interface methods on types 91 | which only accept pointer receivers from non-pointer variables. 92 | Pointer method invocation is enabled by default. 93 | 94 | * DisablePointerAddresses 95 | DisablePointerAddresses specifies whether to disable the printing of 96 | pointer addresses. This is useful when diffing data structures in tests. 97 | 98 | * DisableCapacities 99 | DisableCapacities specifies whether to disable the printing of 100 | capacities for arrays, slices, maps and channels. This is useful when 101 | diffing data structures in tests. 102 | 103 | * ContinueOnMethod 104 | Enables recursion into types after invoking error and Stringer interface 105 | methods. Recursion after method invocation is disabled by default. 106 | 107 | * SortKeys 108 | Specifies map keys should be sorted before being printed. Use 109 | this to have a more deterministic, diffable output. Note that 110 | only native types (bool, int, uint, floats, uintptr and string) 111 | and types which implement error or Stringer interfaces are 112 | supported with other types sorted according to the 113 | reflect.Value.String() output which guarantees display 114 | stability. Natural map order is used by default. 115 | 116 | * SpewKeys 117 | Specifies that, as a last resort attempt, map keys should be 118 | spewed to strings and sorted by those strings. This is only 119 | considered if SortKeys is true. 120 | 121 | Dump Usage 122 | 123 | Simply call spew.Dump with a list of variables you want to dump: 124 | 125 | spew.Dump(myVar1, myVar2, ...) 126 | 127 | You may also call spew.Fdump if you would prefer to output to an arbitrary 128 | io.Writer. For example, to dump to standard error: 129 | 130 | spew.Fdump(os.Stderr, myVar1, myVar2, ...) 131 | 132 | A third option is to call spew.Sdump to get the formatted output as a string: 133 | 134 | str := spew.Sdump(myVar1, myVar2, ...) 135 | 136 | Sample Dump Output 137 | 138 | See the Dump example for details on the setup of the types and variables being 139 | shown here. 140 | 141 | (main.Foo) { 142 | unexportedField: (*main.Bar)(0xf84002e210)({ 143 | flag: (main.Flag) flagTwo, 144 | data: (uintptr) 145 | }), 146 | ExportedField: (map[interface {}]interface {}) (len=1) { 147 | (string) (len=3) "one": (bool) true 148 | } 149 | } 150 | 151 | Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C 152 | command as shown. 153 | ([]uint8) (len=32 cap=32) { 154 | 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | 155 | 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| 156 | 00000020 31 32 |12| 157 | } 158 | 159 | Custom Formatter 160 | 161 | Spew provides a custom formatter that implements the fmt.Formatter interface 162 | so that it integrates cleanly with standard fmt package printing functions. The 163 | formatter is useful for inline printing of smaller data types similar to the 164 | standard %v format specifier. 165 | 166 | The custom formatter only responds to the %v (most compact), %+v (adds pointer 167 | addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb 168 | combinations. Any other verbs such as %x and %q will be sent to the the 169 | standard fmt package for formatting. In addition, the custom formatter ignores 170 | the width and precision arguments (however they will still work on the format 171 | specifiers not handled by the custom formatter). 172 | 173 | Custom Formatter Usage 174 | 175 | The simplest way to make use of the spew custom formatter is to call one of the 176 | convenience functions such as spew.Printf, spew.Println, or spew.Printf. The 177 | functions have syntax you are most likely already familiar with: 178 | 179 | spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) 180 | spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) 181 | spew.Println(myVar, myVar2) 182 | spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) 183 | spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) 184 | 185 | See the Index for the full list convenience functions. 186 | 187 | Sample Formatter Output 188 | 189 | Double pointer to a uint8: 190 | %v: <**>5 191 | %+v: <**>(0xf8400420d0->0xf8400420c8)5 192 | %#v: (**uint8)5 193 | %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 194 | 195 | Pointer to circular struct with a uint8 field and a pointer to itself: 196 | %v: <*>{1 <*>} 197 | %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)} 198 | %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)} 199 | %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)} 200 | 201 | See the Printf example for details on the setup of variables being shown 202 | here. 203 | 204 | Errors 205 | 206 | Since it is possible for custom Stringer/error interfaces to panic, spew 207 | detects them and handles them internally by printing the panic information 208 | inline with the output. Since spew is intended to provide deep pretty printing 209 | capabilities on structures, it intentionally does not return any errors. 210 | */ 211 | package spew 212 | -------------------------------------------------------------------------------- /consensus/common/plugin.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | "github.com/DSiSc/craft/log" 6 | "github.com/DSiSc/craft/types" 7 | "github.com/DSiSc/validator/tools/account" 8 | "sync" 9 | ) 10 | 11 | type contentState uint8 12 | 13 | const ( 14 | _ = contentState(iota) 15 | Initial 16 | InConsensus 17 | ToConsensus 18 | ) 19 | 20 | type Content struct { 21 | digest types.Hash 22 | state contentState 23 | lock sync.RWMutex 24 | signMap map[account.Account][]byte 25 | signatures [][]byte 26 | payload interface{} 27 | } 28 | 29 | func newContent(digest types.Hash, payload interface{}) *Content { 30 | return &Content{ 31 | digest: digest, 32 | state: Initial, 33 | payload: payload, 34 | signMap: make(map[account.Account][]byte), 35 | } 36 | } 37 | 38 | func (instance *Content) SetState(state contentState) error { 39 | instance.lock.Lock() 40 | if 1 != (state - instance.state) { 41 | instance.lock.Unlock() 42 | return fmt.Errorf("can not move state from %v to %v", instance.state, state) 43 | } 44 | log.Debug("set state from %v to %v.", instance.state, state) 45 | instance.state = state 46 | instance.lock.Unlock() 47 | return nil 48 | } 49 | 50 | func (instance *Content) AddSignature(account account.Account, sign []byte) bool { 51 | instance.lock.Lock() 52 | if _, ok := instance.signMap[account]; !ok { 53 | log.Info("add account %d signature.", account.Extension.Id) 54 | instance.signMap[account] = sign 55 | instance.signatures = append(instance.signatures, sign) 56 | instance.lock.Unlock() 57 | return true 58 | } 59 | instance.lock.Unlock() 60 | log.Warn("account %d signature has exist.", account.Extension.Id) 61 | return false 62 | } 63 | 64 | func (instance *Content) Signatures() [][]byte { 65 | instance.lock.RLock() 66 | defer instance.lock.RUnlock() 67 | return instance.signatures 68 | } 69 | 70 | func (instance *Content) State() contentState { 71 | instance.lock.RLock() 72 | defer instance.lock.RUnlock() 73 | return instance.state 74 | } 75 | 76 | func (instance *Content) GetSignByAccount(account account.Account) ([]byte, bool) { 77 | instance.lock.RLock() 78 | sign, ok := instance.signMap[account] 79 | instance.lock.RUnlock() 80 | return sign, ok 81 | } 82 | 83 | func (instance *Content) GetSignMap() map[account.Account][]byte { 84 | instance.lock.RLock() 85 | defer instance.lock.RUnlock() 86 | return instance.signMap 87 | } 88 | 89 | func (instance *Content) GetContentPayload() interface{} { 90 | instance.lock.RLock() 91 | defer instance.lock.RUnlock() 92 | return instance.payload 93 | } 94 | 95 | type ConsensusPlugin struct { 96 | mutex sync.RWMutex 97 | latestBlockHeight uint64 98 | content map[types.Hash]*Content 99 | } 100 | 101 | func NewConsensusPlugin() *ConsensusPlugin { 102 | return &ConsensusPlugin{ 103 | latestBlockHeight: uint64(0), 104 | content: make(map[types.Hash]*Content), 105 | } 106 | } 107 | 108 | const maxPayloadCacheNum = 10 109 | 110 | func (instance *ConsensusPlugin) Add(digest types.Hash, payload interface{}) *Content { 111 | instance.mutex.Lock() 112 | defer instance.mutex.Unlock() 113 | 114 | // delete unused cache 115 | block := payload.(*types.Block) 116 | for key, val := range instance.content { 117 | valBlock := val.payload.(*types.Block) 118 | if valBlock.Header.Height < block.Header.Height-maxPayloadCacheNum { 119 | delete(instance.content, key) 120 | } 121 | } 122 | 123 | if _, ok := instance.content[digest]; !ok { 124 | log.Info("add content %x to map to prepare consensus process.", digest) 125 | instance.content[digest] = newContent(digest, payload) 126 | return instance.content[digest] 127 | } 128 | 129 | log.Warn("content %x has exist, please confirm.", digest) 130 | return instance.content[digest] 131 | } 132 | 133 | func (instance *ConsensusPlugin) Remove(digest types.Hash) { 134 | instance.mutex.Lock() 135 | defer instance.mutex.Unlock() 136 | cachedContent := instance.content[digest] 137 | if cachedContent == nil { 138 | return 139 | } 140 | 141 | cachedBlock := cachedContent.payload.(*types.Block) 142 | for key, val := range instance.content { 143 | valBlock := val.payload.(*types.Block) 144 | if valBlock.Header.Height <= cachedBlock.Header.Height { 145 | delete(instance.content, key) 146 | } 147 | } 148 | } 149 | 150 | func (instance *ConsensusPlugin) GetContentByHash(digest types.Hash) (*Content, error) { 151 | instance.mutex.RLock() 152 | defer instance.mutex.RUnlock() 153 | if _, ok := instance.content[digest]; !ok { 154 | log.Error("content %x not exist, please confirm.", digest) 155 | return nil, fmt.Errorf("content %x not exist, please confirm", digest) 156 | } 157 | return instance.content[digest], nil 158 | } 159 | 160 | func (instance *ConsensusPlugin) GetLatestBlockHeight() uint64 { 161 | instance.mutex.RLock() 162 | defer instance.mutex.RUnlock() 163 | return instance.latestBlockHeight 164 | } 165 | 166 | func (instance *ConsensusPlugin) SetLatestBlockHeight(height uint64) { 167 | instance.mutex.Lock() 168 | instance.latestBlockHeight = height 169 | instance.mutex.Unlock() 170 | } 171 | 172 | type OnlineWizard struct { 173 | mutex sync.RWMutex 174 | blockHeight uint64 175 | blockHeightList []uint64 176 | response map[uint64]*ResponseNodes 177 | } 178 | 179 | type ResponseNodes struct { 180 | mutex sync.RWMutex 181 | nodes []account.Account 182 | node map[types.Address]account.Account 183 | state OnlineState 184 | master account.Account 185 | viewNum uint64 186 | walterLevel int 187 | } 188 | 189 | func NewResponseNodes(walterLevel int, master account.Account) *ResponseNodes { 190 | return &ResponseNodes{ 191 | node: make(map[types.Address]account.Account), 192 | state: GoOnline, 193 | master: master, 194 | walterLevel: walterLevel, 195 | nodes: make([]account.Account, 0), 196 | } 197 | } 198 | 199 | func (instance *ResponseNodes) GetResponseState() OnlineState { 200 | instance.mutex.RLock() 201 | defer instance.mutex.RUnlock() 202 | return instance.state 203 | } 204 | 205 | func (instance *ResponseNodes) AddResponseNodes(node account.Account) ([]account.Account, OnlineState) { 206 | instance.mutex.Lock() 207 | defer instance.mutex.Unlock() 208 | if _, ok := instance.node[node.Address]; !ok { 209 | instance.node[node.Address] = node 210 | instance.nodes = append(instance.nodes, node) 211 | } 212 | if len(instance.nodes) >= instance.walterLevel { 213 | log.Info("now node has reached consensus, so state from GoOnline to Online") 214 | instance.state = Online 215 | } 216 | return instance.nodes, instance.state 217 | } 218 | 219 | func NewOnlineWizard() *OnlineWizard { 220 | return &OnlineWizard{ 221 | blockHeight: DefaultBlockHeight, 222 | blockHeightList: make([]uint64, 0), 223 | response: make(map[uint64]*ResponseNodes), 224 | } 225 | } 226 | 227 | func (instance *OnlineWizard) AddOnlineResponse(blockHeight uint64, nodes []account.Account, walterLevel int, master account.Account, viewNum uint64) ([]account.Account, OnlineState) { 228 | instance.mutex.Lock() 229 | defer instance.mutex.Unlock() 230 | var nodeList []account.Account 231 | var state OnlineState 232 | if _, ok := instance.response[blockHeight]; !ok { 233 | instance.response[blockHeight] = NewResponseNodes(walterLevel, master) 234 | instance.blockHeightList = append(instance.blockHeightList, blockHeight) 235 | } 236 | if blockHeight > instance.blockHeight { 237 | log.Info("update block height from %d to %d.", instance.blockHeight, blockHeight) 238 | instance.blockHeight = blockHeight 239 | instance.response[blockHeight].master = master 240 | } 241 | if master != instance.response[blockHeight].master { 242 | log.Error("master not in agreement which exist is %d, while receive is %d.", 243 | instance.response[blockHeight].master.Extension.Id, master.Extension.Id) 244 | if instance.response[blockHeight].viewNum < viewNum { 245 | instance.response[blockHeight].master = master 246 | } else { 247 | panic(fmt.Sprintf("master not in agreement which exist is %d, while receive is %d.", 248 | instance.response[blockHeight].master.Extension.Id, master.Extension.Id)) 249 | } 250 | } 251 | if instance.response[blockHeight].viewNum < viewNum { 252 | instance.response[blockHeight].viewNum = viewNum 253 | } 254 | for _, node := range nodes { 255 | nodeList, state = instance.response[blockHeight].AddResponseNodes(node) 256 | } 257 | return nodeList, state 258 | } 259 | 260 | func (instance *OnlineWizard) GetCurrentState() OnlineState { 261 | instance.mutex.RLock() 262 | defer instance.mutex.RUnlock() 263 | return instance.response[instance.blockHeight].GetResponseState() 264 | } 265 | 266 | func (instance *OnlineWizard) GetCurrentStateByHeight(blockHeight uint64) OnlineState { 267 | instance.mutex.RLock() 268 | defer instance.mutex.RUnlock() 269 | if _, ok := instance.response[blockHeight]; !ok { 270 | log.Debug("block height %d info not exists.", blockHeight) 271 | return GoOnline 272 | } 273 | return instance.response[blockHeight].GetResponseState() 274 | } 275 | 276 | func (instance *OnlineWizard) GetCurrentHeight() uint64 { 277 | instance.mutex.RLock() 278 | defer instance.mutex.RUnlock() 279 | return instance.blockHeight 280 | } 281 | 282 | func (instance *OnlineWizard) GetMasterByBlockHeight(blockHeight uint64) account.Account { 283 | instance.mutex.RLock() 284 | defer instance.mutex.RUnlock() 285 | return instance.response[blockHeight].master 286 | } 287 | 288 | func (instance *OnlineWizard) GetResponseNodesStateByBlockHeight(blockHeight uint64) OnlineState { 289 | instance.mutex.Lock() 290 | defer instance.mutex.Unlock() 291 | if _, ok := instance.response[blockHeight]; !ok { 292 | log.Warn("never received %d response before.", blockHeight) 293 | return GoOnline 294 | } 295 | return instance.response[blockHeight].state 296 | } 297 | 298 | func (instance *OnlineWizard) DeleteOnlineResponse(blockHeight uint64) { 299 | instance.mutex.Lock() 300 | delete(instance.response, blockHeight) 301 | instance.mutex.Unlock() 302 | } 303 | -------------------------------------------------------------------------------- /consensus/messages/message.go: -------------------------------------------------------------------------------- 1 | package messages 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/DSiSc/craft/log" 8 | "github.com/DSiSc/craft/types" 9 | "github.com/DSiSc/validator/tools/account" 10 | "io" 11 | "net" 12 | "time" 13 | ) 14 | 15 | //MessageType is the message type 16 | type MessageType uint32 17 | 18 | const ( 19 | _ = MessageType(iota) // 0, nil ,message 20 | RequestMessageType // 1, request message for bft 21 | ProposalMessageType // 2, proposal message for bft 22 | ResponseMessageType // 3, response message for bft 23 | CommitMessageType // 4, commit message for bft 24 | SyncBlockReqMessageType // 5, sync block request 25 | SyncBlockRespMessageType // 6, sync block response 26 | ViewChangeMessageReqType // 7, change view request 27 | OnlineRequestType // 8, node online request 28 | OnlineResponseType // 9, response for node online request 29 | ) 30 | 31 | const connWriteTimeOut = 60 32 | 33 | type Message struct { 34 | MessageType MessageType 35 | PayLoad interface{} 36 | } 37 | 38 | type MessageHeader struct { 39 | Magic uint32 40 | MessageType MessageType 41 | Length uint32 42 | } 43 | 44 | // EncodeMessage encode message to byte array. 45 | func EncodeMessage(msg Message) ([]byte, error) { 46 | msgByte, err := json.Marshal(msg.PayLoad) 47 | if err != nil { 48 | return nil, fmt.Errorf("failed to encode message %v to json, as: %v", msg, err) 49 | } 50 | 51 | header, err := buildMessageHeader(msg, len(msgByte)) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | buf, err := encodeMessageHeader(header) 57 | if err != nil { 58 | return nil, err 59 | } 60 | 61 | return append(buf, msgByte...), nil 62 | } 63 | 64 | // fill the header according to the message. 65 | func buildMessageHeader(msg Message, len int) (*MessageHeader, error) { 66 | header := &MessageHeader{ 67 | Magic: 0, 68 | MessageType: msg.MessageType, 69 | Length: uint32(len), 70 | } 71 | return header, nil 72 | } 73 | 74 | // encodeMessageHeader encode message header to byte array. 75 | func encodeMessageHeader(header *MessageHeader) ([]byte, error) { 76 | buf := make([]byte, 12) 77 | binary.LittleEndian.PutUint32(buf, header.Magic) 78 | binary.LittleEndian.PutUint32(buf[4:], uint32(header.MessageType)) 79 | binary.LittleEndian.PutUint32(buf[8:], header.Length) 80 | return buf, nil 81 | } 82 | 83 | // ReadMessage read message 84 | func ReadMessage(reader io.Reader) (Message, error) { 85 | header, err := readMessageHeader(reader) 86 | if err != nil { 87 | return Message{}, err 88 | } 89 | 90 | body := make([]byte, header.Length) 91 | _, err = io.ReadFull(reader, body) 92 | if err != nil { 93 | return Message{}, err 94 | } 95 | 96 | return DecodeMessage(header.MessageType, body) 97 | } 98 | 99 | // read message header from reader. 100 | func readMessageHeader(reader io.Reader) (MessageHeader, error) { 101 | header := MessageHeader{} 102 | err := binary.Read(reader, binary.LittleEndian, &header) 103 | return header, err 104 | } 105 | 106 | // make empty message according to the message type 107 | func makeEmptyMessage(MessageType MessageType) (interface{}, error) { 108 | switch MessageType { 109 | case RequestMessageType: 110 | return &RequestMessage{}, nil 111 | case ProposalMessageType: 112 | return &ProposalMessage{}, nil 113 | case ResponseMessageType: 114 | return &ResponseMessage{}, nil 115 | case CommitMessageType: 116 | return &CommitMessage{}, nil 117 | case SyncBlockReqMessageType: 118 | return &SyncBlockReqMessage{}, nil 119 | case SyncBlockRespMessageType: 120 | return &SyncBlockRespMessage{}, nil 121 | case ViewChangeMessageReqType: 122 | return &ViewChangeReqMessage{}, nil 123 | case OnlineRequestType: 124 | return &OnlineRequestMessage{}, nil 125 | case OnlineResponseType: 126 | return &OnlineResponseMessage{}, nil 127 | default: 128 | return nil, fmt.Errorf("unknown message type %v", MessageType) 129 | } 130 | } 131 | 132 | func DecodeMessage(MessageType MessageType, rawMsg []byte) (Message, error) { 133 | payload, err := makeEmptyMessage(MessageType) 134 | if nil != err { 135 | return Message{}, err 136 | } 137 | err = json.Unmarshal(rawMsg, payload) 138 | if nil != err { 139 | log.Error("unmarshal rawMsg failed with err %v.", err) 140 | return Message{}, err 141 | } 142 | return Message{MessageType: MessageType, PayLoad: payload}, nil 143 | } 144 | 145 | func sendMsgByUrl(url string, msgPayload []byte) error { 146 | tcpAddr, err := net.ResolveTCPAddr("tcp4", url) 147 | if err != nil { 148 | log.Error("resolve tcp address %s occur fatal error: %v", url, err) 149 | return err 150 | } 151 | conn, err := net.DialTCP("tcp", nil, tcpAddr) 152 | if err != nil { 153 | log.Error("dial tcp with %s occur error: %s", url, err) 154 | return err 155 | } 156 | defer conn.Close() 157 | conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(connWriteTimeOut))) 158 | _, err = conn.Write(msgPayload) 159 | if nil != err { 160 | log.Error("write connection error %v.", err) 161 | } 162 | return err 163 | } 164 | 165 | type ConsensusResult struct { 166 | Signatures [][]byte 167 | Result error 168 | } 169 | 170 | // request msg 171 | type RequestMessage struct { 172 | Request *Request 173 | } 174 | 175 | type Request struct { 176 | Timestamp int64 177 | Account account.Account 178 | Payload *types.Block 179 | } 180 | 181 | // proposal msg 182 | type ProposalMessage struct { 183 | Proposal *Proposal 184 | } 185 | 186 | type Proposal struct { 187 | Account account.Account 188 | Timestamp int64 189 | Payload *types.Block 190 | Signature []byte 191 | } 192 | 193 | // response msg 194 | type ResponseMessage struct { 195 | Response *Response 196 | } 197 | 198 | type Response struct { 199 | Account account.Account 200 | Timestamp int64 201 | Digest types.Hash 202 | Signature []byte 203 | BlockHeight uint64 204 | } 205 | 206 | // online request 207 | type OnlineRequestMessage struct { 208 | OnlineRequest *OnlineRequest 209 | } 210 | 211 | type OnlineRequest struct { 212 | Account account.Account 213 | Timestamp int64 214 | BlockHeight uint64 215 | } 216 | 217 | // online response 218 | type OnlineResponseMessage struct { 219 | OnlineResponse *OnlineResponse 220 | } 221 | 222 | type OnlineResponse struct { 223 | Account account.Account 224 | Timestamp int64 225 | BlockHeight uint64 226 | ViewNum uint64 227 | Nodes []account.Account 228 | Master account.Account 229 | } 230 | 231 | // sync role assignment from other node 232 | type SyncRoleAssignmentReqMessage struct { 233 | SyncRoleAssignmentReq *SyncRoleAssignmentReq 234 | } 235 | 236 | type SyncRoleAssignmentReq struct { 237 | Account account.Account 238 | Timestamp int64 239 | ViewNum uint64 240 | } 241 | 242 | // send sync role assignment to other node 243 | type SyncRoleAssignmentRespMessage struct { 244 | SyncRoleAssignmentResp *SyncRoleAssignmentResp 245 | } 246 | 247 | type SyncRoleAssignmentResp struct { 248 | Account account.Account 249 | ViewNum uint64 250 | Master account.Account 251 | Timestamp int64 252 | } 253 | 254 | // commit msg 255 | type CommitMessage struct { 256 | Commit *Commit 257 | } 258 | 259 | type Commit struct { 260 | Account account.Account 261 | Timestamp int64 262 | BlockHash types.Hash 263 | Digest types.Hash 264 | Signatures [][]byte 265 | Result bool 266 | } 267 | 268 | // sync block request msg 269 | type SyncBlockReqMessage struct { 270 | SyncBlockReq *SyncBlockReq 271 | } 272 | 273 | type SyncBlockReq struct { 274 | Account account.Account 275 | Timestamp int64 276 | BlockStart uint64 277 | BlockEnd uint64 278 | } 279 | 280 | // sync block response msg 281 | type SyncBlockRespMessage struct { 282 | SyncBlockResp *SyncBlockResp 283 | } 284 | 285 | type SyncBlockResp struct { 286 | // TODO: add signatures 287 | Blocks []*types.Block 288 | } 289 | 290 | // change view request msg 291 | type ViewChangeReqMessage struct { 292 | ViewChange *ViewChangeReq 293 | } 294 | 295 | type ViewChangeReq struct { 296 | Account account.Account 297 | Nodes []account.Account 298 | Timestamp int64 299 | ViewNum uint64 300 | } 301 | 302 | // send msg to specified destination 303 | func Unicast(account account.Account, msgPayload []byte, MessageType MessageType, digest types.Hash) error { 304 | log.Info("send msg [type %v, digest %x] to %d with url %s.", MessageType, digest, account.Extension.Id, account.Extension.Url) 305 | err := sendMsgByUrl(account.Extension.Url, msgPayload) 306 | if nil != err { 307 | log.Error("send msg [type %v and digest %x] to %d with url %s occurs error %v.", 308 | MessageType, digest, account.Extension.Id, account.Extension.Url, err) 309 | } 310 | return err 311 | } 312 | 313 | func BroadcastPeers(msgPayload []byte, MessageType MessageType, digest types.Hash, peers []account.Account) { 314 | for _, peer := range peers { 315 | log.Info("broadcast to %d by url %s with message type %v and digest %x.", 316 | peer.Extension.Id, peer.Extension.Url, MessageType, digest) 317 | err := sendMsgByUrl(peer.Extension.Url, msgPayload) 318 | if nil != err { 319 | log.Error("broadcast to %d by url %s with message type %v and digest %x occur error %v.", 320 | peer.Extension.Id, peer.Extension.Url, MessageType, digest, err) 321 | } 322 | } 323 | } 324 | 325 | func BroadcastPeersFilter(msgPayload []byte, MessageType MessageType, digest types.Hash, peers []account.Account, black account.Account) { 326 | for _, peer := range peers { 327 | if peer != black { 328 | log.Info("broadcast to %d by url %s with message type %v and digest %x.", 329 | peer.Extension.Id, peer.Extension.Url, MessageType, digest) 330 | err := sendMsgByUrl(peer.Extension.Url, msgPayload) 331 | if nil != err { 332 | log.Error("broadcast to %d by url %s with message type %v and digest %x occur error %v.", 333 | peer.Extension.Id, peer.Extension.Url, MessageType, digest, err) 334 | } 335 | } 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /consensus/policy/solo/solo_test.go: -------------------------------------------------------------------------------- 1 | package solo 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/DSiSc/craft/log" 7 | "github.com/DSiSc/craft/types" 8 | "github.com/DSiSc/galaxy/consensus/common" 9 | "github.com/DSiSc/galaxy/consensus/config" 10 | "github.com/DSiSc/monkey" 11 | "github.com/DSiSc/validator" 12 | "github.com/DSiSc/validator/tools/account" 13 | "github.com/DSiSc/validator/tools/signature" 14 | "github.com/DSiSc/validator/tools/signature/keypair" 15 | "github.com/stretchr/testify/assert" 16 | "math" 17 | "reflect" 18 | "sync" 19 | "testing" 20 | "time" 21 | ) 22 | 23 | var events types.EventCenter 24 | 25 | type Event struct { 26 | m sync.RWMutex 27 | Subscribers map[types.EventType]map[types.Subscriber]types.EventFunc 28 | } 29 | 30 | func NewEvent() types.EventCenter { 31 | return &Event{ 32 | Subscribers: make(map[types.EventType]map[types.Subscriber]types.EventFunc), 33 | } 34 | } 35 | 36 | // adds a new subscriber to Event. 37 | func (e *Event) Subscribe(eventType types.EventType, eventFunc types.EventFunc) types.Subscriber { 38 | e.m.Lock() 39 | defer e.m.Unlock() 40 | 41 | sub := make(chan interface{}) 42 | _, ok := e.Subscribers[eventType] 43 | if !ok { 44 | e.Subscribers[eventType] = make(map[types.Subscriber]types.EventFunc) 45 | } 46 | e.Subscribers[eventType][sub] = eventFunc 47 | 48 | return sub 49 | } 50 | 51 | func (e *Event) UnSubscribe(eventType types.EventType, subscriber types.Subscriber) (err error) { 52 | e.m.Lock() 53 | defer e.m.Unlock() 54 | 55 | subEvent, ok := e.Subscribers[eventType] 56 | if !ok { 57 | err = errors.New("event type not exist") 58 | return 59 | } 60 | 61 | delete(subEvent, subscriber) 62 | close(subscriber) 63 | 64 | return 65 | } 66 | 67 | func (e *Event) Notify(eventType types.EventType, value interface{}) (err error) { 68 | 69 | e.m.RLock() 70 | defer e.m.RUnlock() 71 | 72 | subs, ok := e.Subscribers[eventType] 73 | if !ok { 74 | err = errors.New("event type not register") 75 | return 76 | } 77 | 78 | switch value.(type) { 79 | case error: 80 | log.Error("Receive errors is [%v].", value) 81 | } 82 | log.Info("Receive eventType is [%d].", eventType) 83 | 84 | for _, event := range subs { 85 | go e.NotifySubscriber(event, value) 86 | } 87 | return nil 88 | } 89 | 90 | func (e *Event) NotifySubscriber(eventFunc types.EventFunc, value interface{}) { 91 | if eventFunc == nil { 92 | return 93 | } 94 | 95 | // invoke subscriber event func 96 | eventFunc(value) 97 | 98 | } 99 | 100 | //Notify all event subscribers 101 | func (e *Event) NotifyAll() (errs []error) { 102 | e.m.RLock() 103 | defer e.m.RUnlock() 104 | 105 | for eventType, _ := range e.Subscribers { 106 | if err := e.Notify(eventType, nil); err != nil { 107 | errs = append(errs, err) 108 | } 109 | } 110 | 111 | return errs 112 | } 113 | 114 | // unsubscribe all event and subscriber elegant 115 | func (e *Event) UnSubscribeAll() { 116 | for eventtype, _ := range e.Subscribers { 117 | subs, ok := e.Subscribers[eventtype] 118 | if !ok { 119 | continue 120 | } 121 | for subscriber, _ := range subs { 122 | delete(subs, subscriber) 123 | close(subscriber) 124 | } 125 | } 126 | // TODO: open it when txswitch and blkswith stop complete 127 | //e.Subscribers = make(map[types.EventType]map[types.Subscriber]types.EventFunc) 128 | return 129 | } 130 | 131 | var mockAccounts = []account.Account{ 132 | account.Account{ 133 | Address: types.Address{0x33, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 134 | 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 135 | Extension: account.AccountExtension{ 136 | Id: 0, 137 | Url: "172.0.0.1:8080", 138 | }, 139 | }, 140 | account.Account{ 141 | Address: types.Address{0x34, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 142 | 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 143 | Extension: account.AccountExtension{ 144 | Id: 1, 145 | Url: "172.0.0.1:8081"}, 146 | }, 147 | account.Account{ 148 | Address: types.Address{0x35, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 149 | Extension: account.AccountExtension{ 150 | Id: 2, 151 | Url: "172.0.0.1:8082", 152 | }, 153 | }, 154 | 155 | account.Account{ 156 | Address: types.Address{0x36, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 157 | Extension: account.AccountExtension{ 158 | Id: 3, 159 | Url: "172.0.0.1:8083", 160 | }, 161 | }, 162 | } 163 | 164 | func mock_proposal() *common.Proposal { 165 | return &common.Proposal{ 166 | Block: &types.Block{ 167 | Header: &types.Header{ 168 | Height: 1, 169 | SigData: make([][]byte, 0), 170 | }, 171 | }, 172 | } 173 | } 174 | 175 | func mock_solo_proposal() *SoloProposal { 176 | return &SoloProposal{ 177 | proposal: nil, 178 | version: 0, 179 | status: common.Proposing, 180 | } 181 | } 182 | 183 | var MockSignatureVerifySwitch = config.SignatureVerifySwitch{ 184 | SyncVerifySignature: true, 185 | LocalVerifySignature: true, 186 | } 187 | 188 | func TestNewSoloPolicy(t *testing.T) { 189 | asserts := assert.New(t) 190 | sp, err := NewSoloPolicy(nil, true, MockSignatureVerifySwitch) 191 | asserts.Nil(err) 192 | asserts.Equal(uint8(common.SoloConsensusNum), sp.tolerance) 193 | asserts.Equal(common.SoloPolicy, sp.name) 194 | } 195 | 196 | func Test_toSoloProposal(t *testing.T) { 197 | asserts := assert.New(t) 198 | p := mock_proposal() 199 | sp, _ := NewSoloPolicy(nil, true, MockSignatureVerifySwitch) 200 | proposal := sp.toSoloProposal(p) 201 | asserts.NotNil(proposal) 202 | asserts.Equal(common.Proposing, proposal.status) 203 | asserts.Equal(common.Version(1), proposal.version) 204 | asserts.NotNil(proposal.proposal) 205 | } 206 | 207 | func Test_prepareConsensus(t *testing.T) { 208 | asserts := assert.New(t) 209 | sp, _ := NewSoloPolicy(nil, true, MockSignatureVerifySwitch) 210 | proposal := mock_solo_proposal() 211 | 212 | err := sp.prepareConsensus(proposal) 213 | asserts.NotNil(err) 214 | 215 | proposal.version = 1 216 | err = sp.prepareConsensus(proposal) 217 | asserts.Nil(err) 218 | asserts.Equal(common.Propose, proposal.status) 219 | 220 | proposal.status = common.Propose 221 | err = sp.prepareConsensus(proposal) 222 | asserts.NotNil(err) 223 | 224 | sp.version = math.MaxUint64 225 | proposal = sp.toSoloProposal(nil) 226 | err = sp.prepareConsensus(proposal) 227 | asserts.Nil(err) 228 | } 229 | 230 | func Test_submitConsensus(t *testing.T) { 231 | asserts := assert.New(t) 232 | proposal := mock_solo_proposal() 233 | sp, _ := NewSoloPolicy(nil, true, MockSignatureVerifySwitch) 234 | err := sp.submitConsensus(proposal) 235 | asserts.NotNil(err) 236 | asserts.Equal(err, fmt.Errorf("proposal status must be Propose")) 237 | 238 | proposal.status = common.Propose 239 | err = sp.submitConsensus(proposal) 240 | asserts.Nil(err) 241 | asserts.Equal(common.Committed, proposal.status) 242 | } 243 | 244 | func TestSoloPolicy_ToConsensus(t *testing.T) { 245 | asserts := assert.New(t) 246 | event := NewEvent() 247 | blockSwitch := make(chan interface{}) 248 | var subscriber1 types.EventFunc = func(v interface{}) { 249 | log.Info("TEST: consensus failed event func.") 250 | } 251 | sub1 := event.Subscribe(types.EventConsensusFailed, subscriber1) 252 | assert.NotNil(t, sub1) 253 | proposal := mock_proposal() 254 | sp, _ := NewSoloPolicy(blockSwitch, true, MockSignatureVerifySwitch) 255 | sp.Initialization(mockAccounts[0], mockAccounts[0], mockAccounts[:1], event, false) 256 | 257 | err := sp.ToConsensus(proposal) 258 | asserts.Equal(err, fmt.Errorf("local verify failed")) 259 | 260 | var v *validator.Validator 261 | monkey.PatchInstanceMethod(reflect.TypeOf(v), "ValidateBlock", func(*validator.Validator, *types.Block, bool) (*types.Header, error) { 262 | return nil, nil 263 | }) 264 | var fakeSignature = []byte{ 265 | 0x33, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 266 | 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d, 267 | } 268 | proposal.Block.Header.SigData = append(proposal.Block.Header.SigData, fakeSignature) 269 | monkey.Patch(signature.Verify, func(keypair.PublicKey, []byte) (types.Address, error) { 270 | return mockAccounts[2].Address, fmt.Errorf("invalid signature") 271 | }) 272 | err = sp.ToConsensus(proposal) 273 | asserts.Equal(err, fmt.Errorf("not enough valid signature")) 274 | 275 | monkey.Patch(signature.Verify, func(keypair.PublicKey, []byte) (types.Address, error) { 276 | return mockAccounts[2].Address, nil 277 | }) 278 | err = sp.ToConsensus(proposal) 279 | asserts.Equal(err, fmt.Errorf("absence self signature")) 280 | 281 | monkey.Patch(signature.Verify, func(keypair.PublicKey, []byte) (types.Address, error) { 282 | return mockAccounts[0].Address, nil 283 | }) 284 | go func() { 285 | err = sp.ToConsensus(proposal) 286 | assert.Nil(t, err) 287 | }() 288 | block := <-blockSwitch 289 | asserts.NotNil(block) 290 | monkey.UnpatchAll() 291 | } 292 | 293 | func TestSolo_prepareConsensus(t *testing.T) { 294 | asserts := assert.New(t) 295 | sp, _ := NewSoloPolicy(nil, true, MockSignatureVerifySwitch) 296 | sp.version = math.MaxUint64 297 | proposal := sp.toSoloProposal(nil) 298 | err := sp.prepareConsensus(proposal) 299 | asserts.Nil(err) 300 | } 301 | 302 | func TestSoloPolicy_PolicyName(t *testing.T) { 303 | sp, err := NewSoloPolicy(nil, true, MockSignatureVerifySwitch) 304 | assert.Nil(t, err) 305 | assert.NotNil(t, sp) 306 | assert.Equal(t, common.SoloPolicy, sp.PolicyName()) 307 | } 308 | 309 | func Test_toConsensus(t *testing.T) { 310 | sp, _ := NewSoloPolicy(nil, true, MockSignatureVerifySwitch) 311 | err := sp.toConsensus(nil) 312 | assert.Equal(t, false, err) 313 | } 314 | 315 | func TestSoloPolicy_Start(t *testing.T) { 316 | sp, _ := NewSoloPolicy(nil, true, MockSignatureVerifySwitch) 317 | sp.Start() 318 | } 319 | 320 | func TestSoloPolicy_Halt(t *testing.T) { 321 | sp, _ := NewSoloPolicy(nil, true, MockSignatureVerifySwitch) 322 | sp.Halt() 323 | } 324 | 325 | func TestSoloPolicy_Initialization(t *testing.T) { 326 | sp, _ := NewSoloPolicy(nil, true, MockSignatureVerifySwitch) 327 | sp.Initialization(mockAccounts[0], mockAccounts[0], mockAccounts[:1], nil, true) 328 | } 329 | 330 | func TestSoloPolicy_GetConsensusResult(t *testing.T) { 331 | mm := time.NewTimer(3 * time.Second) 332 | go waitTime(mm) 333 | time.Sleep(1 * time.Second) 334 | fmt.Print("1 .\n") 335 | mm.Reset(5 * time.Second) 336 | time.Sleep(3 * time.Second) 337 | fmt.Print("2 .\n") 338 | } 339 | 340 | func waitTime(timer *time.Timer) { 341 | for { 342 | select { 343 | case <-timer.C: 344 | fmt.Print("time out.\n") 345 | } 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /consensus/messages/message_test.go: -------------------------------------------------------------------------------- 1 | package messages 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "github.com/DSiSc/craft/types" 7 | "github.com/DSiSc/monkey" 8 | "github.com/DSiSc/validator/tools/account" 9 | "github.com/stretchr/testify/assert" 10 | "net" 11 | "reflect" 12 | "testing" 13 | "time" 14 | ) 15 | 16 | func TestEncodeMessage(t *testing.T) { 17 | request := &RequestMessage{ 18 | Request: &Request{ 19 | Timestamp: int64(0), 20 | Payload: &types.Block{ 21 | Header: &types.Header{ 22 | Height: uint64(1), 23 | }, 24 | }, 25 | }, 26 | } 27 | msg := Message{ 28 | MessageType: RequestMessageType, 29 | PayLoad: request, 30 | } 31 | rawData, err := EncodeMessage(msg) 32 | assert.Nil(t, err) 33 | assert.NotNil(t, rawData) 34 | 35 | ttt, err := DecodeMessage(RequestMessageType, rawData[12:]) 36 | assert.Nil(t, err) 37 | assert.Equal(t, ttt, msg) 38 | } 39 | 40 | var mockAccounts = []account.Account{ 41 | account.Account{ 42 | Address: types.Address{0x33, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 43 | 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 44 | Extension: account.AccountExtension{ 45 | Id: 0, 46 | Url: "172.0.0.1:8080", 47 | }, 48 | }, 49 | account.Account{ 50 | Address: types.Address{0x34, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 51 | 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 52 | Extension: account.AccountExtension{ 53 | Id: 1, 54 | Url: "172.0.0.1:8081"}, 55 | }, 56 | account.Account{ 57 | Address: types.Address{0x35, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 58 | Extension: account.AccountExtension{ 59 | Id: 2, 60 | Url: "172.0.0.1:8082", 61 | }, 62 | }, 63 | 64 | account.Account{ 65 | Address: types.Address{0x36, 0x3c, 0x33, 0x10, 0x82, 0x4b, 0x7c, 0x68, 0x51, 0x33, 0xf2, 0xbe, 0xdb, 0x2c, 0xa4, 0xb8, 0xb4, 0xdf, 0x63, 0x3d}, 66 | Extension: account.AccountExtension{ 67 | Id: 3, 68 | Url: "172.0.0.1:8083", 69 | }, 70 | }, 71 | } 72 | 73 | var mockHash = types.Hash{ 74 | 0xbd, 0x79, 0x1d, 0x4a, 0xf9, 0x64, 0x8f, 0xc3, 0x7f, 0x94, 0xeb, 0x36, 0x53, 0x19, 0xf6, 0xd0, 75 | 0xa9, 0x78, 0x9f, 0x9c, 0x22, 0x47, 0x2c, 0xa7, 0xa6, 0x12, 0xa9, 0xca, 0x4, 0x13, 0xc1, 0x4, 76 | } 77 | 78 | func TestEncodeMessage2(t *testing.T) { 79 | response := Message{ 80 | MessageType: ResponseMessageType, 81 | PayLoad: &ResponseMessage{ 82 | Response: &Response{ 83 | Account: mockAccounts[0], 84 | Timestamp: time.Now().Unix(), 85 | Digest: mockHash, 86 | Signature: mockHash[:], 87 | }, 88 | }, 89 | } 90 | msgRaw, err := EncodeMessage(response) 91 | assert.NotNil(t, msgRaw) 92 | assert.Nil(t, err) 93 | } 94 | 95 | func TestNewBFTCore_broadcast(t *testing.T) { 96 | monkey.Patch(net.ResolveTCPAddr, func(string, string) (*net.TCPAddr, error) { 97 | return nil, fmt.Errorf("resolve error") 98 | }) 99 | BroadcastPeers(nil, ProposalMessageType, mockHash, mockAccounts) 100 | 101 | monkey.Patch(net.ResolveTCPAddr, func(string, string) (*net.TCPAddr, error) { 102 | return nil, nil 103 | }) 104 | monkey.Patch(net.DialTCP, func(string, *net.TCPAddr, *net.TCPAddr) (*net.TCPConn, error) { 105 | return nil, fmt.Errorf("dail error") 106 | }) 107 | BroadcastPeers(nil, ProposalMessageType, mockHash, mockAccounts) 108 | 109 | var c net.TCPConn 110 | monkey.Patch(net.DialTCP, func(string, *net.TCPAddr, *net.TCPAddr) (*net.TCPConn, error) { 111 | return &c, nil 112 | }) 113 | monkey.PatchInstanceMethod(reflect.TypeOf(&c), "Write", func(*net.TCPConn, []byte) (int, error) { 114 | return 0, nil 115 | }) 116 | BroadcastPeers(nil, ProposalMessageType, mockHash, mockAccounts) 117 | 118 | monkey.UnpatchAll() 119 | } 120 | 121 | func TestBroadcastPeersFilter(t *testing.T) { 122 | monkey.Patch(net.ResolveTCPAddr, func(string, string) (*net.TCPAddr, error) { 123 | return nil, fmt.Errorf("resolve error") 124 | }) 125 | BroadcastPeersFilter(nil, ProposalMessageType, mockHash, mockAccounts, mockAccounts[1]) 126 | 127 | monkey.Patch(net.ResolveTCPAddr, func(string, string) (*net.TCPAddr, error) { 128 | return nil, nil 129 | }) 130 | monkey.Patch(net.DialTCP, func(string, *net.TCPAddr, *net.TCPAddr) (*net.TCPConn, error) { 131 | return nil, fmt.Errorf("dail error") 132 | }) 133 | BroadcastPeersFilter(nil, ProposalMessageType, mockHash, mockAccounts, mockAccounts[1]) 134 | 135 | var c net.TCPConn 136 | monkey.Patch(net.DialTCP, func(string, *net.TCPAddr, *net.TCPAddr) (*net.TCPConn, error) { 137 | return &c, nil 138 | }) 139 | monkey.PatchInstanceMethod(reflect.TypeOf(&c), "Write", func(*net.TCPConn, []byte) (int, error) { 140 | return 0, nil 141 | }) 142 | BroadcastPeersFilter(nil, ProposalMessageType, mockHash, mockAccounts, mockAccounts[1]) 143 | 144 | monkey.UnpatchAll() 145 | } 146 | 147 | func TestBftCore_unicast(t *testing.T) { 148 | monkey.Patch(net.ResolveTCPAddr, func(string, string) (*net.TCPAddr, error) { 149 | return nil, fmt.Errorf("resolve error") 150 | }) 151 | err := Unicast(mockAccounts[1], nil, ProposalMessageType, mockHash) 152 | assert.Equal(t, fmt.Errorf("resolve error"), err) 153 | 154 | monkey.Patch(net.ResolveTCPAddr, func(string, string) (*net.TCPAddr, error) { 155 | return nil, nil 156 | }) 157 | var c net.TCPConn 158 | monkey.Patch(net.DialTCP, func(string, *net.TCPAddr, *net.TCPAddr) (*net.TCPConn, error) { 159 | return &c, nil 160 | }) 161 | monkey.PatchInstanceMethod(reflect.TypeOf(&c), "Write", func(*net.TCPConn, []byte) (int, error) { 162 | return 10, nil 163 | }) 164 | err = Unicast(mockAccounts[1], nil, ProposalMessageType, mockHash) 165 | assert.NotNil(t, err) 166 | 167 | monkey.UnpatchAll() 168 | } 169 | 170 | func mockBlocks(num int) []*types.Block { 171 | blocks := make([]*types.Block, 0) 172 | for index := 0; index < num; index++ { 173 | block := &types.Block{ 174 | Header: &types.Header{ 175 | Height: uint64(index), 176 | }, 177 | } 178 | blocks = append(blocks, block) 179 | } 180 | return blocks 181 | } 182 | 183 | func TestReadMessage(t *testing.T) { 184 | request := Message{ 185 | MessageType: RequestMessageType, 186 | PayLoad: &RequestMessage{ 187 | Request: &Request{ 188 | Account: mockAccounts[0], 189 | Timestamp: time.Now().Unix(), 190 | Payload: mockBlocks(1)[0], 191 | }, 192 | }, 193 | } 194 | msgRaw, err := EncodeMessage(request) 195 | assert.NotNil(t, msgRaw) 196 | assert.Nil(t, err) 197 | r := bytes.NewReader(msgRaw) 198 | message, err := ReadMessage(r) 199 | assert.Nil(t, err) 200 | assert.Equal(t, request, message) 201 | 202 | response := Message{ 203 | MessageType: ProposalMessageType, 204 | PayLoad: &ProposalMessage{ 205 | Proposal: &Proposal{ 206 | Account: mockAccounts[0], 207 | Timestamp: time.Now().Unix(), 208 | Signature: mockHash[:], 209 | Payload: mockBlocks(1)[0], 210 | }, 211 | }, 212 | } 213 | msgRaw, err = EncodeMessage(response) 214 | assert.NotNil(t, msgRaw) 215 | assert.Nil(t, err) 216 | r = bytes.NewReader(msgRaw) 217 | message, err = ReadMessage(r) 218 | assert.Nil(t, err) 219 | assert.Equal(t, response, message) 220 | 221 | response = Message{ 222 | MessageType: ResponseMessageType, 223 | PayLoad: &ResponseMessage{ 224 | Response: &Response{ 225 | Account: mockAccounts[0], 226 | Timestamp: time.Now().Unix(), 227 | Digest: mockHash, 228 | Signature: mockHash[:], 229 | BlockHeight: uint64(1), 230 | }, 231 | }, 232 | } 233 | msgRaw, err = EncodeMessage(response) 234 | assert.NotNil(t, msgRaw) 235 | assert.Nil(t, err) 236 | r = bytes.NewReader(msgRaw) 237 | message, err = ReadMessage(r) 238 | assert.Nil(t, err) 239 | assert.Equal(t, response, message) 240 | 241 | commit := Message{ 242 | MessageType: CommitMessageType, 243 | PayLoad: &CommitMessage{ 244 | Commit: &Commit{ 245 | Account: mockAccounts[0], 246 | Timestamp: time.Now().Unix(), 247 | BlockHash: mockHash, 248 | Digest: mockHash, 249 | Signatures: [][]byte{mockHash[:], mockHash[:]}, 250 | Result: false, 251 | }, 252 | }, 253 | } 254 | msgRaw, err = EncodeMessage(commit) 255 | assert.NotNil(t, msgRaw) 256 | assert.Nil(t, err) 257 | r = bytes.NewReader(msgRaw) 258 | message, err = ReadMessage(r) 259 | assert.Nil(t, err) 260 | assert.Equal(t, commit, message) 261 | 262 | syncBlockReq := Message{ 263 | MessageType: SyncBlockReqMessageType, 264 | PayLoad: &SyncBlockReqMessage{ 265 | SyncBlockReq: &SyncBlockReq{ 266 | Account: mockAccounts[0], 267 | Timestamp: time.Now().Unix(), 268 | BlockStart: uint64(1), 269 | BlockEnd: uint64(2), 270 | }, 271 | }, 272 | } 273 | msgRaw, err = EncodeMessage(syncBlockReq) 274 | assert.NotNil(t, msgRaw) 275 | assert.Nil(t, err) 276 | r = bytes.NewReader(msgRaw) 277 | message, err = ReadMessage(r) 278 | assert.Nil(t, err) 279 | assert.Equal(t, syncBlockReq, message) 280 | 281 | syncBlockResp := Message{ 282 | MessageType: SyncBlockRespMessageType, 283 | PayLoad: &SyncBlockRespMessage{ 284 | SyncBlockResp: &SyncBlockResp{ 285 | Blocks: mockBlocks(10), 286 | }, 287 | }, 288 | } 289 | msgRaw, err = EncodeMessage(syncBlockResp) 290 | assert.NotNil(t, msgRaw) 291 | assert.Nil(t, err) 292 | r = bytes.NewReader(msgRaw) 293 | message, err = ReadMessage(r) 294 | assert.Nil(t, err) 295 | assert.Equal(t, syncBlockResp, message) 296 | 297 | viewChangeReq := Message{ 298 | MessageType: ViewChangeMessageReqType, 299 | PayLoad: &ViewChangeReqMessage{ 300 | ViewChange: &ViewChangeReq{ 301 | Account: mockAccounts[0], 302 | Nodes: mockAccounts, 303 | Timestamp: time.Now().Unix(), 304 | ViewNum: uint64(1), 305 | }, 306 | }, 307 | } 308 | msgRaw, err = EncodeMessage(viewChangeReq) 309 | assert.NotNil(t, msgRaw) 310 | assert.Nil(t, err) 311 | r = bytes.NewReader(msgRaw) 312 | message, err = ReadMessage(r) 313 | assert.Nil(t, err) 314 | assert.Equal(t, viewChangeReq, message) 315 | 316 | onlineRequest := Message{ 317 | MessageType: OnlineRequestType, 318 | PayLoad: &OnlineRequestMessage{ 319 | OnlineRequest: &OnlineRequest{ 320 | Account: mockAccounts[0], 321 | Timestamp: time.Now().Unix(), 322 | BlockHeight: uint64(1), 323 | }, 324 | }, 325 | } 326 | msgRaw, err = EncodeMessage(onlineRequest) 327 | assert.NotNil(t, msgRaw) 328 | assert.Nil(t, err) 329 | r = bytes.NewReader(msgRaw) 330 | message, err = ReadMessage(r) 331 | assert.Nil(t, err) 332 | assert.Equal(t, onlineRequest, message) 333 | 334 | onlineResponse := Message{ 335 | MessageType: OnlineResponseType, 336 | PayLoad: &OnlineResponseMessage{ 337 | OnlineResponse: &OnlineResponse{ 338 | Account: mockAccounts[0], 339 | Timestamp: time.Now().Unix(), 340 | BlockHeight: uint64(1), 341 | Nodes: mockAccounts, 342 | ViewNum: uint64(1), 343 | Master: mockAccounts[1], 344 | }, 345 | }, 346 | } 347 | msgRaw, err = EncodeMessage(onlineResponse) 348 | assert.NotNil(t, msgRaw) 349 | assert.Nil(t, err) 350 | r = bytes.NewReader(msgRaw) 351 | message, err = ReadMessage(r) 352 | assert.Nil(t, err) 353 | assert.Equal(t, onlineResponse, message) 354 | } 355 | --------------------------------------------------------------------------------