diff --git a/.gitignore b/.gitignore index ec04931d..e49a4707 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ # git config --global core.excludesfile ~/.gitignore_global build/bin build/_workspace +build/test_sdk *.bak diff --git a/.goreleaser.yml b/.goreleaser.yml index 2f66235e..79ce6918 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,4 +1,19 @@ -# This is an example goreleaser.yaml file with some sane defaults. +# Copyright 2018 The Fractal Team Authors +# This file is part of the fractal project. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + # Make sure to check the documentation at http://goreleaser.com builds: - binary: ft @@ -10,15 +25,6 @@ builds: goarch: - amd64 - 386 -- binary: ftkey - main: ./cmd/ftkey - goos: - - windows - - darwin - - linux - goarch: - - amd64 - - 386 - binary: ftfinder main: ./cmd/ftfinder goos: diff --git a/Makefile b/Makefile index a4affb81..30baea3a 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ SHELL:=/bin/bash REPO := $(shell pwd) GOFILES_NOVENDOR := $(shell go list -f "{{.Dir}}" ./...) -PACKAGES_NOVENDOR := $(shell go list ./...) +PACKAGES_NOVENDOR := $(shell go list ./... | grep -v test) WORK_SPACE := ${REPO}/build/_workspace FT_DIR :=${WORK_SPACE}/src/github.com/fractalplatform TEMP_GOPATH := $(GOPATH) @@ -50,6 +50,13 @@ fmt: @echo "Correcting any formatting style corrections." @gofmt -l -w ${GOFILES_NOVENDOR} +# vet runs extended compilation checks to find recommendations for +# suspicious code constructs. +.PHONY: vet +vet: + @echo "Running go vet." + @go vet ${PACKAGES_NOVENDOR} + ### Building project # Output commit_hash but only if we have the git repo (e.g. not in docker build @@ -64,7 +71,7 @@ build_workspace: # build all targets .PHONY: all -all:check build_workspace build_ft build_ftkey build_ftfinder +all:check build_workspace build_ft build_ftfinder # build ft .PHONY: build_ft @@ -72,11 +79,6 @@ build_ft: commit_hash check build_workspace @echo "Building ft." $(call build,ft) -# build ftkey -.PHONY: build_ftkey -build_ftkey: commit_hash check build_workspace - @echo "Building ftkey." - $(call build,ftkey) # build ftfinder .PHONY: build_ftfinder @@ -87,7 +89,7 @@ build_ftfinder: commit_hash check build_workspace ### Test .PHONY: test -test: build_workspace +test: all @cd ${FT_DIR}/fractal && scripts/test.sh .PHONY: test_win @@ -121,13 +123,15 @@ docs: CHANGELOG NOTES # Tag the current HEAD commit with the current release defined in .PHONY: tag_release -tag_release: test check docs all +tag_release: test check docs @scripts/tag_release.sh .PHONY: release -release: test check docs all +release: test check docs @scripts/is_checkout_dirty.sh || (echo "checkout is dirty so not releasing!" && exit 1) @export GOPATH=${TEMP_GOPATH} && scripts/release.sh - - +.PHONY: tmp_release +tmp_release: test check + @echo "Building and releasing" + @export GOPATH=${TEMP_GOPATH} && goreleaser --snapshot --rm-dist \ No newline at end of file diff --git a/accountmanager/account.go b/accountmanager/account.go index e86c5ee7..9f8931d8 100644 --- a/accountmanager/account.go +++ b/accountmanager/account.go @@ -17,8 +17,10 @@ package accountmanager import ( + "fmt" "math/big" + "github.com/ethereum/go-ethereum/log" "github.com/fractalplatform/fractal/asset" "github.com/fractalplatform/fractal/common" "github.com/fractalplatform/fractal/crypto" @@ -30,6 +32,17 @@ type AssetBalance struct { Balance *big.Int `json:"balance"` } +type recoverActionResult struct { + acctAuthors map[common.Name]*accountAuthor +} + +type accountAuthor struct { + threshold uint64 + updateAuthorThreshold uint64 + version uint64 + indexWeight map[uint64]uint64 +} + func newAssetBalance(assetID uint64, amount *big.Int) *AssetBalance { ab := AssetBalance{ AssetID: assetID, @@ -41,16 +54,22 @@ func newAssetBalance(assetID uint64, amount *big.Int) *AssetBalance { //Account account object type Account struct { //LastTime *big.Int - AcctName common.Name `json:"accountName"` - Founder common.Name `json:"founder"` - ChargeRatio uint64 `json:"chargeRatio"` - Nonce uint64 `json:"nonce"` - PublicKey common.PubKey `json:"publicKey"` - Code []byte `json:"code"` - CodeHash common.Hash `json:"codeHash"` - CodeSize uint64 `json:"codeSize"` + AcctName common.Name `json:"accountName"` + Founder common.Name `json:"founder"` + AccountID uint64 `json:"accountID"` + Number uint64 `json:"number"` + ChargeRatio uint64 `json:"chargeRatio"` + Nonce uint64 `json:"nonce"` + Code []byte `json:"code"` + CodeHash common.Hash `json:"codeHash"` + CodeSize uint64 `json:"codeSize"` + Threshold uint64 `json:"threshold"` + UpdateAuthorThreshold uint64 `json:"updateAuthorThreshold"` + AuthorVersion uint64 `json:"authorVersion"` //sort by asset id asc Balances []*AssetBalance `json:"balances"` + //realated account, pubkey and address + Authors []*common.Author `json:"authors"` //code Suicide Suicide bool `json:"suicide"` //account destroy @@ -59,25 +78,40 @@ type Account struct { // NewAccount create a new account object. func NewAccount(accountName common.Name, founderName common.Name, pubkey common.PubKey) (*Account, error) { - if !common.IsValidName(accountName.String()) { + if !common.IsValidAccountName(accountName.String()) { return nil, ErrAccountNameInvalid } + auth := common.NewAuthor(pubkey, 1) acctObject := Account{ - AcctName: accountName, - Founder: founderName, - ChargeRatio: 0, - PublicKey: pubkey, - Nonce: 0, - Balances: make([]*AssetBalance, 0), - Code: make([]byte, 0), - CodeHash: crypto.Keccak256Hash(nil), - Suicide: false, - Destroy: false, + AcctName: accountName, + Founder: founderName, + AccountID: 0, + Number: 0, + ChargeRatio: 0, + Nonce: 0, + Balances: make([]*AssetBalance, 0), + Code: make([]byte, 0), + CodeHash: crypto.Keccak256Hash(nil), + Threshold: 1, + UpdateAuthorThreshold: 1, + AuthorVersion: 1, + Authors: []*common.Author{auth}, + Suicide: false, + Destroy: false, } return &acctObject, nil } +//HaveCode check account have code +func (a *Account) HaveCode() bool { + if a.GetCodeSize() == 0 { + return false + } + return true +} + +// IsEmpty check account empty func (a *Account) IsEmpty() bool { if a.GetCodeSize() == 0 && len(a.Balances) == 0 && a.Nonce == 0 { return true @@ -90,18 +124,42 @@ func (a *Account) GetName() common.Name { return a.AcctName } +//GetFounder return account object founder func (a *Account) GetFounder() common.Name { return a.Founder } +//SetFounder set account object founder func (a *Account) SetFounder(f common.Name) { a.Founder = f } +//GetAccountID return account object id +func (a *Account) GetAccountID() uint64 { + return a.AccountID +} + +//SetAccountID set account object id +func (a *Account) SetAccountID(id uint64) { + a.AccountID = id +} + +//GetAccountNumber return account object number +func (a *Account) GetAccountNumber() uint64 { + return a.Number +} + +//SetAccountNumber set account object number +func (a *Account) SetAccountNumber(number uint64) { + a.Number = number +} + +//GetChargeRatio return account charge ratio func (a *Account) GetChargeRatio() uint64 { return a.ChargeRatio } +//SetChargeRatio set account object charge ratio func (a *Account) SetChargeRatio(ra uint64) { a.ChargeRatio = ra } @@ -116,14 +174,12 @@ func (a *Account) SetNonce(nonce uint64) { a.Nonce = nonce } -//GetPubKey get bugkey -func (a *Account) GetPubKey() common.PubKey { - return a.PublicKey +func (a *Account) GetAuthorVersion() uint64 { + return a.AuthorVersion } -//SetPubKey set pub key -func (a *Account) SetPubKey(pubkey common.PubKey) { - a.PublicKey.SetBytes(pubkey.Bytes()) +func (a *Account) SetAuthorVersion() { + a.AuthorVersion++ } //GetCode get code @@ -150,6 +206,52 @@ func (a *Account) SetCode(code []byte) error { return nil } +func (a *Account) GetThreshold() uint64 { + return a.Threshold +} + +func (a *Account) SetThreshold(t uint64) { + a.Threshold = t +} + +func (a *Account) SetUpdateAuthorThreshold(t uint64) { + a.UpdateAuthorThreshold = t +} + +func (a *Account) GetUpdateAuthorThreshold() uint64 { + return a.UpdateAuthorThreshold +} + +func (a *Account) AddAuthor(author *common.Author) error { + for _, auth := range a.Authors { + if author.Owner.String() == auth.Owner.String() { + return fmt.Errorf("%s already exist", auth.Owner.String()) + } + } + a.Authors = append(a.Authors, author) + return nil +} + +func (a *Account) UpdateAuthor(author *common.Author) error { + for _, auth := range a.Authors { + if author.Owner.String() == auth.Owner.String() { + auth.Weight = author.Weight + break + } + } + return nil +} + +func (a *Account) DeleteAuthor(author *common.Author) error { + for i, auth := range a.Authors { + if author.Owner.String() == auth.Owner.String() { + a.Authors = append(a.Authors[:i], a.Authors[i+1:]...) + break + } + } + return nil +} + // GetCodeHash get code hash func (a *Account) GetCodeHash() (common.Hash, error) { if len(a.CodeHash) == 0 { @@ -166,6 +268,7 @@ func (a *Account) GetBalanceByID(assetID uint64) (*big.Int, error) { if p, find := a.binarySearch(assetID); find == true { return a.Balances[p].Balance, nil } + log.Debug("get balance by ID", "err", ErrAccountAssetNotExist, "account", a.AcctName, "asset", assetID) return big.NewInt(0), ErrAccountAssetNotExist } @@ -310,13 +413,13 @@ func (a *Account) SetSuicide() { a.Suicide = true } -//IsDestoryed is destoryed -func (a *Account) IsDestoryed() bool { +//IsDestroyed is destroyed +func (a *Account) IsDestroyed() bool { return a.Destroy } -//SetDestory set destory -func (a *Account) SetDestory() { +//SetDestroy set destroy +func (a *Account) SetDestroy() { //just make a sign now a.Destroy = true } diff --git a/accountmanager/account_test.go b/accountmanager/account_test.go index b79bab4e..1dc903a9 100644 --- a/accountmanager/account_test.go +++ b/accountmanager/account_test.go @@ -90,15 +90,14 @@ func TestAccount_GetName(t *testing.T) { } for _, tt := range tests { a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, + AcctName: tt.fields.AcctName, + Nonce: tt.fields.Nonce, + Code: tt.fields.Code, + CodeHash: tt.fields.CodeHash, + CodeSize: tt.fields.CodeSize, + Balances: tt.fields.Balances, + Suicide: tt.fields.Suicide, + Destroy: tt.fields.Destroy, } if got := a.GetName(); !reflect.DeepEqual(got, tt.want) { t.Errorf("%q. Account.GetName() = %v, want %v", tt.name, got, tt.want) @@ -127,15 +126,14 @@ func TestAccount_GetNonce(t *testing.T) { } for _, tt := range tests { a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, + AcctName: tt.fields.AcctName, + Nonce: tt.fields.Nonce, + Code: tt.fields.Code, + CodeHash: tt.fields.CodeHash, + CodeSize: tt.fields.CodeSize, + Balances: tt.fields.Balances, + Suicide: tt.fields.Suicide, + Destroy: tt.fields.Destroy, } if got := a.GetNonce(); got != tt.want { t.Errorf("%q. Account.GetNonce() = %v, want %v", tt.name, got, tt.want) @@ -167,95 +165,19 @@ func TestAccount_SetNonce(t *testing.T) { } for _, tt := range tests { a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, + AcctName: tt.fields.AcctName, + Nonce: tt.fields.Nonce, + Code: tt.fields.Code, + CodeHash: tt.fields.CodeHash, + CodeSize: tt.fields.CodeSize, + Balances: tt.fields.Balances, + Suicide: tt.fields.Suicide, + Destroy: tt.fields.Destroy, } a.SetNonce(tt.args.nonce) } } -func TestAccount_GetPubKey(t *testing.T) { - type fields struct { - AcctName common.Name - Nonce uint64 - PublicKey common.PubKey - Code []byte - CodeHash common.Hash - CodeSize uint64 - Balances []*AssetBalance - Suicide bool - Destroy bool - } - tests := []struct { - name string - fields fields - want common.PubKey - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, - } - if got := a.GetPubKey(); !reflect.DeepEqual(got, tt.want) { - t.Errorf("%q. Account.GetPubKey() = %v, want %v", tt.name, got, tt.want) - } - } -} - -func TestAccount_SetPubKey(t *testing.T) { - type fields struct { - AcctName common.Name - Nonce uint64 - PublicKey common.PubKey - Code []byte - CodeHash common.Hash - CodeSize uint64 - Balances []*AssetBalance - Suicide bool - Destroy bool - } - type args struct { - pubkey common.PubKey - } - tests := []struct { - name string - fields fields - args args - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, - } - a.SetPubKey(tt.args.pubkey) - } -} - func TestAccount_GetCode(t *testing.T) { type fields struct { AcctName common.Name @@ -278,15 +200,14 @@ func TestAccount_GetCode(t *testing.T) { } for _, tt := range tests { a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, + AcctName: tt.fields.AcctName, + Nonce: tt.fields.Nonce, + Code: tt.fields.Code, + CodeHash: tt.fields.CodeHash, + CodeSize: tt.fields.CodeSize, + Balances: tt.fields.Balances, + Suicide: tt.fields.Suicide, + Destroy: tt.fields.Destroy, } got, err := a.GetCode() if (err != nil) != tt.wantErr { @@ -320,15 +241,14 @@ func TestAccount_GetCodeSize(t *testing.T) { } for _, tt := range tests { a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, + AcctName: tt.fields.AcctName, + Nonce: tt.fields.Nonce, + Code: tt.fields.Code, + CodeHash: tt.fields.CodeHash, + CodeSize: tt.fields.CodeSize, + Balances: tt.fields.Balances, + Suicide: tt.fields.Suicide, + Destroy: tt.fields.Destroy, } if got := a.GetCodeSize(); got != tt.want { t.Errorf("%q. Account.GetCodeSize() = %v, want %v", tt.name, got, tt.want) @@ -361,15 +281,14 @@ func TestAccount_SetCode(t *testing.T) { } for _, tt := range tests { a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, + AcctName: tt.fields.AcctName, + Nonce: tt.fields.Nonce, + Code: tt.fields.Code, + CodeHash: tt.fields.CodeHash, + CodeSize: tt.fields.CodeSize, + Balances: tt.fields.Balances, + Suicide: tt.fields.Suicide, + Destroy: tt.fields.Destroy, } if err := a.SetCode(tt.args.code); (err != nil) != tt.wantErr { t.Errorf("%q. Account.SetCode() error = %v, wantErr %v", tt.name, err, tt.wantErr) @@ -399,15 +318,14 @@ func TestAccount_GetCodeHash(t *testing.T) { } for _, tt := range tests { a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, + AcctName: tt.fields.AcctName, + Nonce: tt.fields.Nonce, + Code: tt.fields.Code, + CodeHash: tt.fields.CodeHash, + CodeSize: tt.fields.CodeSize, + Balances: tt.fields.Balances, + Suicide: tt.fields.Suicide, + Destroy: tt.fields.Destroy, } got, err := a.GetCodeHash() if (err != nil) != tt.wantErr { @@ -446,15 +364,14 @@ func TestAccount_GetBalanceByID(t *testing.T) { } for _, tt := range tests { a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, + AcctName: tt.fields.AcctName, + Nonce: tt.fields.Nonce, + Code: tt.fields.Code, + CodeHash: tt.fields.CodeHash, + CodeSize: tt.fields.CodeSize, + Balances: tt.fields.Balances, + Suicide: tt.fields.Suicide, + Destroy: tt.fields.Destroy, } got, err := a.GetBalanceByID(tt.args.assetID) if (err != nil) != tt.wantErr { @@ -488,15 +405,14 @@ func TestAccount_GetBalancesList(t *testing.T) { } for _, tt := range tests { a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, + AcctName: tt.fields.AcctName, + Nonce: tt.fields.Nonce, + Code: tt.fields.Code, + CodeHash: tt.fields.CodeHash, + CodeSize: tt.fields.CodeSize, + Balances: tt.fields.Balances, + Suicide: tt.fields.Suicide, + Destroy: tt.fields.Destroy, } if got := a.GetBalancesList(); !reflect.DeepEqual(got, tt.want) { t.Errorf("%q. Account.GetBalancesList() = %v, want %v", tt.name, got, tt.want) @@ -526,15 +442,14 @@ func TestAccount_GetAllBalances(t *testing.T) { } for _, tt := range tests { a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, + AcctName: tt.fields.AcctName, + Nonce: tt.fields.Nonce, + Code: tt.fields.Code, + CodeHash: tt.fields.CodeHash, + CodeSize: tt.fields.CodeSize, + Balances: tt.fields.Balances, + Suicide: tt.fields.Suicide, + Destroy: tt.fields.Destroy, } got, err := a.GetAllBalances() if (err != nil) != tt.wantErr { @@ -573,15 +488,14 @@ func TestAccount_binarySearch(t *testing.T) { } for _, tt := range tests { a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, + AcctName: tt.fields.AcctName, + Nonce: tt.fields.Nonce, + Code: tt.fields.Code, + CodeHash: tt.fields.CodeHash, + CodeSize: tt.fields.CodeSize, + Balances: tt.fields.Balances, + Suicide: tt.fields.Suicide, + Destroy: tt.fields.Destroy, } got, got1 := a.binarySearch(tt.args.assetID) if got != tt.want { @@ -619,15 +533,14 @@ func TestAccount_AddNewAssetByAssetID(t *testing.T) { for _, tt := range tests { a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, + AcctName: tt.fields.AcctName, + Nonce: tt.fields.Nonce, + Code: tt.fields.Code, + CodeHash: tt.fields.CodeHash, + CodeSize: tt.fields.CodeSize, + Balances: tt.fields.Balances, + Suicide: tt.fields.Suicide, + Destroy: tt.fields.Destroy, } a.AddNewAssetByAssetID(tt.args.assetID, tt.args.amount) } @@ -659,15 +572,14 @@ func TestAccount_SetBalance(t *testing.T) { } for _, tt := range tests { a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, + AcctName: tt.fields.AcctName, + Nonce: tt.fields.Nonce, + Code: tt.fields.Code, + CodeHash: tt.fields.CodeHash, + CodeSize: tt.fields.CodeSize, + Balances: tt.fields.Balances, + Suicide: tt.fields.Suicide, + Destroy: tt.fields.Destroy, } if err := a.SetBalance(tt.args.assetID, tt.args.amount); (err != nil) != tt.wantErr { t.Errorf("%q. Account.SetBalance() error = %v, wantErr %v", tt.name, err, tt.wantErr) @@ -701,15 +613,14 @@ func TestAccount_SubBalanceByID(t *testing.T) { } for _, tt := range tests { a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, + AcctName: tt.fields.AcctName, + Nonce: tt.fields.Nonce, + Code: tt.fields.Code, + CodeHash: tt.fields.CodeHash, + CodeSize: tt.fields.CodeSize, + Balances: tt.fields.Balances, + Suicide: tt.fields.Suicide, + Destroy: tt.fields.Destroy, } if err := a.SubBalanceByID(tt.args.assetID, tt.args.value); (err != nil) != tt.wantErr { t.Errorf("%q. Account.SubBalanceByID() error = %v, wantErr %v", tt.name, err, tt.wantErr) @@ -743,15 +654,14 @@ func TestAccount_AddBalanceByID(t *testing.T) { } for _, tt := range tests { a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, + AcctName: tt.fields.AcctName, + Nonce: tt.fields.Nonce, + Code: tt.fields.Code, + CodeHash: tt.fields.CodeHash, + CodeSize: tt.fields.CodeSize, + Balances: tt.fields.Balances, + Suicide: tt.fields.Suicide, + Destroy: tt.fields.Destroy, } if err := a.AddBalanceByID(tt.args.assetID, tt.args.value); (err != nil) != tt.wantErr { t.Errorf("%q. Account.AddBalanceByID() error = %v, wantErr %v", tt.name, err, tt.wantErr) @@ -785,15 +695,14 @@ func TestAccount_EnoughAccountBalance(t *testing.T) { } for _, tt := range tests { a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, + AcctName: tt.fields.AcctName, + Nonce: tt.fields.Nonce, + Code: tt.fields.Code, + CodeHash: tt.fields.CodeHash, + CodeSize: tt.fields.CodeSize, + Balances: tt.fields.Balances, + Suicide: tt.fields.Suicide, + Destroy: tt.fields.Destroy, } if err := a.EnoughAccountBalance(tt.args.assetID, tt.args.value); (err != nil) != tt.wantErr { t.Errorf("%q. Account.EnoughAccountBalance() error = %v, wantErr %v", tt.name, err, tt.wantErr) @@ -822,15 +731,14 @@ func TestAccount_IsSuicided(t *testing.T) { } for _, tt := range tests { a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, + AcctName: tt.fields.AcctName, + Nonce: tt.fields.Nonce, + Code: tt.fields.Code, + CodeHash: tt.fields.CodeHash, + CodeSize: tt.fields.CodeSize, + Balances: tt.fields.Balances, + Suicide: tt.fields.Suicide, + Destroy: tt.fields.Destroy, } if got := a.IsSuicided(); got != tt.want { t.Errorf("%q. Account.IsSuicided() = %v, want %v", tt.name, got, tt.want) @@ -858,21 +766,20 @@ func TestAccount_SetSuicide(t *testing.T) { } for _, tt := range tests { a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, + AcctName: tt.fields.AcctName, + Nonce: tt.fields.Nonce, + Code: tt.fields.Code, + CodeHash: tt.fields.CodeHash, + CodeSize: tt.fields.CodeSize, + Balances: tt.fields.Balances, + Suicide: tt.fields.Suicide, + Destroy: tt.fields.Destroy, } a.SetSuicide() } } -func TestAccount_IsDestoryed(t *testing.T) { +func TestAccount_IsDestroyed(t *testing.T) { type fields struct { AcctName common.Name Nonce uint64 @@ -893,23 +800,22 @@ func TestAccount_IsDestoryed(t *testing.T) { } for _, tt := range tests { a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, + AcctName: tt.fields.AcctName, + Nonce: tt.fields.Nonce, + Code: tt.fields.Code, + CodeHash: tt.fields.CodeHash, + CodeSize: tt.fields.CodeSize, + Balances: tt.fields.Balances, + Suicide: tt.fields.Suicide, + Destroy: tt.fields.Destroy, } - if got := a.IsDestoryed(); got != tt.want { - t.Errorf("%q. Account.IsDestoryed() = %v, want %v", tt.name, got, tt.want) + if got := a.IsDestroyed(); got != tt.want { + t.Errorf("%q. Account.IsDestroyed() = %v, want %v", tt.name, got, tt.want) } } } -func TestAccount_SetDestory(t *testing.T) { +func TestAccount_SetDestroy(t *testing.T) { type fields struct { AcctName common.Name Nonce uint64 @@ -929,16 +835,15 @@ func TestAccount_SetDestory(t *testing.T) { } for _, tt := range tests { a := &Account{ - AcctName: tt.fields.AcctName, - Nonce: tt.fields.Nonce, - PublicKey: tt.fields.PublicKey, - Code: tt.fields.Code, - CodeHash: tt.fields.CodeHash, - CodeSize: tt.fields.CodeSize, - Balances: tt.fields.Balances, - Suicide: tt.fields.Suicide, - Destroy: tt.fields.Destroy, - } - a.SetDestory() + AcctName: tt.fields.AcctName, + Nonce: tt.fields.Nonce, + Code: tt.fields.Code, + CodeHash: tt.fields.CodeHash, + CodeSize: tt.fields.CodeSize, + Balances: tt.fields.Balances, + Suicide: tt.fields.Suicide, + Destroy: tt.fields.Destroy, + } + a.SetDestroy() } } diff --git a/accountmanager/accountmanager.go b/accountmanager/accountmanager.go index 306a76a8..503dd312 100644 --- a/accountmanager/accountmanager.go +++ b/accountmanager/accountmanager.go @@ -19,63 +19,193 @@ package accountmanager import ( "fmt" "math/big" + "strconv" + "github.com/ethereum/go-ethereum/log" "github.com/fractalplatform/fractal/asset" "github.com/fractalplatform/fractal/common" + "github.com/fractalplatform/fractal/crypto" + "github.com/fractalplatform/fractal/params" "github.com/fractalplatform/fractal/state" "github.com/fractalplatform/fractal/types" "github.com/fractalplatform/fractal/utils/rlp" ) -var acctInfoPrefix = "AcctInfo" +var ( + acctInfoPrefix = "acctInfo" + accountNameIDPrefix = "accountNameId" + counterPrefix = "accountCounter" +) +var acctManagerName = "sysAccount" +var sysName string = "fractal.account" +var counterID uint64 = 4096 + +type AuthorActionType uint64 const ( - //Account Type - NormalAccount uint64 = iota - ContractAccount - AssetAccount + AddAuthor AuthorActionType = iota + UpdateAuthor + DeleteAuthor ) type AccountAction struct { - Founder common.Name - ChargeRatio uint64 - PublicKey common.PubKey + AccountName common.Name `json:"accountName,omitempty"` + Founder common.Name `json:"founder,omitempty"` + ChargeRatio uint64 `json:"chargeRatio,omitempty"` + PublicKey common.PubKey `json:"publicKey,omitempty"` +} + +type AuthorAction struct { + ActionType AuthorActionType + Author *common.Author +} + +type AccountAuthorAction struct { + Threshold uint64 `json:"threshold,omitempty"` + UpdateAuthorThreshold uint64 `json:"updateAuthorThreshold,omitempty"` + AuthorActions []*AuthorAction `json:"authorActions,omitempty"` } type IncAsset struct { - AssetId uint64 `json:"assetid,omitempty"` + AssetId uint64 `json:"assetId,omitempty"` Amount *big.Int `json:"amount,omitempty"` To common.Name `json:"account,omitempty"` } -// AccountManager represents account management model. +//AccountManager represents account management model. type AccountManager struct { sdb SdbIf ast *asset.Asset } +func SetAccountNameConfig(config *Config) bool { + if config == nil { + return false + } + + if config.AccountNameLevel < 0 || config.AccountNameLength <= 8 { + return false + } + + if config.AccountNameLevel > 0 { + if config.SubAccountNameLength < 1 { + return false + } + } + + common.SetAccountNameCheckRule(config.AccountNameLevel, config.AccountNameLength, config.SubAccountNameLength) + return true +} + +//SetSysName set the global sys name +func SetSysName(name common.Name) bool { + if common.IsValidAccountName(name.String()) { + sysName = name.String() + return true + } + return false +} + +//SetAcctMangerName set the global account manager name +func SetAcctMangerName(name common.Name) bool { + if common.IsValidAccountName(name.String()) { + acctManagerName = name.String() + return true + } + return false +} + //NewAccountManager create new account manager func NewAccountManager(db *state.StateDB) (*AccountManager, error) { if db == nil { return nil, ErrNewAccountErr } - return &AccountManager{ + if len(acctManagerName) == 0 { + log.Error("NewAccountManager error", "name", ErrAccountManagerNotExist, acctManagerName) + return nil, ErrAccountManagerNotExist + } + am := &AccountManager{ sdb: db, ast: asset.NewAsset(db), - }, nil + } + + am.InitAccountCounter() + return am, nil +} + +//InitAccountCounter init account manage counter +func (am *AccountManager) InitAccountCounter() { + _, err := am.getAccountCounter() + if err == ErrCounterNotExist { + //var counterID uint64 + //counterID = 0 + //store assetCount + b, err := rlp.EncodeToBytes(&counterID) + if err != nil { + panic(err) + } + am.sdb.Put(acctManagerName, counterPrefix, b) + } + return +} + +//getAccountCounter get account counter cur value +func (am *AccountManager) getAccountCounter() (uint64, error) { + b, err := am.sdb.Get(acctManagerName, counterPrefix) + if err != nil { + return 0, err + } + if len(b) == 0 { + return 0, ErrCounterNotExist + } + var accountCounter uint64 + err = rlp.DecodeBytes(b, &accountCounter) + if err != nil { + return 0, err + } + return accountCounter, nil } // AccountIsExist check account is exist. func (am *AccountManager) AccountIsExist(accountName common.Name) (bool, error) { //check is exist - acct, err := am.GetAccountByName(accountName) + accountID, err := am.GetAccountIDByName(accountName) if err != nil { return false, err } - if acct != nil { + if accountID > 0 { return true, nil + } else { + return false, nil } - return false, nil +} + +// AccountIDIsExist check account is exist by ID. +func (am *AccountManager) AccountIDIsExist(accountID uint64) (bool, error) { + //check is exist + account, err := am.GetAccountById(accountID) + if err != nil { + return false, err + } + if account != nil { + return true, nil + } else { + return false, nil + } +} + +//AccountHaveCode check account have code +func (am *AccountManager) AccountHaveCode(accountName common.Name) (bool, error) { + //check is exist + acct, err := am.GetAccountByName(accountName) + if err != nil { + return false, err + } + if acct == nil { + return false, ErrAccountNotExist + } + + return acct.HaveCode(), nil } //AccountIsEmpty check account is empty @@ -95,17 +225,43 @@ func (am *AccountManager) AccountIsEmpty(accountName common.Name) (bool, error) return false, nil } +func (am *AccountManager) CreateAnyAccount(fromName common.Name, accountName common.Name, founderName common.Name, number uint64, chargeRatio uint64, pubkey common.PubKey) error { + + if accountName.AccountNameLevel() > 1 { + if !fromName.IsValidCreator(accountName.String()) { + return ErrAccountInvaid + } + } + + if err := am.CreateAccount(accountName, founderName, number, 0, pubkey); err != nil { + return err + } + + return nil +} + //CreateAccount contract account -func (am *AccountManager) CreateAccount(accountName common.Name, founderName common.Name, chargeRatio uint64, pubkey common.PubKey) error { +func (am *AccountManager) CreateAccount(accountName common.Name, founderName common.Name, number uint64, chargeRatio uint64, pubkey common.PubKey) error { + if !common.IsValidAccountName(accountName.String()) { + return fmt.Errorf("account %s is invalid", accountName.String()) + } + //check is exist - acct, err := am.GetAccountByName(accountName) + accountID, err := am.GetAccountIDByName(accountName) if err != nil { return err } - if acct != nil { + if accountID > 0 { return ErrAccountIsExist } - if len(founderName.String()) > 0 { + + assetID, _ := am.ast.GetAssetIdByName(accountName.String()) + if assetID > 0 { + return ErrNameIsExist + } + + var fname common.Name + if len(founderName.String()) > 0 && founderName != accountName { f, err := am.GetAccountByName(founderName) if err != nil { return err @@ -113,9 +269,12 @@ func (am *AccountManager) CreateAccount(accountName common.Name, founderName com if f == nil { return ErrAccountNotExist } + fname.SetString(founderName.String()) + } else { + fname.SetString(accountName.String()) } - acctObj, err := NewAccount(accountName, founderName, pubkey) + acctObj, err := NewAccount(accountName, fname, pubkey) if err != nil { return err } @@ -123,8 +282,25 @@ func (am *AccountManager) CreateAccount(accountName common.Name, founderName com return ErrCreateAccountError } + //get accountCounter + accountCounter, err := am.getAccountCounter() + if err != nil { + return err + } + accountCounter = accountCounter + 1 + //set account id + acctObj.SetAccountID(accountCounter) + + //store account name with account id + aid, err := rlp.EncodeToBytes(&accountCounter) + if err != nil { + return err + } + acctObj.SetAccountNumber(number) acctObj.SetChargeRatio(0) am.SetAccount(acctObj) + am.sdb.Put(acctManagerName, accountNameIDPrefix+accountName.String(), aid) + am.sdb.Put(acctManagerName, counterPrefix, aid) return nil } @@ -141,8 +317,8 @@ func (am *AccountManager) SetChargeRatio(accountName common.Name, ra uint64) err return am.SetAccount(acct) } -//UpdateAccount update the pubkey of the accunt -func (am *AccountManager) UpdateAccount(accountName common.Name, founderName common.Name, chargeRatio uint64, pubkey common.PubKey) error { +//UpdateAccount update the pubkey of the account +func (am *AccountManager) UpdateAccount(accountName common.Name, accountAction *AccountAction) error { acct, err := am.GetAccountByName(accountName) if acct == nil { return ErrAccountNotExist @@ -150,27 +326,65 @@ func (am *AccountManager) UpdateAccount(accountName common.Name, founderName com if err != nil { return err } - if len(founderName.String()) > 0 { - f, err := am.GetAccountByName(founderName) + if len(accountAction.Founder.String()) > 0 { + f, err := am.GetAccountByName(accountAction.Founder) if err != nil { return err } if f == nil { return ErrAccountNotExist } + } else { + accountAction.Founder.SetString(accountName.String()) } - if chargeRatio > 100 { + + if accountAction.ChargeRatio > 100 { return ErrChargeRatioInvalid } - acct.SetFounder(founderName) - acct.SetChargeRatio(chargeRatio) - acct.SetPubKey(pubkey) + acct.SetFounder(accountAction.Founder) + acct.SetChargeRatio(accountAction.ChargeRatio) + return am.SetAccount(acct) +} + +func (am *AccountManager) UpdateAccountAuthor(accountName common.Name, acctAuth *AccountAuthorAction) error { + acct, err := am.GetAccountByName(accountName) + if acct == nil { + return ErrAccountNotExist + } + if err != nil { + return err + } + if acctAuth.Threshold != 0 { + acct.SetThreshold(acctAuth.Threshold) + } + if acctAuth.UpdateAuthorThreshold != 0 { + acct.SetUpdateAuthorThreshold(acctAuth.UpdateAuthorThreshold) + } + for _, authorAct := range acctAuth.AuthorActions { + actionTy := authorAct.ActionType + switch actionTy { + case AddAuthor: + acct.AddAuthor(authorAct.Author) + case UpdateAuthor: + acct.UpdateAuthor(authorAct.Author) + case DeleteAuthor: + acct.DeleteAuthor(authorAct.Author) + default: + return fmt.Errorf("invalid account author operation type %d", actionTy) + } + } + acct.SetAuthorVersion() return am.SetAccount(acct) } //GetAccountByTime get account by name and time func (am *AccountManager) GetAccountByTime(accountName common.Name, time uint64) (*Account, error) { - b, err := am.sdb.GetSnapshot(accountName.String(), acctInfoPrefix, time) + accountID, err := am.GetAccountIDByName(accountName) + if err != nil { + return nil, err + } + + b, err := am.sdb.GetSnapshot(acctManagerName, acctInfoPrefix+strconv.FormatUint(accountID, 10), time) if err != nil { return nil, err } @@ -188,40 +402,68 @@ func (am *AccountManager) GetAccountByTime(accountName common.Name, time uint64) //GetAccountByName get account by name func (am *AccountManager) GetAccountByName(accountName common.Name) (*Account, error) { - b, err := am.sdb.Get(accountName.String(), acctInfoPrefix) + accountID, err := am.GetAccountIDByName(accountName) if err != nil { return nil, err } + return am.GetAccountById(accountID) +} + +//GetAccountIDByName get account id by account name +func (am *AccountManager) GetAccountIDByName(accountName common.Name) (uint64, error) { + if accountName == "" { + return 0, nil + } + b, err := am.sdb.Get(acctManagerName, accountNameIDPrefix+accountName.String()) + if err != nil { + return 0, err + } if len(b) == 0 { + return 0, nil + } + var accountID uint64 + if err := rlp.DecodeBytes(b, &accountID); err != nil { + return 0, err + } + return accountID, nil +} + +//GetAccountById get account by account id +func (am *AccountManager) GetAccountById(id uint64) (*Account, error) { + if id == 0 { return nil, nil } + b, err := am.sdb.Get(acctManagerName, acctInfoPrefix+strconv.FormatUint(id, 10)) + + if err != nil { + return nil, err + } + if len(b) == 0 { + log.Debug("account not exist", "id", ErrAccountNotExist, id) + return nil, nil + } var acct Account if err := rlp.DecodeBytes(b, &acct); err != nil { return nil, err } - - //user can find destroyed account - //if acct.IsDestoryed() == true { - // return nil, ErrAccountNotExist - //} - return &acct, nil } -//store account object to db +//SetAccount store account object to db func (am *AccountManager) SetAccount(acct *Account) error { if acct == nil { return ErrAccountIsNil } - if acct.IsDestoryed() == true { + if acct.IsDestroyed() == true { return ErrAccountIsDestroy } b, err := rlp.EncodeToBytes(acct) if err != nil { return err } - am.sdb.Put(acct.GetName().String(), acctInfoPrefix, b) + //am.sdb.Put(acctManagerName, acctInfoPrefix+acct.GetName().String(), b) + am.sdb.Put(acctManagerName, acctInfoPrefix+strconv.FormatUint(acct.GetAccountID(), 10), b) return nil } @@ -235,7 +477,7 @@ func (am *AccountManager) DeleteAccountByName(accountName common.Name) error { return ErrAccountNotExist } - acct.SetDestory() + acct.SetDestroy() b, err := rlp.EncodeToBytes(acct) if err != nil { return err @@ -269,57 +511,66 @@ func (am *AccountManager) SetNonce(accountName common.Name, nonce uint64) error return am.SetAccount(acct) } -//GetBalancesList get Balances return a list -//func (am *AccountManager) GetBalancesList(accountName common.Name) ([]*AssetBalance, error) { -// acct, err := am.GetAccountByName(accountName) -// if err != nil { -// return nil, err -// } -// return acct.GetBalancesList(), nil -//} - -//GetAllAccountBalance return all balance in map. -//func (am *AccountManager) GetAccountAllBalance(accountName common.Name) (map[uint64]*big.Int, error) { -// acct, err := am.GetAccountByName(accountName) -// if err != nil { -// return nil, err -// } -// if acct == nil { -// return nil, ErrAccountNotExist -// } -// -// return acct.GetAllBalances() -//} - -//GetAcccountPubkey get account pub key -//func (am *AccountManager) GetAcccountPubkey(accountName common.Name) ([]byte, error) { -// acct, err := am.GetAccountByName(accountName) -// if err != nil { -// return nil, err -// } -// if acct == nil { -// return nil, ErrAccountNotExist -// } -// return acct.GetPubKey().Bytes(), nil -//} +// GetAuthorVersion returns the account author version +func (am *AccountManager) GetAuthorVersion(accountName common.Name) (uint64, error) { + acct, err := am.GetAccountByName(accountName) + if err != nil { + return 0, err + } + if acct == nil { + return 0, ErrAccountNotExist + } + return acct.GetAuthorVersion(), nil +} // RecoverTx Make sure the transaction is signed properly and validate account authorization. func (am *AccountManager) RecoverTx(signer types.Signer, tx *types.Transaction) error { for _, action := range tx.GetActions() { - pub, err := types.Recover(signer, action, tx) + pubs, err := types.RecoverMultiKey(signer, action, tx) if err != nil { return err } - if err := am.IsValidSign(action.Sender(), action.Type(), pub); err != nil { - return err + if uint64(len(pubs)) > params.MaxSignLength { + return fmt.Errorf("exceed max sign length, want most %d, actual is %d", params.MaxSignLength, len(pubs)) + } + + recoverRes := &recoverActionResult{make(map[common.Name]*accountAuthor, 0)} + for i, pub := range pubs { + index := action.GetSignIndex(uint64(i)) + if uint64(len(index)) > params.MaxSignDepth { + return fmt.Errorf("exceed max sign depth, want most %d, actual is %d", params.MaxSignDepth, len(index)) + } + + if err := am.ValidSign(action.Sender(), pub, index, recoverRes); err != nil { + return err + } + } + + authorVersion := make(map[common.Name]uint64, 0) + for name, acctAuthor := range recoverRes.acctAuthors { + + var count uint64 + for _, weight := range acctAuthor.indexWeight { + count += weight + } + threshold := acctAuthor.threshold + if name.String() == action.Sender().String() && action.Type() == types.UpdateAccountAuthor { + threshold = acctAuthor.updateAuthorThreshold + } + if count < threshold { + return fmt.Errorf("account %s want threshold %d, but actual is %d", name, acctAuthor.threshold, count) + } + authorVersion[name] = acctAuthor.version } + + types.StoreAuthorCache(action, authorVersion) } return nil } // IsValidSign -func (am *AccountManager) IsValidSign(accountName common.Name, aType types.ActionType, pub common.PubKey) error { +func (am *AccountManager) IsValidSign(accountName common.Name, pub common.PubKey) error { acct, err := am.GetAccountByName(accountName) if err != nil { return err @@ -327,16 +578,88 @@ func (am *AccountManager) IsValidSign(accountName common.Name, aType types.Actio if acct == nil { return ErrAccountNotExist } - if acct.IsDestoryed() { + if acct.IsDestroyed() { return ErrAccountIsDestroy } //TODO action type verify - if acct.GetPubKey().Compare(pub) != 0 { - return fmt.Errorf("%v %v have %v excepted %v", acct.AcctName, ErrkeyNotSame, acct.GetPubKey().String(), pub.String()) + for _, author := range acct.Authors { + if author.String() == pub.String() && author.GetWeight() >= acct.GetThreshold() { + return nil + } } - return nil + return fmt.Errorf("%v %v excepted %v", acct.AcctName, ErrkeyNotSame, pub.String()) +} + +//IsValidSign check the sign +func (am *AccountManager) ValidSign(accountName common.Name, pub common.PubKey, index []uint64, recoverRes *recoverActionResult) error { + acct, err := am.GetAccountByName(accountName) + if err != nil { + return err + } + if acct == nil { + return ErrAccountNotExist + } + if acct.IsDestroyed() { + return ErrAccountIsDestroy + } + + var i int + var idx uint64 + for i, idx = range index { + if idx >= uint64(len(acct.Authors)) { + return fmt.Errorf("acct authors modified") + } + if i == len(index)-1 { + break + } + switch ownerTy := acct.Authors[idx].Owner.(type) { + case common.Name: + nextacct, err := am.GetAccountByName(ownerTy) + if err != nil { + return err + } + if nextacct == nil { + return ErrAccountNotExist + } + if nextacct.IsDestroyed() { + return ErrAccountIsDestroy + } + if recoverRes.acctAuthors[acct.GetName()] == nil { + a := &accountAuthor{version: acct.AuthorVersion, threshold: acct.Threshold, updateAuthorThreshold: acct.UpdateAuthorThreshold, indexWeight: map[uint64]uint64{idx: acct.Authors[idx].GetWeight()}} + recoverRes.acctAuthors[acct.GetName()] = a + } else { + recoverRes.acctAuthors[acct.GetName()].indexWeight[idx] = acct.Authors[idx].GetWeight() + } + acct = nextacct + default: + return ErrAccountNotExist + } + } + return am.ValidOneSign(acct, idx, pub, recoverRes) +} +func (am *AccountManager) ValidOneSign(acct *Account, index uint64, pub common.PubKey, recoverRes *recoverActionResult) error { + switch ownerTy := acct.Authors[index].Owner.(type) { + case common.PubKey: + if pub.Compare(ownerTy) != 0 { + return fmt.Errorf("%v %v have %v excepted %v", acct.AcctName, ErrkeyNotSame, pub.String(), ownerTy.String()) + } + case common.Address: + addr := common.BytesToAddress(crypto.Keccak256(pub.Bytes()[1:])[12:]) + if addr.Compare(ownerTy) != 0 { + return fmt.Errorf("%v %v have %v excepted %v", acct.AcctName, ErrkeyNotSame, addr.String(), ownerTy.String()) + } + default: + return fmt.Errorf("wrong sign type") + } + if recoverRes.acctAuthors[acct.GetName()] == nil { + a := &accountAuthor{version: acct.AuthorVersion, threshold: acct.Threshold, updateAuthorThreshold: acct.UpdateAuthorThreshold, indexWeight: map[uint64]uint64{index: acct.Authors[index].GetWeight()}} + recoverRes.acctAuthors[acct.GetName()] = a + return nil + } + recoverRes.acctAuthors[acct.GetName()].indexWeight[index] = acct.Authors[index].GetWeight() + return nil } //GetAssetInfoByName get asset info by asset name. @@ -353,8 +676,95 @@ func (am *AccountManager) GetAssetInfoByID(assetID uint64) (*asset.AssetObject, return am.ast.GetAssetObjectById(assetID) } +// GetAllAssetbyAssetId get accout asset and subAsset Info +func (am *AccountManager) GetAllAssetbyAssetId(acct *Account, assetId uint64) (map[uint64]*big.Int, error) { + var ba = make(map[uint64]*big.Int, 0) + + b, err := acct.GetBalanceByID(assetId) + if err != nil { + return nil, err + } + ba[assetId] = b + + assetObj, err := am.ast.GetAssetObjectById(assetId) + if err != nil { + return nil, err + } + + assetName := assetObj.GetAssetName() + balances, err := acct.GetAllBalances() + if err != nil { + return nil, err + } + + for id, balance := range balances { + subAssetObj, err := am.ast.GetAssetObjectById(id) + if err != nil { + return nil, err + } + + if common.IsValidCreator(assetName, subAssetObj.GetAssetName()) { + ba[id] = balance + } + } + + return ba, nil +} + +// GetAllBalancebyAssetID get account balance, balance(asset) = asset + subAsset +func (am *AccountManager) GetAllBalancebyAssetID(acct *Account, assetID uint64) (*big.Int, error) { + var ba *big.Int + ba = big.NewInt(0) + + b, _ := acct.GetBalanceByID(assetID) + ba = ba.Add(ba, b) + + assetObj, err := am.ast.GetAssetObjectById(assetID) + if err != nil { + return big.NewInt(0), err + } + + assetName := assetObj.GetAssetName() + balances, err := acct.GetAllBalances() + if err != nil { + return big.NewInt(0), err + } + + for id, balance := range balances { + subAssetObj, err := am.ast.GetAssetObjectById(id) + if err != nil { + return big.NewInt(0), err + } + + if common.IsValidCreator(assetName, subAssetObj.GetAssetName()) { + ba = ba.Add(ba, balance) + } + } + + return ba, nil +} + +//GetBalanceByTime get account balance by Time +func (am *AccountManager) GetBalanceByTime(accountName common.Name, assetID uint64, typeID uint64, time uint64) (*big.Int, error) { + acct, err := am.GetAccountByTime(accountName, time) + if err != nil { + return big.NewInt(0), err + } + if acct == nil { + return big.NewInt(0), ErrAccountNotExist + } + + if typeID == 0 { + return acct.GetBalanceByID(assetID) + } else if typeID == 1 { + return am.GetAllBalancebyAssetID(acct, assetID) + } else { + return big.NewInt(0), fmt.Errorf("type ID %d invalid", typeID) + } +} + //GetAccountBalanceByID get account balance by ID -func (am *AccountManager) GetAccountBalanceByID(accountName common.Name, assetID uint64) (*big.Int, error) { +func (am *AccountManager) GetAccountBalanceByID(accountName common.Name, assetID uint64, typeID uint64) (*big.Int, error) { acct, err := am.GetAccountByName(accountName) if err != nil { return big.NewInt(0), err @@ -362,7 +772,13 @@ func (am *AccountManager) GetAccountBalanceByID(accountName common.Name, assetID if acct == nil { return big.NewInt(0), ErrAccountNotExist } - return acct.GetBalanceByID(assetID) + if typeID == 0 { + return acct.GetBalanceByID(assetID) + } else if typeID == 1 { + return am.GetAllBalancebyAssetID(acct, assetID) + } else { + return big.NewInt(0), fmt.Errorf("type ID %d invalid", typeID) + } } //GetAssetAmountByTime get asset amount by time @@ -406,30 +822,7 @@ func (am *AccountManager) GetSnapshotTime(num uint64, time uint64) (uint64, erro return 0, ErrTimeTypeInvalid } -//GetBalanceByTime get account balance by Time -func (am *AccountManager) GetBalanceByTime(accountName common.Name, assetID uint64, time uint64) (*big.Int, error) { - acct, err := am.GetAccountByTime(accountName, time) - if err != nil { - return nil, err - } - if acct == nil { - return nil, ErrAccountNotExist - } - return acct.GetBalanceByID(assetID) - //if time == 0 { - // //current - // return acct.GetBalanceByID(assetID) - //}else { - // //spec time balance - // if acct, err = am.GetAccountByTime(accountName,time);err!= nil{ - // return nil,err - // } - // return acct.GetBalanceByID(assetID) - //} - -} - -//Get Account Founder +//GetFounder Get Account Founder func (am *AccountManager) GetFounder(accountName common.Name) (common.Name, error) { acct, err := am.GetAccountByName(accountName) if err != nil { @@ -441,12 +834,12 @@ func (am *AccountManager) GetFounder(accountName common.Name) (common.Name, erro return acct.GetFounder(), nil } -//Get Asset Founder +//GetAssetFounder Get Asset Founder func (am *AccountManager) GetAssetFounder(assetID uint64) (common.Name, error) { return am.ast.GetAssetFounderById(assetID) } -//Get Account ChargeRatio +//GetChargeRatio Get Account ChargeRatio func (am *AccountManager) GetChargeRatio(accountName common.Name) (uint64, error) { acct, err := am.GetAccountByName(accountName) if err != nil { @@ -458,7 +851,7 @@ func (am *AccountManager) GetChargeRatio(accountName common.Name) (uint64, error return acct.GetChargeRatio(), nil } -//Get Asset ChargeRatio +//GetAssetChargeRatio Get Asset ChargeRatio func (am *AccountManager) GetAssetChargeRatio(assetID uint64) (uint64, error) { acctName, err := am.ast.GetAssetFounderById(assetID) if err != nil { @@ -510,15 +903,12 @@ func (am *AccountManager) SubAccountBalanceByID(accountName common.Name, assetID if value.Cmp(big.NewInt(0)) < 0 { return ErrAmountValueInvalid } - // - val, err := acct.GetBalanceByID(assetID) + + err = acct.SubBalanceByID(assetID, value) if err != nil { return err } - if val.Cmp(big.NewInt(0)) < 0 || val.Cmp(value) < 0 { - return ErrInsufficientBalance - } - acct.SetBalance(assetID, new(big.Int).Sub(val, value)) + return am.SetAccount(acct) } @@ -536,15 +926,15 @@ func (am *AccountManager) AddAccountBalanceByID(accountName common.Name, assetID return ErrAmountValueInvalid } - val, err := acct.GetBalanceByID(assetID) - if err == ErrAccountAssetNotExist { - acct.AddNewAssetByAssetID(assetID, value) - } else { - acct.SetBalance(assetID, new(big.Int).Add(val, value)) + err = acct.AddBalanceByID(assetID, value) + if err != nil { + return err } + return am.SetAccount(acct) } +//AddAccountBalanceByName add balance by name func (am *AccountManager) AddAccountBalanceByName(accountName common.Name, assetName string, value *big.Int) error { acct, err := am.GetAccountByName(accountName) if err != nil { @@ -553,6 +943,7 @@ func (am *AccountManager) AddAccountBalanceByName(accountName common.Name, asset if acct == nil { return ErrAccountNotExist } + assetID, err := am.ast.GetAssetIdByName(assetName) if err != nil { return err @@ -565,12 +956,11 @@ func (am *AccountManager) AddAccountBalanceByName(accountName common.Name, asset return ErrAmountValueInvalid } - val, err := acct.GetBalanceByID(assetID) - if err == ErrAccountAssetNotExist { - acct.AddNewAssetByAssetID(assetID, value) - } else { - acct.SetBalance(assetID, new(big.Int).Add(val, value)) + err = acct.AddBalanceByID(assetID, value) + if err != nil { + return err } + return am.SetAccount(acct) } @@ -676,8 +1066,19 @@ func (am *AccountManager) CanTransfer(accountName common.Name, assetID uint64, v return false, err } -//TransferAsset +//TransferAsset transfer asset func (am *AccountManager) TransferAsset(fromAccount common.Name, toAccount common.Name, assetID uint64, value *big.Int) error { + if value.Sign() == -1 { + return ErrNegativeValue + } + + if ast, err := am.GetAssetInfoByID(assetID); err != nil { + return err + } else if len(ast.Contract.String()) != 0 && !(fromAccount == ast.Contract || toAccount == ast.Contract) { + return ErrInvalidReceiptAsset + } + + //check from account fromAcct, err := am.GetAccountByName(fromAccount) if err != nil { return err @@ -688,9 +1089,10 @@ func (am *AccountManager) TransferAsset(fromAccount common.Name, toAccount commo if value.Cmp(big.NewInt(0)) < 0 { return ErrAmountValueInvalid } - if fromAccount == toAccount { + if fromAccount == toAccount || value.Cmp(big.NewInt(0)) == 0 { return nil } + //check from account balance val, err := fromAcct.GetBalanceByID(assetID) if err != nil { return err @@ -699,7 +1101,7 @@ func (am *AccountManager) TransferAsset(fromAccount common.Name, toAccount commo return ErrInsufficientBalance } fromAcct.SetBalance(assetID, new(big.Int).Sub(val, value)) - + //check to account toAcct, err := am.GetAccountByName(toAccount) if err != nil { return err @@ -707,7 +1109,7 @@ func (am *AccountManager) TransferAsset(fromAccount common.Name, toAccount commo if toAcct == nil { return ErrAccountNotExist } - if toAcct.IsDestoryed() { + if toAcct.IsDestroyed() { return ErrAccountIsDestroy } val, err = toAcct.GetBalanceByID(assetID) @@ -722,11 +1124,20 @@ func (am *AccountManager) TransferAsset(fromAccount common.Name, toAccount commo return am.SetAccount(toAcct) } -//IssueAsset issue asset -func (am *AccountManager) IssueAsset(asset *asset.AssetObject) error { - if err := am.ast.IssueAsset(asset.GetAssetName(), asset.GetSymbol(), asset.GetAssetAmount(), asset.GetDecimals(), asset.GetAssetFounder(), asset.GetAssetOwner(), asset.GetUpperLimit()); err != nil { +func (am *AccountManager) IssueAnyAsset(fromName common.Name, asset *asset.AssetObject) error { + if !am.ast.IsValidOwner(fromName, asset.GetAssetName()) { + return fmt.Errorf("account %s can not create %s", fromName, asset.GetAssetName()) + } + + if err := am.IssueAsset(asset); err != nil { return err } + return nil +} + +//IssueAsset issue asset +func (am *AccountManager) IssueAsset(asset *asset.AssetObject) error { + //check owner acct, err := am.GetAccountByName(asset.GetAssetOwner()) if err != nil { return err @@ -734,17 +1145,35 @@ func (am *AccountManager) IssueAsset(asset *asset.AssetObject) error { if acct == nil { return ErrAccountNotExist } - f, err := am.GetAccountByName(asset.GetAssetFounder()) - if err != nil { - return err + //check founder + if len(asset.GetAssetFounder()) > 0 { + f, err := am.GetAccountByName(asset.GetAssetFounder()) + if err != nil { + return err + } + if f == nil { + return ErrAccountNotExist + } + } else { + asset.SetAssetFounder(asset.GetAssetOwner()) } - if f == nil { - return ErrAccountNotExist + + if name, err := common.StringToName(asset.GetAssetName()); err == nil { + accountID, _ := am.GetAccountIDByName(name) + if accountID > 0 { + return ErrNameIsExist + } + } + + if err := am.ast.IssueAsset(asset.GetAssetName(), asset.GetAssetNumber(), asset.GetSymbol(), asset.GetAssetAmount(), asset.GetDecimals(), asset.GetAssetFounder(), asset.GetAssetOwner(), asset.GetUpperLimit(), asset.GetContract()); err != nil { + return err } + + //add the asset to owner return am.AddAccountBalanceByName(asset.GetAssetOwner(), asset.GetAssetName(), asset.GetAssetAmount()) } -//increase asset and add amount to accout balance +//IncAsset2Acct increase asset and add amount to accout balance func (am *AccountManager) IncAsset2Acct(fromName common.Name, toName common.Name, assetID uint64, amount *big.Int) error { if err := am.ast.IncreaseAsset(fromName, assetID, amount); err != nil { return err @@ -765,118 +1194,195 @@ func (am *AccountManager) IncAsset2Acct(fromName common.Name, toName common.Name // rerturn //} -// Process account action - -func (am *AccountManager) Process(action *types.Action) error { +//Process account action +func (am *AccountManager) Process(accountManagerContext *types.AccountManagerContext) ([]*types.InternalAction, error) { snap := am.sdb.Snapshot() - err := am.process(action) + internalActions, err := am.process(accountManagerContext) if err != nil { am.sdb.RevertToSnapshot(snap) } - return err + return internalActions, err } -func (am *AccountManager) process(action *types.Action) error { +func (am *AccountManager) process(accountManagerContext *types.AccountManagerContext) ([]*types.InternalAction, error) { + action := accountManagerContext.Action + number := accountManagerContext.Number + + var internalActions []*types.InternalAction + + if !action.CheckValue() { + return nil, ErrAmountValueInvalid + } + + if action.Type() != types.Transfer && action.Recipient() != common.Name(sysName) { + return nil, ErrInvalidReceipt + } + + //transfer + if action.Value().Cmp(big.NewInt(0)) > 0 { + if err := am.TransferAsset(action.Sender(), action.Recipient(), action.AssetID(), action.Value()); err != nil { + return nil, err + } + } + + //transaction switch action.Type() { case types.CreateAccount: - key := common.BytesToPubKey(action.Data()) - if err := am.CreateAccount(action.Recipient(), common.Name(""), 0, key); err != nil { - return err + var acct AccountAction + err := rlp.DecodeBytes(action.Data(), &acct) + if err != nil { + return nil, err + } + + if err := am.CreateAnyAccount(action.Sender(), acct.AccountName, acct.Founder, number, 0, acct.PublicKey); err != nil { + return nil, err + } + + if action.Value().Cmp(big.NewInt(0)) > 0 { + if err := am.TransferAsset(common.Name(sysName), acct.AccountName, action.AssetID(), action.Value()); err != nil { + return nil, err + } + actionX := types.NewAction(action.Type(), common.Name(sysName), acct.AccountName, 0, 0, action.AssetID(), action.Value(), nil) + internalAction := &types.InternalAction{Action: actionX.NewRPCAction(0), ActionType: "", GasUsed: 0, GasLimit: 0, Depth: 0, Error: ""} + internalActions = append(internalActions, internalAction) } break case types.UpdateAccount: var acct AccountAction err := rlp.DecodeBytes(action.Data(), &acct) if err != nil { - return err + return nil, err } - if err := am.UpdateAccount(action.Sender(), acct.Founder, 0, acct.PublicKey); err != nil { - return err + + if err := am.UpdateAccount(action.Sender(), &acct); err != nil { + return nil, err + } + break + case types.UpdateAccountAuthor: + var acctAuth AccountAuthorAction + err := rlp.DecodeBytes(action.Data(), &acctAuth) + if err != nil { + return nil, err + } + if err := am.UpdateAccountAuthor(action.Sender(), &acctAuth); err != nil { + return nil, err } break - //case types.DeleteAccount: - // if err := am.DeleteAccountByName(action.Sender()); err != nil { - // return err - // } - // break case types.IssueAsset: var asset asset.AssetObject err := rlp.DecodeBytes(action.Data(), &asset) if err != nil { - return err + return nil, err } - if err := am.IssueAsset(&asset); err != nil { - return err + + asset.SetAssetNumber(number) + if err := am.IssueAnyAsset(action.Sender(), &asset); err != nil { + return nil, err } + actionX := types.NewAction(action.Type(), common.Name(sysName), asset.GetAssetOwner(), 0, 0, asset.GetAssetId(), asset.GetAssetAmount(), nil) + internalAction := &types.InternalAction{Action: actionX.NewRPCAction(0), ActionType: "", GasUsed: 0, GasLimit: 0, Depth: 0, Error: ""} + internalActions = append(internalActions, internalAction) break case types.IncreaseAsset: var inc IncAsset + var accountFrom = common.Name("") err := rlp.DecodeBytes(action.Data(), &inc) if err != nil { - return err + return nil, err } if err = am.IncAsset2Acct(action.Sender(), inc.To, inc.AssetId, inc.Amount); err != nil { - return err + return nil, err } + actionX := types.NewAction(action.Type(), common.Name(accountFrom), inc.To, 0, 0, inc.AssetId, inc.Amount, nil) + internalAction := &types.InternalAction{Action: actionX.NewRPCAction(0), ActionType: "", GasUsed: 0, GasLimit: 0, Depth: 0, Error: ""} + internalActions = append(internalActions, internalAction) break - case types.DestoryAsset: - var asset asset.AssetObject - err := rlp.DecodeBytes(action.Data(), &asset) - if err != nil { - return err - } - if err = am.SubAccountBalanceByID(action.Sender(), asset.GetAssetId(), asset.GetAssetAmount()); err != nil { - return err + case types.DestroyAsset: + // var asset asset.AssetObject + // err := rlp.DecodeBytes(action.Data(), &asset) + // if err != nil { + // return err + // } + if err := am.SubAccountBalanceByID(common.Name(sysName), action.AssetID(), action.Value()); err != nil { + return nil, err } - if err = am.ast.DestoryAsset(action.Sender(), asset.GetAssetId(), asset.GetAssetAmount()); err != nil { - return err + + if err := am.ast.DestroyAsset(common.Name(sysName), action.AssetID(), action.Value()); err != nil { + return nil, err } + actionX := types.NewAction(action.Type(), common.Name(sysName), "", 0, 0, action.AssetID(), action.Value(), nil) + internalAction := &types.InternalAction{Action: actionX.NewRPCAction(0), ActionType: "", GasUsed: 0, GasLimit: 0, Depth: 0, Error: ""} + internalActions = append(internalActions, internalAction) break - case types.SetAssetOwner: + case types.UpdateAsset: var asset asset.AssetObject err := rlp.DecodeBytes(action.Data(), &asset) if err != nil { - return err + return nil, err } acct, err := am.GetAccountByName(asset.GetAssetOwner()) if err != nil { - return err + return nil, err } if acct == nil { - return ErrAccountNotExist - } - if err := am.ast.SetAssetNewOwner(action.Sender(), asset.GetAssetId(), asset.GetAssetOwner()); err != nil { - return err - } - break - case types.SetAssetFounder: - var asset asset.AssetObject - err := rlp.DecodeBytes(action.Data(), &asset) - if err != nil { - return err + return nil, ErrAccountNotExist } if len(asset.GetAssetFounder().String()) > 0 { acct, err := am.GetAccountByName(asset.GetAssetFounder()) if err != nil { - return err + return nil, err } if acct == nil { - return ErrAccountNotExist + return nil, ErrAccountNotExist } } - if err = am.ast.SetAssetFounder(action.Sender(), asset.GetAssetId(), asset.GetAssetFounder()); err != nil { - return err + if err := am.ast.UpdateAsset(action.Sender(), asset.GetAssetId(), asset.GetAssetOwner(), asset.GetAssetFounder(), asset.GetAssetContract()); err != nil { + return nil, err + } + break + case types.SetAssetOwner: + var asset asset.AssetObject + err := rlp.DecodeBytes(action.Data(), &asset) + if err != nil { + return nil, err + } + acct, err := am.GetAccountByName(asset.GetAssetOwner()) + if err != nil { + return nil, err + } + if acct == nil { + return nil, ErrAccountNotExist } + if err := am.ast.SetAssetNewOwner(action.Sender(), asset.GetAssetId(), asset.GetAssetOwner()); err != nil { + return nil, err + } + break + // case types.SetAssetFounder: + // var asset asset.AssetObject + // err := rlp.DecodeBytes(action.Data(), &asset) + // if err != nil { + // return err + // } + // if len(asset.GetAssetFounder().String()) > 0 { + // acct, err := am.GetAccountByName(asset.GetAssetFounder()) + // if err != nil { + // return err + // } + // if acct == nil { + // return ErrAccountNotExist + // } + // } + // if err = am.ast.SetAssetFounder(action.Sender(), asset.GetAssetId(), asset.GetAssetFounder()); err != nil { + // return err + // } + // break + case types.Transfer: + //return am.TransferAsset(action.Sender(), action.Recipient(), action.AssetID(), action.Value()) break - //case types.Transfer: - // return am.TransferAsset(action.Sender(), action.Recipient(), action.AssetID(), action.Value()) default: - return ErrUnkownTxType + return nil, ErrUnkownTxType } - if action.Value().Cmp(big.NewInt(0)) > 0 { - return am.TransferAsset(action.Sender(), action.Recipient(), action.AssetID(), action.Value()) - } - return nil + return internalActions, nil } diff --git a/accountmanager/accountmanager_test.go b/accountmanager/accountmanager_test.go index eab47857..2894ac81 100644 --- a/accountmanager/accountmanager_test.go +++ b/accountmanager/accountmanager_test.go @@ -17,7 +17,6 @@ package accountmanager import ( - "bytes" "crypto/ecdsa" "fmt" "math/big" @@ -43,7 +42,7 @@ func getStateDB() *state.StateDB { tridb := state.NewDatabase(db) statedb, err := state.New(common.Hash{}, tridb) if err != nil { - //.Fatal("test NewState failure ", err) + fmt.Printf("test getStateDB() failure %v", err) return nil } @@ -53,19 +52,23 @@ func getAsset() *asset.Asset { return asset.NewAsset(sdb) } func getAccountManager() *AccountManager { - + SetAcctMangerName("systestname") + SetSysName("systestname") am, err := NewAccountManager(sdb) if err != nil { - //t.Fatal("test NewAccount failure ", err) + fmt.Printf("test getAccountManager() failure %v", err) } + pubkey := new(common.PubKey) + pubkey.SetBytes([]byte("abcde123456789")) + am.CreateAccount(common.Name("systestname"), common.Name(""), 0, 0, *pubkey) return am } func TestSDB(t *testing.T) { - b, err := rlp.EncodeToBytes("aaaa") if err != nil { fmt.Printf("encode err = %v", err) + } sdb.Put("test1", acctInfoPrefix, b) b1, err := sdb.Get("test1", acctInfoPrefix) @@ -86,10 +89,10 @@ func TestSDB(t *testing.T) { } func TestNN(t *testing.T) { - if err := acctm.CreateAccount(common.Name("123asdf2"), common.Name(""), 0, *new(common.PubKey)); err != nil { + if err := acctm.CreateAccount(common.Name("123asdf2"), common.Name(""), 0, 0, *new(common.PubKey)); err != nil { t.Errorf("err create account\n") } - _, err := acctm.GetAccountBalanceByID(common.Name("123asdf2"), 1) + _, err := acctm.GetAccountBalanceByID(common.Name("123asdf2"), 1, 0) if err == nil { t.Errorf("err get balance err %v\n", err) } @@ -124,6 +127,10 @@ func TestNewAccountManager(t *testing.T) { } } +func TestAccountManager_InitAccountCounter(t *testing.T) { + //TODO +} + func TestAccountManager_CreateAccount(t *testing.T) { type fields struct { sdb *state.StateDB @@ -158,7 +165,7 @@ func TestAccountManager_CreateAccount(t *testing.T) { sdb: tt.fields.sdb, ast: tt.fields.ast, } - if err := am.CreateAccount(tt.args.accountName, tt.args.founderName, 1, tt.args.pubkey); (err != nil) != tt.wantErr { + if err := am.CreateAccount(tt.args.accountName, tt.args.founderName, 0, 1, tt.args.pubkey); (err != nil) != tt.wantErr { t.Errorf("%q. AccountManager.CreateAccount() error = %v, wantErr %v", tt.name, err, tt.wantErr) } } @@ -167,7 +174,7 @@ func TestAccountManager_CreateAccount(t *testing.T) { sdb: sdb, ast: ast, } - err := am1.CreateAccount(common.Name("aaaadddd"), common.Name("111222332a"), 1, *pubkey) + err := am1.CreateAccount(common.Name("aaaadddd"), common.Name("111222332a"), 0, 1, *pubkey) if err != nil { t.Errorf("create acct err:%v", err) } @@ -228,7 +235,7 @@ func TestAccountManager_AccountIsEmpty(t *testing.T) { wantErr error }{ // - {"accountEmpty", fields{sdb, ast}, args{common.Name("11122233")}, false, ErrAccountNotExist}, + {"accountNotEmpty", fields{sdb, ast}, args{common.Name("11122233")}, false, ErrAccountNotExist}, {"accountEmpty", fields{sdb, ast}, args{common.Name("a123456789aeee")}, true, nil}, } for _, tt := range tests { @@ -250,52 +257,6 @@ func GeneragePubKey() (common.PubKey, *ecdsa.PrivateKey) { prikey, _ := crypto.GenerateKey() return common.BytesToPubKey(crypto.FromECDSAPub(&prikey.PublicKey)), prikey } -func TestAccountManager_UpdateAccount(t *testing.T) { - type fields struct { - sdb SdbIf - ast *asset.Asset - } - pubkey := new(common.PubKey) - pubkey2 := new(common.PubKey) - pubkey.SetBytes([]byte("abcde123456789")) - - pubkey3, _ := GeneragePubKey() - //fmt.Printf("UpdateAccount key=%v\n", pubkey3.Bytes()) - type args struct { - accountName common.Name - founderName common.Name - chargeRatio uint64 - pubkey common.PubKey - } - tests := []struct { - name string - fields fields - args args - wantErr bool - }{ - // - {"updateAccount", fields{sdb, ast}, args{common.Name("a123456789aeee"), common.Name("a123456789aeee"), 1, pubkey3}, false}, - {"updateAccount", fields{sdb, ast}, args{common.Name("a123456789aeee"), common.Name("111222332a"), 1, pubkey3}, false}, - {"updateAccountNilFounder", fields{sdb, ast}, args{common.Name("111222332a"), common.Name("1112223-"), 1, *pubkey2}, true}, - {"updateAccountInvaidName", fields{sdb, ast}, args{common.Name("a123456789aeeegty"), common.Name("a123456789aeeegty"), 1, *pubkey2}, true}, - } - for _, tt := range tests { - am := &AccountManager{ - sdb: tt.fields.sdb, - ast: tt.fields.ast, - } - //if err := am.CreateAccount(tt.args.accountName, *pubkey2); (err != nil) != tt.wantErr { - // t.Errorf("%q. AccountManager.CreateAccount() error = %v, wantErr %v", tt.name, err, tt.wantErr) - //} - if err := am.UpdateAccount(tt.args.accountName, tt.args.founderName, tt.args.chargeRatio, tt.args.pubkey); (err != nil) != tt.wantErr { - t.Errorf("%q. AccountManager.UpdateAccount() error = %v, wantErr %v", tt.name, err, tt.wantErr) - } - } - acct, _ := acctm.GetAccountByName(common.Name("a123456789aeee")) - if bytes.Compare(acct.PublicKey.Bytes(), pubkey3.Bytes()) != 0 { - t.Errorf("AccountManager.UpdateAccount() error key not eque:%v\n =%v", acct.PublicKey.Bytes(), []byte("abcde123456789")) - } -} func TestAccountManager_GetAccountByName(t *testing.T) { type fields struct { @@ -355,7 +316,7 @@ func TestAccountManager_SetAccount(t *testing.T) { acct *Account } pubkey2 := new(common.PubKey) - acctm.CreateAccount(common.Name("a123456789"), common.Name(""), 0, *pubkey2) + acctm.CreateAccount(common.Name("a123456789"), common.Name(""), 0, 0, *pubkey2) ac, _ := acctm.GetAccountByName(common.Name("a123456789")) tests := []struct { @@ -572,13 +533,12 @@ func TestAccountManager_IsValidSign(t *testing.T) { } type args struct { accountName common.Name - aType types.ActionType pub common.PubKey } pubkey := new(common.PubKey) pubkey2 := new(common.PubKey) pubkey2.SetBytes([]byte("abcde123456789")) - acctm.UpdateAccount(common.Name("a123456789aeee"), common.Name("a123456789aeee"), 1, *pubkey2) + //acctm.UpdateAccount(common.Name("a123456789aeee"), &AccountAction{common.Name("a123456789aeee"), common.Name("a123456789aeee"), 1, *pubkey2}) tests := []struct { name string fields fields @@ -586,15 +546,15 @@ func TestAccountManager_IsValidSign(t *testing.T) { wantErr bool }{ // - {"invalidsign", fields{sdb, ast}, args{common.Name("a123456789aeee"), types.CreateContract, *pubkey}, true}, - {"validsign", fields{sdb, ast}, args{common.Name("a123456789aeee"), types.CreateContract, *pubkey2}, false}, + {"invalidsign", fields{sdb, ast}, args{common.Name("a123456789aeee"), *pubkey}, false}, + {"validsign", fields{sdb, ast}, args{common.Name("a123456789aeee"), *pubkey2}, true}, } for _, tt := range tests { am := &AccountManager{ sdb: tt.fields.sdb, ast: tt.fields.ast, } - if err := am.IsValidSign(tt.args.accountName, tt.args.aType, tt.args.pub); (err != nil) != tt.wantErr { + if err := am.IsValidSign(tt.args.accountName, tt.args.pub); (err != nil) != tt.wantErr { t.Errorf("%q. AccountManager.IsValidSign() error = %v, wantErr %v", tt.name, err, tt.wantErr) } } @@ -609,16 +569,16 @@ func TestAccountManager_GetAccountBalanceByID(t *testing.T) { accountName common.Name assetID uint64 } - - acctm.ast.IssueAsset("ziz", "zz", big.NewInt(1000), 10, common.Name("a123456789aeee"), common.Name("a123456789aeee"), big.NewInt(1000)) + //asset ID = 1 + acctm.ast.IssueAsset("ziz", 0, "zz", big.NewInt(1000), 0, common.Name("a123456789aeee"), common.Name("a123456789aeee"), big.NewInt(1000), common.Name("")) id, _ := acctm.ast.GetAssetIdByName("ziz") - //t.Logf("GetAccountBalanceByID id=%v", id) + t.Logf("GetAccountBalanceByID id=%v", id) if err := acctm.AddAccountBalanceByID(common.Name("a123456789aeee"), id, big.NewInt(800)); err != nil { t.Errorf("%q. GetAccountByName.AddBalanceByName() error = %v, ", common.Name("a123456789aeee"), err) } - //val, _ := acctm.GetAccountBalanceByID(common.Name("a123456789aeee"), id) - // t.Logf("GetAccountBalanceByID asset id=%v : val=%v\n", id, val) + val, _ := acctm.GetAccountBalanceByID(common.Name("a123456789aeee"), id, 0) + t.Logf("a123456789aeee asset id=%v : val=%v\n", id, val) tests := []struct { name string @@ -628,7 +588,7 @@ func TestAccountManager_GetAccountBalanceByID(t *testing.T) { wantErr bool }{ // - {"getAcctBaByID", fields{sdb, ast}, args{common.Name("a123456789aeee"), id}, big.NewInt(800), false}, + {"getAcctByID", fields{sdb, ast}, args{common.Name("a123456789aeee"), id}, big.NewInt(800), false}, } //acctm := getAccountManager(t) //pubkey2 := new(common.PubKey) @@ -642,7 +602,7 @@ func TestAccountManager_GetAccountBalanceByID(t *testing.T) { sdb: tt.fields.sdb, ast: tt.fields.ast, } - got, err := am.GetAccountBalanceByID(tt.args.accountName, tt.args.assetID) + got, err := am.GetAccountBalanceByID(tt.args.accountName, tt.args.assetID, 0) if (err != nil) != tt.wantErr { t.Errorf("%q. AccountManager.GetAccountBalanceByID() error = %v, wantErr %v", tt.name, err, tt.wantErr) continue @@ -651,7 +611,7 @@ func TestAccountManager_GetAccountBalanceByID(t *testing.T) { t.Errorf("%q. AccountManager.GetAccountBalanceByID() = %v, want %v", tt.name, got, tt.want) } } - val, _ := acctm.GetAccountBalanceByID(common.Name("a123456789aeee"), id) + val, _ = acctm.GetAccountBalanceByID(common.Name("a123456789aeee"), id, 0) if val.Cmp(big.NewInt(800)) != 0 { t.Errorf("TestAccountManager_GetAccountBalanceByID = %v", val) } @@ -665,7 +625,7 @@ func TestAccountManager_GetAssetInfoByName(t *testing.T) { type args struct { name string } - ast1, err := asset.NewAssetObject("ziz", "zz", big.NewInt(1000), 10, common.Name("a123456789aeee"), common.Name("a123456789aeee"), big.NewInt(1000)) + ast1, err := asset.NewAssetObject("ziz", 0, "zz", big.NewInt(1000), 0, common.Name("a123456789aeee"), common.Name("a123456789aeee"), big.NewInt(1000), common.Name("")) if err != nil { t.Errorf("new asset object err") } @@ -706,7 +666,7 @@ func TestAccountManager_GetAssetInfoByID(t *testing.T) { assetID uint64 } - ast1, err := asset.NewAssetObject("ziz", "zz", big.NewInt(1000), 10, common.Name("a123456789aeee"), common.Name("a123456789aeee"), big.NewInt(1000)) + ast1, err := asset.NewAssetObject("ziz", 0, "zz", big.NewInt(1000), 0, common.Name("a123456789aeee"), common.Name("a123456789aeee"), big.NewInt(1000), common.Name("")) if err != nil { t.Errorf("new asset object err") } @@ -898,7 +858,7 @@ func TestAccountManager_GetBalanceByTime(t *testing.T) { sdb: tt.fields.sdb, ast: tt.fields.ast, } - got, err := am.GetBalanceByTime(tt.args.accountName, tt.args.assetID, tt.args.time) + got, err := am.GetBalanceByTime(tt.args.accountName, tt.args.assetID, 0, tt.args.time) if (err != nil) != tt.wantErr { t.Errorf("%q. AccountManager.GetBalanceByTime() error = %v, wantErr %v", tt.name, err, tt.wantErr) continue @@ -1115,7 +1075,7 @@ func TestAccountManager_EnoughAccountBalance(t *testing.T) { args args wantErr bool }{ - // + //amount = 1000 {"enough", fields{sdb, ast}, args{common.Name("a123456789aeee"), 1, big.NewInt(-2)}, true}, {"enough", fields{sdb, ast}, args{common.Name("a123456789aeee"), 1, big.NewInt(999)}, false}, {"notenough", fields{sdb, ast}, args{common.Name("a123456789aeee"), 1, big.NewInt(1001)}, true}, @@ -1133,7 +1093,7 @@ func TestAccountManager_EnoughAccountBalance(t *testing.T) { t.Errorf("%q. AccountManager.EnoughAccountBalance() error = %v, wantErr %v", tt.name, err, tt.wantErr) } } - val, _ := acctm.GetAccountBalanceByID(common.Name("a123456789aeee"), 1) + val, _ := acctm.GetAccountBalanceByID(common.Name("a123456789aeee"), 1, 0) if val.Cmp(big.NewInt(1000)) != 0 { t.Logf("TestAccountManager_EnoughAccountBalance = %v", val) } @@ -1149,7 +1109,7 @@ func TestAccountManager_GetCode(t *testing.T) { } pubkey2 := new(common.PubKey) acct, _ := acctm.GetAccountByName(common.Name("a123456789aeee")) - acctm.CreateAccount(common.Name("a123456789aeed"), common.Name("a123456789aeed"), 0, *pubkey2) + acctm.CreateAccount(common.Name("a123456789aeed"), common.Name("a123456789aeed"), 0, 0, *pubkey2) acct.SetCode([]byte("abcde123456789")) acctm.SetAccount(acct) //t.Logf("EnoughAccountBalance asset id=%v : val=%v\n", 1, val) @@ -1339,7 +1299,7 @@ func TestAccountManager_TransferAsset(t *testing.T) { // {"tranferok", fields{sdb, ast}, args{common.Name("a123456789aeee"), common.Name("a123456789aeed"), 1, big.NewInt(3)}, false}, } - val, err := acctm.GetAccountBalanceByID(common.Name("a123456789aeee"), 1) + val, err := acctm.GetAccountBalanceByID(common.Name("a123456789aeee"), 1, 0) if err != nil { t.Error("TransferAsset GetAccountBalanceByID err") } @@ -1356,7 +1316,7 @@ func TestAccountManager_TransferAsset(t *testing.T) { t.Errorf("%q. AccountManager.TransferAsset() error = %v, wantErr %v", tt.name, err, tt.wantErr) } } - val1, err := acctm.GetAccountBalanceByID(common.Name("a123456789aeee"), 1) + val1, err := acctm.GetAccountBalanceByID(common.Name("a123456789aeee"), 1, 0) if err != nil { t.Error("TransferAsset GetAccountBalanceByID err") } @@ -1381,17 +1341,18 @@ func TestAccountManager_IssueAsset(t *testing.T) { //if err != nil { // t.Fatal("IssueAsset err", err) //} - ast1, err := asset.NewAssetObject("ziz0123456789ziz", "ziz", big.NewInt(2), 18, common.Name("a0123456789ziz"), common.Name("a0123456789ziz"), big.NewInt(100000)) + + ast1, err := asset.NewAssetObject("ziz0123456789ziz", 0, "ziz", big.NewInt(2), 18, common.Name("a123456789aeee"), common.Name("a0123456789ziz"), big.NewInt(100000), common.Name("")) if err != nil { t.Fatal("IssueAsset err", err) } - //ast3, err := asset.NewAssetObject("ziz0123456789", "ziz", big.NewInt(2), 18, common.Name("a123456789aeee")) - //if err != nil { - // t.Fatal("IssueAsset err", err) - //} - - ast2, err := asset.NewAssetObject("ziz0123456789zi", "ziz", big.NewInt(2), 18, common.Name("a123456789aeee"), common.Name("a123456789aeee"), big.NewInt(2)) + ast3, err := asset.NewAssetObject("ziz0123456789", 0, "ziz", big.NewInt(2), 18, common.Name("a123456777777"), common.Name("a123456789aeee"), big.NewInt(2), common.Name("")) + if err != nil { + t.Fatal("IssueAsset err", err) + } + //asset id =2 + ast2, err := asset.NewAssetObject("ziz0123456789zi", 0, "ziz", big.NewInt(2), 18, common.Name("a123456789aeee"), common.Name("a123456789aeee"), big.NewInt(12000), common.Name("")) if err != nil { t.Fatal("IssueAsset err", err) } @@ -1409,6 +1370,7 @@ func TestAccountManager_IssueAsset(t *testing.T) { }{ // {"ownernotexist", fields{sdb, ast}, args{ast1}, true}, + {"foundernotexist", fields{sdb, ast}, args{ast3}, true}, {"ownerexist", fields{sdb, ast}, args{ast2}, false}, } @@ -1441,7 +1403,8 @@ func TestAccountManager_IncAsset2Acct(t *testing.T) { wantErr bool }{ // - {"accountexist", fields{sdb, ast}, args{common.Name("a0123456789ziz"), common.Name("a123456789aeee"), 2, big.NewInt(10)}, false}, + {"over upperlimit", fields{sdb, ast}, args{common.Name("a123456789aeee"), common.Name("a123456789aeee"), 2, big.NewInt(11999)}, true}, + {"accountexist", fields{sdb, ast}, args{common.Name("a123456789aeee"), common.Name("a123456789aeee"), 2, big.NewInt(10)}, false}, {"notexist", fields{sdb, ast}, args{common.Name("a0123456789ziz"), common.Name("a123456789aeef"), 2, big.NewInt(1)}, true}, } for _, tt := range tests { @@ -1494,8 +1457,8 @@ func TestAccountManager_Process(t *testing.T) { } inc := &IncAsset{ - AssetId: 4, - Amount: big.NewInt(100000000), + AssetId: 2, + Amount: big.NewInt(100), To: common.Name("a123456789aeee"), } payload2, err := rlp.EncodeToBytes(inc) @@ -1504,7 +1467,7 @@ func TestAccountManager_Process(t *testing.T) { } ast0 := &asset.AssetObject{ - AssetId: 4, + AssetId: 2, AssetName: "abced99", Symbol: "aaa", Amount: big.NewInt(100000000), @@ -1515,13 +1478,13 @@ func TestAccountManager_Process(t *testing.T) { UpperLimit: big.NewInt(1000000000), } ast1 := &asset.AssetObject{ - AssetId: 4, + AssetId: 2, AssetName: "abced99", Symbol: "aaa", Amount: big.NewInt(100000000), Decimals: 2, - Founder: common.Name("a123456789addd"), - Owner: common.Name("a123456789addd"), + Founder: common.Name(sysName), + Owner: common.Name(sysName), AddIssue: big.NewInt(0), UpperLimit: big.NewInt(1000000000), } @@ -1536,6 +1499,8 @@ func TestAccountManager_Process(t *testing.T) { pubkey, _ := GeneragePubKey() pubkey1, _ := GeneragePubKey() aa := &AccountAction{ + AccountName: common.Name("a123456789addd"), + Founder: common.Name(""), ChargeRatio: 10, PublicKey: pubkey, } @@ -1544,6 +1509,8 @@ func TestAccountManager_Process(t *testing.T) { panic("rlp payload err") } aa1 := &AccountAction{ + AccountName: common.Name("a123456789addd"), + Founder: common.Name(""), ChargeRatio: 99, PublicKey: pubkey1, } @@ -1551,12 +1518,24 @@ func TestAccountManager_Process(t *testing.T) { if err != nil { panic("rlp payload err") } + ca1 := common.NewAuthor(common.Name("a123456789aeee"), 1) + autha1 := &AuthorAction{ActionType: 0, Author: ca1} + ca2 := common.NewAuthor(common.Name("a123456789aeee"), 2) + autha2 := &AuthorAction{ActionType: 1, Author: ca2} + + aaa1 := &AccountAuthorAction{Threshold: 10, AuthorActions: []*AuthorAction{autha1, autha2}} + payload5, err := rlp.EncodeToBytes(aaa1) + if err != nil { + panic("rlp payload err") + } + + action := types.NewAction(types.IssueAsset, common.Name("a123456789aeee"), common.Name(sysName), 1, 1, 0, big.NewInt(0), payload) + action1 := types.NewAction(types.IncreaseAsset, common.Name("a123456789aeee"), common.Name(sysName), 1, 1, 2, big.NewInt(0), payload2) + action2 := types.NewAction(types.UpdateAsset, common.Name("a123456789aeee"), common.Name(sysName), 1, 1, 2, big.NewInt(0), payload1) + action3 := types.NewAction(types.CreateAccount, common.Name("a123456789aeee"), common.Name(sysName), 1, 1, 2, big.NewInt(10), payload3) + action4 := types.NewAction(types.UpdateAccount, common.Name("a123456789addd"), common.Name(sysName), 1, 1, 2, big.NewInt(0), payload4) + action5 := types.NewAction(types.UpdateAccountAuthor, common.Name("a123456789addd"), common.Name(sysName), 1, 1, 2, big.NewInt(0), payload5) - action := types.NewAction(types.IssueAsset, common.Name("a123456789aeee"), common.Name("a123456789aeee"), 1, 1, 0, big.NewInt(2), payload) - action1 := types.NewAction(types.IncreaseAsset, common.Name("a123456789aeee"), common.Name("a123456789aeee"), 1, 1, 2, big.NewInt(0), payload2) - action2 := types.NewAction(types.SetAssetOwner, common.Name("a123456789aeee"), common.Name("a123456789addd"), 1, 1, 2, big.NewInt(0), payload1) - action3 := types.NewAction(types.CreateAccount, common.Name("a123456789aeee"), common.Name("a123456789addd"), 1, 1, 2, big.NewInt(10), payload3) - action4 := types.NewAction(types.UpdateAccount, common.Name("a123456789addd"), common.Name("a123456789addd"), 1, 1, 2, big.NewInt(0), payload4) //action5 := types.NewAction(types.DeleteAccount, common.Name("123asdf2"), common.Name("123asdf2"), 1, 1, 2, big.NewInt(0), pubkey1[:]) //action6 := types.NewAction(types.Transfer, common.Name("a123456789aeee"), common.Name("a123456789aeee"), 1, 1, 2, big.NewInt(1), pubkey1[:]) //action7 := types.NewAction(types.Transfer, common.Name("a123456789addd"), common.Name("a123456789aeee"), 1, 1, 2, big.NewInt(1), payload) @@ -1571,18 +1550,21 @@ func TestAccountManager_Process(t *testing.T) { {"issue", fields{sdb, ast}, args{action}, false}, {"increase", fields{sdb, ast}, args{action1}, false}, {"createaccount", fields{sdb, ast}, args{action3}, false}, - {"setowner", fields{sdb, ast}, args{action2}, false}, + {"updateasset", fields{sdb, ast}, args{action2}, false}, {"updateaccount", fields{sdb, ast}, args{action4}, false}, + {"updateaccountauthor", fields{sdb, ast}, args{action5}, false}, + //{"deleteaccount", fields{sdb, ast}, args{action5}, false}, //{"transfer2self", fields{sdb, ast}, args{action6}, false}, //{"transfer", fields{sdb, ast}, args{action7}, false}, } + for _, tt := range tests { am := &AccountManager{ sdb: tt.fields.sdb, ast: tt.fields.ast, } - if err := am.Process(tt.args.action); (err != nil) != tt.wantErr { + if _, err := am.Process(&types.AccountManagerContext{Action: tt.args.action, Number: 0}); (err != nil) != tt.wantErr { t.Errorf("%q. AccountManager.Process() error = %v, wantErr %v", tt.name, err, tt.wantErr) } } @@ -1592,13 +1574,13 @@ func TestAccountManager_Process(t *testing.T) { t.Error("Process issue asset failure") } //t.Logf("issue ok id=%v", asset2.AssetId) - if asset2.Amount.Cmp(big.NewInt(200000000)) != 0 { + if asset2.Amount.Cmp(big.NewInt(100000000)) != 0 { t.Errorf("Process increase asset failure amount=%v", asset2.Amount) } - if asset2.GetAssetOwner() != "a123456789addd" { + if asset2.GetAssetOwner() != "a123456789aeee" { t.Errorf("Process set asset owner failure =%v", asset2.GetAssetName()) } - val, err := acctm.GetAccountBalanceByID(common.Name("a123456789aeee"), 1) + val, err := acctm.GetAccountBalanceByID(common.Name("a123456789aeee"), 1, 0) if err != nil { t.Error("Process GetAccountBalanceByID err") } @@ -1608,7 +1590,7 @@ func TestAccountManager_Process(t *testing.T) { if err != nil { t.Error("Process GetAccountByName err") } - if !ac.IsDestoryed() { + if !ac.IsDestroyed() { //t.Error("Process delete account failure") } @@ -1619,12 +1601,253 @@ func TestAccountManager_Process(t *testing.T) { if ac1 == nil { t.Error("Process create account err") } - if bytes.Compare(ac1.PublicKey.Bytes(), pubkey1[:]) != 0 { - t.Error("Process update account failure") + if ac1.Authors[1].Weight != 2 { + t.Errorf("Process update accountauthor fail") } val, err = ac1.GetBalanceByID(1) if val.Cmp(big.NewInt(10)) != 0 { t.Errorf("Process transfer failure=%v", val) } + ca3 := common.NewAuthor(common.Name("a123456789aeee"), 1) + autha3 := &AuthorAction{ActionType: 2, Author: ca3} + aaa2 := &AccountAuthorAction{AuthorActions: []*AuthorAction{autha3}} + + payload6, err := rlp.EncodeToBytes(aaa2) + if err != nil { + panic("rlp payload err") + } + action6 := types.NewAction(types.UpdateAccountAuthor, common.Name("a123456789addd"), common.Name(sysName), 1, 1, 2, big.NewInt(0), payload6) + + tests = []struct { + name string + fields fields + args args + wantErr bool + }{ + {"updateaccountauthor", fields{sdb, ast}, args{action6}, false}, + } + for _, tt := range tests { + am := &AccountManager{ + sdb: tt.fields.sdb, + ast: tt.fields.ast, + } + if _, err := am.Process(&types.AccountManagerContext{Action: tt.args.action, Number: 0}); (err != nil) != tt.wantErr { + t.Errorf("%q. AccountManager.Process() error = %v, wantErr %v", tt.name, err, tt.wantErr) + } + } + ac2, err := acctm.GetAccountByName(common.Name("a123456789addd")) + if err != nil { + t.Error("Process GetAccountByName err") + } + if ac2 == nil { + t.Error("Process create account err") + } + if len(ac2.Authors) != 1 && ac2.Threshold != 10 { + t.Errorf("Process delete accountauthor fail") + } + +} + +func TestAccountManager_SubAccount(t *testing.T) { + + type fields struct { + sdb SdbIf + ast *asset.Asset + } + type args struct { + action *types.Action + } + + pubkey, _ := GeneragePubKey() + a := &AccountAction{ + AccountName: common.Name("bbbbbbbb"), + Founder: common.Name(""), + ChargeRatio: 10, + PublicKey: pubkey, + } + payload, err := rlp.EncodeToBytes(a) + if err != nil { + panic("rlp payload err") + } + + a1 := &AccountAction{ + AccountName: common.Name("bbbbbbbb.cc"), + Founder: common.Name(""), + ChargeRatio: 10, + PublicKey: pubkey, + } + payload1, err := rlp.EncodeToBytes(a1) + if err != nil { + panic("rlp payload err") + } + + a2 := &AccountAction{ + AccountName: common.Name("bbbbbbbb.dd"), + Founder: common.Name(""), + ChargeRatio: 10, + PublicKey: pubkey, + } + payload2, err := rlp.EncodeToBytes(a2) + if err != nil { + panic("rlp payload err") + } + + a3 := &AccountAction{ + AccountName: common.Name("bbbbbbbb.ccc"), + Founder: common.Name(""), + ChargeRatio: 10, + PublicKey: pubkey, + } + payload3, err := rlp.EncodeToBytes(a3) + if err != nil { + panic("rlp payload err") + } + + a4 := &AccountAction{ + AccountName: common.Name("bbbbbbbb.cc.dd"), + Founder: common.Name(""), + ChargeRatio: 10, + PublicKey: pubkey, + } + payload4, err := rlp.EncodeToBytes(a4) + if err == nil { + panic("rlp payload err") + } + + a5 := &AccountAction{ + AccountName: common.Name("bbbbbbbb.cc.ee"), + Founder: common.Name(""), + ChargeRatio: 10, + PublicKey: pubkey, + } + payload5, err := rlp.EncodeToBytes(a5) + if err == nil { + panic("rlp payload err") + } + + a6 := &AccountAction{ + AccountName: common.Name("bbbbbbbb.cc.ff"), + Founder: common.Name(""), + ChargeRatio: 10, + PublicKey: pubkey, + } + payload6, err := rlp.EncodeToBytes(a6) + if err == nil { + panic("rlp payload err") + } + + a7 := &AccountAction{ + AccountName: common.Name("cccccccc"), + Founder: common.Name(""), + ChargeRatio: 10, + PublicKey: pubkey, + } + payload7, err := rlp.EncodeToBytes(a7) + if err != nil { + panic("rlp payload err") + } + + a8 := &AccountAction{ + AccountName: common.Name("bbbbbbbb.ee"), + Founder: common.Name(""), + ChargeRatio: 10, + PublicKey: pubkey, + } + payload8, err := rlp.EncodeToBytes(a8) + if err != nil { + panic("rlp payload err") + } + + a9 := &AccountAction{ + AccountName: common.Name("bbbbbbbb."), + Founder: common.Name(""), + ChargeRatio: 10, + PublicKey: pubkey, + } + _, err = rlp.EncodeToBytes(a9) + if err == nil { + panic("rlp payload err") + } + + a10 := &AccountAction{ + AccountName: common.Name("bbbbbb"), + Founder: common.Name(""), + ChargeRatio: 10, + PublicKey: pubkey, + } + _, err = rlp.EncodeToBytes(a10) + if err == nil { + panic("rlp payload err") + } + + a11 := &AccountAction{ + AccountName: common.Name("bbbbbbbbbbbbbbbbb"), + Founder: common.Name(""), + ChargeRatio: 10, + PublicKey: pubkey, + } + _, err = rlp.EncodeToBytes(a11) + if err == nil { + panic("rlp payload err") + } + + a12 := &AccountAction{ + AccountName: common.Name("bbbbbbbbbbbbbb.."), + Founder: common.Name(""), + ChargeRatio: 10, + PublicKey: pubkey, + } + _, err = rlp.EncodeToBytes(a12) + if err == nil { + panic("rlp payload err") + } + + a13 := &AccountAction{ + AccountName: common.Name("bbbbbbbbbbbbbbbb.aaa.."), + Founder: common.Name(""), + ChargeRatio: 10, + PublicKey: pubkey, + } + _, err = rlp.EncodeToBytes(a13) + if err == nil { + panic("rlp payload err") + } + + action := types.NewAction(types.CreateAccount, common.Name("a123456789aeee"), common.Name(sysName), 1, 1, 2, big.NewInt(40), payload) + action1 := types.NewAction(types.CreateAccount, common.Name("bbbbbbbb"), common.Name(sysName), 1, 1, 2, big.NewInt(10), payload1) + action2 := types.NewAction(types.CreateAccount, common.Name("bbbbbbbb"), common.Name(sysName), 1, 1, 2, big.NewInt(10), payload2) + action3 := types.NewAction(types.CreateAccount, common.Name("bbbbbbbb"), common.Name(sysName), 1, 1, 2, big.NewInt(10), payload3) + action4 := types.NewAction(types.CreateAccount, common.Name("bbbbbbbb"), common.Name(sysName), 1, 1, 2, big.NewInt(10), payload4) + action5 := types.NewAction(types.CreateAccount, common.Name("bbbbbbbb.cc"), common.Name(sysName), 1, 1, 2, big.NewInt(10), payload5) + action6 := types.NewAction(types.CreateAccount, common.Name("bbbbbbbb.ccc"), common.Name(sysName), 1, 1, 2, big.NewInt(10), payload6) + action7 := types.NewAction(types.CreateAccount, common.Name("a123456789aeee"), common.Name(sysName), 1, 1, 2, big.NewInt(30), payload7) + action8 := types.NewAction(types.CreateAccount, common.Name("cccccccc"), common.Name(sysName), 1, 1, 2, big.NewInt(30), payload8) + + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + {"createaccount", fields{sdb, ast}, args{action}, false}, + {"createsubaccount1", fields{sdb, ast}, args{action1}, false}, + {"createsubaccount2", fields{sdb, ast}, args{action2}, false}, + {"createsubaccount3", fields{sdb, ast}, args{action3}, false}, + {"createsubaccount4", fields{sdb, ast}, args{action4}, true}, + {"createsubaccount5", fields{sdb, ast}, args{action5}, true}, + {"createsubaccount6", fields{sdb, ast}, args{action6}, true}, + {"createsubaccount7", fields{sdb, ast}, args{action7}, false}, + {"createsubaccount8", fields{sdb, ast}, args{action8}, true}, + } + + for _, tt := range tests { + am := &AccountManager{ + sdb: tt.fields.sdb, + ast: tt.fields.ast, + } + if _, err := am.Process(&types.AccountManagerContext{Action: tt.args.action, Number: 0}); (err != nil) != tt.wantErr { + t.Errorf("%q. AccountManager.Process() error = %v, wantErr %v", tt.name, err, tt.wantErr) + } + } } diff --git a/test/systemtestcase/rpc/globalConfig.go b/accountmanager/config.go similarity index 56% rename from test/systemtestcase/rpc/globalConfig.go rename to accountmanager/config.go index 0c808556..9e074092 100644 --- a/test/systemtestcase/rpc/globalConfig.go +++ b/accountmanager/config.go @@ -14,18 +14,20 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package rpc +package accountmanager -import ( - "github.com/fractalplatform/fractal/common" - "github.com/fractalplatform/fractal/crypto" -) +// Config Account Level +type Config struct { + AccountNameLevel uint64 `json:"accountNameLevel"` + AccountNameLength uint64 `json:"accountNameLength"` + SubAccountNameLength uint64 `json:"subAccountNameLength"` +} -var ( - SystemAccountPriKey, _ = crypto.HexToECDSA("289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032") - SystemAccountPubKey = common.HexToPubKey("0x047db227d7094ce215c3a0f57e1bcc732551fe351f94249471934567e0f5dc1bf795962b8cccb87a2eb56b29fbe37d614e2f4c3c45b789ae4f1f51f4cb21972ffd") - Gaslimit = uint64(2000000) - SystemAccount = "ftsystemio" - Minernonce = uint64(0) - MaxTxNumInTxpool = 40960 + 4096 -) +// DefaultAccountNameConf return account config +func DefaultAccountNameConf() *Config { + return &Config{ + AccountNameLevel: 0, + AccountNameLength: 16, + SubAccountNameLength: 0, + } +} diff --git a/accountmanager/error.go b/accountmanager/error.go index 0268462a..e0d3af62 100644 --- a/accountmanager/error.go +++ b/accountmanager/error.go @@ -19,24 +19,34 @@ package accountmanager import "errors" var ( - ErrInsufficientBalance = errors.New("insufficient balance") - ErrNewAccountErr = errors.New("new account err") - ErrAssetIDInvalid = errors.New("asset id invalid") - ErrCreateAccountError = errors.New("create account error") - ErrAccountIsExist = errors.New("account is exist") - ErrAccountIsDestroy = errors.New("account is destory") - ErrAccountNotExist = errors.New("account not exist") - ErrHashIsEmpty = errors.New("hash is empty") - ErrkeyNotSame = errors.New("key not same") - ErrAccountNameInvalid = errors.New("account name is Invalid") - ErrInvalidPubKey = errors.New("invalid public key") - ErrAccountIsNil = errors.New("account object is empty") - ErrCodeIsEmpty = errors.New("code is empty") - ErrAmountValueInvalid = errors.New("amount value is invalid") - ErrAccountAssetNotExist = errors.New("account asset not exist") - ErrUnkownTxType = errors.New("not support action type") - ErrTimeInvalid = errors.New("input time invalid ") - ErrTimeTypeInvalid = errors.New("get snapshot time type invalid ") - ErrChargeRatioInvalid = errors.New("charge ratio value invalid ") - ErrSnapshotTimeNotExist = errors.New("next snapshot time not exist") + ErrInsufficientBalance = errors.New("insufficient balance") + ErrNewAccountErr = errors.New("new account err") + ErrAssetIDInvalid = errors.New("asset id invalid") + ErrCreateAccountError = errors.New("create account error") + ErrAccountInvaid = errors.New("account not permission") + ErrAccountIsExist = errors.New("account is exist") + ErrNameIsExist = errors.New("name is exist") + ErrAccountIsDestroy = errors.New("account is destroy") + ErrAccountNotExist = errors.New("account not exist") + ErrHashIsEmpty = errors.New("hash is empty") + ErrkeyNotSame = errors.New("key not same") + ErrAccountNameInvalid = errors.New("account name is Invalid") + ErrInvalidPubKey = errors.New("invalid public key") + ErrAccountIsNil = errors.New("account object is empty") + ErrCodeIsEmpty = errors.New("code is empty") + ErrAmountValueInvalid = errors.New("amount value is invalid") + ErrAccountAssetNotExist = errors.New("account asset not exist") + ErrUnkownTxType = errors.New("not support action type") + ErrTimeInvalid = errors.New("input time invalid ") + ErrTimeTypeInvalid = errors.New("get snapshot time type invalid ") + ErrChargeRatioInvalid = errors.New("charge ratio value invalid ") + ErrSnapshotTimeNotExist = errors.New("next snapshot time not exist") + ErrAccountManagerNotExist = errors.New("account manager name not exist") + ErrAmountMustZero = errors.New("amount must be zero") + ErrToNameInvalid = errors.New("action to name(Recipient) invalid") + ErrCounterNotExist = errors.New("account global counter not exist") + ErrAccountIdInvalid = errors.New("account id invalid") + ErrInvalidReceiptAsset = errors.New("invalid receipt of asset") + ErrInvalidReceipt = errors.New("invalid receipt") + ErrNegativeValue = errors.New("negative value") ) diff --git a/accountmanager/interface.go b/accountmanager/interface.go index 9c47610e..a9697012 100644 --- a/accountmanager/interface.go +++ b/accountmanager/interface.go @@ -49,8 +49,8 @@ type IAccount interface { IsSuicided() bool SetSuicide() // - IsDestoryed() - SetDestory() + IsDestroyed() + SetDestroy() } //export account manager interface diff --git a/asset/asset.go b/asset/asset.go index 2cbcabec..d2afe9dc 100644 --- a/asset/asset.go +++ b/asset/asset.go @@ -17,17 +17,20 @@ package asset import ( + "fmt" + "math/big" + "strconv" + + "github.com/ethereum/go-ethereum/log" "github.com/fractalplatform/fractal/common" - "github.com/fractalplatform/fractal/params" "github.com/fractalplatform/fractal/state" "github.com/fractalplatform/fractal/utils/rlp" - "math/big" - "strconv" ) -var sysAcct string +//AssetManager is used to access asset +var assetManagerName = "sysAccount" + var ( - //sysAccount is used to access asset assetCountPrefix = "assetCount" assetNameIdPrefix = "assetNameId" assetObjectPrefix = "assetDefinitionObject" @@ -37,16 +40,45 @@ type Asset struct { sdb *state.StateDB } -//New create Asset +func SetAssetNameConfig(config *Config) bool { + if config == nil { + return false + } + + if config.AssetNameLevel < 0 || config.AssetNameLength <= 2 { + return false + } + + if config.AssetNameLevel > 0 { + if config.SubAssetNameLength < 1 { + return false + } + } + + common.SetAssetNameCheckRule(config.AssetNameLevel, config.AssetNameLength, config.SubAssetNameLength) + return true +} + +//SetAssetMangerName set the global asset manager name +func SetAssetMangerName(name common.Name) bool { + if common.IsValidAccountName(name.String()) { + assetManagerName = name.String() + return true + } + return false +} + +//NewAsset New create Asset func NewAsset(sdb *state.StateDB) *Asset { asset := Asset{ sdb: sdb, } - if len(params.DefaultChainconfig.SysName) > 0 { - sysAcct = params.DefaultChainconfig.SysName.String() - } else { - sysAcct = "sysAccount" + + if len(assetManagerName) == 0 { + log.Error("NewAsset error", "name", ErrAssetManagerNotExist, assetManagerName) + return nil } + asset.InitAssetCount() return &asset } @@ -65,7 +97,7 @@ func (a *Asset) GetAssetObjectByTime(assetID uint64, time uint64) (*AssetObject, if assetID == 0 { return nil, ErrAssetIdInvalid } - b, err := a.sdb.GetSnapshot(sysAcct, assetObjectPrefix+strconv.FormatUint(assetID, 10), time) + b, err := a.sdb.GetSnapshot(assetManagerName, assetObjectPrefix+strconv.FormatUint(assetID, 10), time) if err != nil { return nil, err } @@ -79,12 +111,12 @@ func (a *Asset) GetAssetObjectByTime(assetID uint64, time uint64) (*AssetObject, return &asset, nil } -//get assset id by asset name +//GetAssetIdByName get assset id by asset name func (a *Asset) GetAssetIdByName(assetName string) (uint64, error) { if assetName == "" { return 0, ErrAssetNameEmpty } - b, err := a.sdb.Get(sysAcct, assetNameIdPrefix+assetName) + b, err := a.sdb.Get(assetManagerName, assetNameIdPrefix+assetName) if err != nil { return 0, err } @@ -98,7 +130,7 @@ func (a *Asset) GetAssetIdByName(assetName string) (uint64, error) { return assetID, nil } -//get asset founder by id +//GetAssetFounderById get asset founder by id func (a *Asset) GetAssetFounderById(id uint64) (common.Name, error) { ao, err := a.GetAssetObjectById(id) if err != nil { @@ -107,12 +139,12 @@ func (a *Asset) GetAssetFounderById(id uint64) (common.Name, error) { return ao.GetAssetFounder(), nil } -//get asset by asset id +//GetAssetObjectById get asset by asset id func (a *Asset) GetAssetObjectById(id uint64) (*AssetObject, error) { if id == 0 { return nil, ErrAssetIdInvalid } - b, err := a.sdb.Get(sysAcct, assetObjectPrefix+strconv.FormatUint(id, 10)) + b, err := a.sdb.Get(assetManagerName, assetObjectPrefix+strconv.FormatUint(id, 10)) if err != nil { return nil, err } @@ -128,7 +160,7 @@ func (a *Asset) GetAssetObjectById(id uint64) (*AssetObject, error) { //get asset total count func (a *Asset) getAssetCount() (uint64, error) { - b, err := a.sdb.Get(sysAcct, assetCountPrefix) + b, err := a.sdb.Get(assetManagerName, assetCountPrefix) if err != nil { return 0, err } @@ -154,12 +186,12 @@ func (a *Asset) InitAssetCount() { if err != nil { panic(err) } - a.sdb.Put(sysAcct, assetCountPrefix, b) + a.sdb.Put(assetManagerName, assetCountPrefix, b) } return } -// +//GetAllAssetObject get all asset func (a *Asset) GetAllAssetObject() ([]*AssetObject, error) { assetCount, err := a.getAssetCount() if err != nil { @@ -173,12 +205,12 @@ func (a *Asset) GetAllAssetObject() ([]*AssetObject, error) { if err != nil { return nil, err } - assets[i] = asset + assets[i-1] = asset } return assets, nil } -//get asset object by name +//GetAssetObjectByName get asset object by name func (a *Asset) GetAssetObjectByName(assetName string) (*AssetObject, error) { assetID, err := a.GetAssetIdByName(assetName) if err != nil { @@ -187,7 +219,7 @@ func (a *Asset) GetAssetObjectByName(assetName string) (*AssetObject, error) { return a.GetAssetObjectById(assetID) } -//add new asset object and store into database +//addNewAssetObject add new asset object and store into database func (a *Asset) addNewAssetObject(ao *AssetObject) (uint64, error) { if ao == nil { return 0, ErrAssetObjectEmpty @@ -209,19 +241,15 @@ func (a *Asset) addNewAssetObject(ao *AssetObject) (uint64, error) { if err != nil { return 0, err } - //store assetCount - b, err := rlp.EncodeToBytes(&assetCount) - if err != nil { - return 0, err - } - a.sdb.Put(sysAcct, assetObjectPrefix+strconv.FormatUint(assetCount, 10), aobject) - a.sdb.Put(sysAcct, assetNameIdPrefix+ao.GetAssetName(), aid) - a.sdb.Put(sysAcct, assetCountPrefix, b) + a.sdb.Put(assetManagerName, assetObjectPrefix+strconv.FormatUint(assetCount, 10), aobject) + a.sdb.Put(assetManagerName, assetNameIdPrefix+ao.GetAssetName(), aid) + //store assetCount + a.sdb.Put(assetManagerName, assetCountPrefix, aid) return assetCount, nil } -//add an asset and store into database +//SetAssetObject store an asset into database func (a *Asset) SetAssetObject(ao *AssetObject) error { if ao == nil { return ErrAssetObjectEmpty @@ -234,11 +262,11 @@ func (a *Asset) SetAssetObject(ao *AssetObject) error { if err != nil { return err } - a.sdb.Put(sysAcct, assetObjectPrefix+strconv.FormatUint(assetId, 10), b) + a.sdb.Put(assetManagerName, assetObjectPrefix+strconv.FormatUint(assetId, 10), b) return nil } -//Issue Asset Object +//IssueAssetObject Issue Asset Object func (a *Asset) IssueAssetObject(ao *AssetObject) (uint64, error) { if ao == nil { return 0, ErrAssetObjectEmpty @@ -257,8 +285,12 @@ func (a *Asset) IssueAssetObject(ao *AssetObject) (uint64, error) { return assetID, nil } -//issue asset -func (a *Asset) IssueAsset(assetName string, symbol string, amount *big.Int, dec uint64, founder common.Name, owner common.Name, limit *big.Int) error { +//IssueAsset issue asset +func (a *Asset) IssueAsset(assetName string, number uint64, symbol string, amount *big.Int, dec uint64, founder common.Name, owner common.Name, limit *big.Int, contract common.Name) error { + if !common.IsValidAssetName(assetName) { + return fmt.Errorf("%s is invalid", assetName) + } + assetId, err := a.GetAssetIdByName(assetName) if err != nil { return err @@ -266,7 +298,8 @@ func (a *Asset) IssueAsset(assetName string, symbol string, amount *big.Int, dec if assetId > 0 { return ErrAssetIsExist } - ao, err := NewAssetObject(assetName, symbol, amount, dec, founder, owner, limit) + + ao, err := NewAssetObject(assetName, number, symbol, amount, dec, founder, owner, limit, contract) if err != nil { return err } @@ -277,8 +310,8 @@ func (a *Asset) IssueAsset(assetName string, symbol string, amount *big.Int, dec return nil } -//destory asset -func (a *Asset) DestoryAsset(accountName common.Name, assetId uint64, amount *big.Int) error { +//DestroyAsset destroy asset +func (a *Asset) DestroyAsset(accountName common.Name, assetId uint64, amount *big.Int) error { if accountName == "" { return ErrAccountNameNull } @@ -295,16 +328,15 @@ func (a *Asset) DestoryAsset(accountName common.Name, assetId uint64, amount *bi if asset == nil { return ErrAssetNotExist } - if asset.GetAssetOwner() != accountName { - return ErrOwnerMismatch - } - //if asset.GetAssetAmount().Cmp(amount) < 0{ - // return ErrAssetAmountZero - //} + //everyone can destory asset + // if asset.GetAssetOwner() != accountName { + // return ErrOwnerMismatch + // } + var total *big.Int if total = new(big.Int).Sub(asset.GetAssetAmount(), amount); total.Cmp(big.NewInt(0)) < 0 { - return ErrDestoryLimit + return ErrDestroyLimit } asset.SetAssetAmount(total) err = a.SetAssetObject(asset) @@ -314,7 +346,7 @@ func (a *Asset) DestoryAsset(accountName common.Name, assetId uint64, amount *bi return nil } -//increase asset +//IncreaseAsset increase asset, upperlimit == 0 means no upper limit func (a *Asset) IncreaseAsset(accountName common.Name, assetId uint64, amount *big.Int) error { if accountName == "" { return ErrAccountNameNull @@ -335,16 +367,23 @@ func (a *Asset) IncreaseAsset(accountName common.Name, assetId uint64, amount *b if asset.GetAssetOwner() != accountName { return ErrOwnerMismatch } + + //check AddIssue > UpperLimit var AddIssue *big.Int - if AddIssue = new(big.Int).Add(asset.GetAssetAddIssue(), amount); AddIssue.Cmp(asset.GetUpperLimit()) > 0 { + AddIssue = new(big.Int).Add(asset.GetAssetAddIssue(), amount) + if asset.GetUpperLimit().Cmp(big.NewInt(0)) > 0 && AddIssue.Cmp(asset.GetUpperLimit()) > 0 { return ErrUpperLimit } asset.SetAssetAddIssue(AddIssue) + + //check Amount > UpperLimit var total *big.Int - if total = new(big.Int).Add(asset.GetAssetAmount(), amount); total.Cmp(asset.GetUpperLimit()) > 0 { + total = new(big.Int).Add(asset.GetAssetAmount(), amount) + if asset.GetUpperLimit().Cmp(big.NewInt(0)) > 0 && total.Cmp(asset.GetUpperLimit()) > 0 { return ErrUpperLimit } asset.SetAssetAmount(total) + //save err = a.SetAssetObject(asset) if err != nil { return err @@ -352,8 +391,8 @@ func (a *Asset) IncreaseAsset(accountName common.Name, assetId uint64, amount *b return nil } -//change asset owner -func (a *Asset) SetAssetNewOwner(accountName common.Name, assetId uint64, newOwner common.Name) error { +//UpdateAsset change asset info +func (a *Asset) UpdateAsset(accountName common.Name, assetId uint64, Owner common.Name, founderName common.Name, contractName common.Name) error { if accountName == "" { return ErrAccountNameNull } @@ -370,12 +409,14 @@ func (a *Asset) SetAssetNewOwner(accountName common.Name, assetId uint64, newOwn if asset.GetAssetOwner() != accountName { return ErrOwnerMismatch } - asset.SetAssetOwner(newOwner) + asset.SetAssetOwner(Owner) + asset.SetAssetFounder(founderName) + asset.SetAssetContract(contractName) return a.SetAssetObject(asset) } -//asset founder -func (a *Asset) SetAssetFounder(accountName common.Name, assetId uint64, founderName common.Name) error { +//SetAssetNewOwner change asset owner +func (a *Asset) SetAssetNewOwner(accountName common.Name, assetId uint64, newOwner common.Name) error { if accountName == "" { return ErrAccountNameNull } @@ -392,6 +433,73 @@ func (a *Asset) SetAssetFounder(accountName common.Name, assetId uint64, founder if asset.GetAssetOwner() != accountName { return ErrOwnerMismatch } - asset.SetAssetFounder(founderName) + asset.SetAssetOwner(newOwner) return a.SetAssetObject(asset) } + +//SetAssetFounder asset founder +// func (a *Asset) SetAssetFounder(accountName common.Name, assetId uint64, founderName common.Name) error { +// if accountName == "" { +// return ErrAccountNameNull +// } +// if assetId == 0 { +// return ErrAssetIdInvalid +// } +// asset, err := a.GetAssetObjectById(assetId) +// if err != nil { +// return err +// } +// if asset == nil { +// return ErrAssetNotExist +// } +// if asset.GetAssetOwner() != accountName { +// return ErrOwnerMismatch +// } +// asset.SetAssetFounder(founderName) +// return a.SetAssetObject(asset) +// } + +func (a *Asset) IsValidOwner(fromName common.Name, assetName string) bool { + assetNames := common.SplitString(assetName) + if len(assetNames) == 1 { + return true + } + + if !common.IsValidAssetName(assetName) { + return false + } + + var an string + for i := 0; i < len(assetNames)-1; i++ { + if i == 0 { + an = assetNames[i] + } else { + an = an + "." + assetNames[i] + } + + assetId, err := a.GetAssetIdByName(an) + if err != nil { + continue + } + + if assetId <= 0 { + continue + } + + assetObj, err := a.GetAssetObjectById(assetId) + if err != nil { + continue + } + + if assetObj == nil { + continue + } + + if assetObj.GetAssetOwner() == fromName { + log.Debug("Asset create", "name", an, "onwer", assetObj.GetAssetOwner(), "fromName", fromName, "newName", assetName) + return true + } + } + log.Debug("Asset create failed", "account", fromName, "name", assetName) + return false +} diff --git a/asset/asset_object.go b/asset/asset_object.go index b99720af..26837795 100644 --- a/asset/asset_object.go +++ b/asset/asset_object.go @@ -17,43 +17,52 @@ package asset import ( "math/big" - "regexp" "github.com/fractalplatform/fractal/common" ) type AssetObject struct { - AssetId uint64 `json:"assetid,omitempty"` - AssetName string `json:"assetname,omitempty"` - Symbol string `json:"symbol,omitempty"` - Amount *big.Int `json:"amount,omitempty"` - Decimals uint64 `json:"decimals,omitempty"` - Founder common.Name `json:"founder,omitempty"` - Owner common.Name `json:"owner,omitempty"` - AddIssue *big.Int `json:"AddIssue,omitempty"` - UpperLimit *big.Int `json:"UpperLimit,omitempty"` -} - -func NewAssetObject(assetName string, symbol string, amount *big.Int, dec uint64, founder common.Name, owner common.Name, limit *big.Int) (*AssetObject, error) { + AssetId uint64 `json:"assetId,omitempty"` + Number uint64 `json:"number,omitempty"` + AssetName string `json:"assetName"` + Symbol string `json:"symbol"` + Amount *big.Int `json:"amount"` + Decimals uint64 `json:"decimals"` + Founder common.Name `json:"founder"` + Owner common.Name `json:"owner"` + AddIssue *big.Int `json:"addIssue"` + UpperLimit *big.Int `json:"upperLimit"` + Contract common.Name `json:"contract"` +} + +func NewAssetObject(assetName string, number uint64, symbol string, amount *big.Int, dec uint64, founder common.Name, owner common.Name, limit *big.Int, contract common.Name) (*AssetObject, error) { if assetName == "" || symbol == "" || owner == "" { return nil, ErrNewAssetObject } - if amount.Cmp(big.NewInt(0)) < 0 || limit.Cmp(big.NewInt(0)) < 0 || amount.Cmp(limit) > 0 { + if amount.Cmp(big.NewInt(0)) < 0 || limit.Cmp(big.NewInt(0)) < 0 { return nil, ErrNewAssetObject } - reg := regexp.MustCompile("^[a-z0-9]{2,16}$") - if reg.MatchString(assetName) == false { + if limit.Cmp(big.NewInt(0)) > 0 { + if amount.Cmp(limit) > 0 { + return nil, ErrNewAssetObject + } + } + + // reg := regexp.MustCompile("^[a-z0-9]{2,16}$") + + if common.IsValidAssetName(assetName) == false { return nil, ErrNewAssetObject } - if reg.MatchString(symbol) == false { + if common.IsValidAssetName(symbol) == false { return nil, ErrNewAssetObject } ao := AssetObject{ AssetId: 0, + Number: number, AssetName: assetName, Symbol: symbol, Amount: amount, @@ -62,6 +71,7 @@ func NewAssetObject(assetName string, symbol string, amount *big.Int, dec uint64 Owner: owner, AddIssue: amount, UpperLimit: limit, + Contract: contract, } return &ao, nil } @@ -74,6 +84,14 @@ func (ao *AssetObject) SetAssetId(assetId uint64) { ao.AssetId = assetId } +func (ao *AssetObject) GetAssetNumber() uint64 { + return ao.Number +} + +func (ao *AssetObject) SetAssetNumber(number uint64) { + ao.Number = number +} + func (ao *AssetObject) GetSymbol() string { return ao.Symbol } @@ -92,6 +110,7 @@ func (ao *AssetObject) SetDecimals(dec uint64) { func (ao *AssetObject) GetAssetName() string { return ao.AssetName } + func (ao *AssetObject) SetAssetName(assetName string) { ao.AssetName = assetName } @@ -112,6 +131,10 @@ func (ao *AssetObject) GetUpperLimit() *big.Int { return ao.UpperLimit } +func (ao *AssetObject) GetContract() common.Name { + return ao.Contract +} + func (ao *AssetObject) SetAssetAmount(amount *big.Int) { ao.Amount = amount } @@ -131,3 +154,11 @@ func (ao *AssetObject) GetAssetOwner() common.Name { func (ao *AssetObject) SetAssetOwner(owner common.Name) { ao.Owner = owner } + +func (ao *AssetObject) GetAssetContract() common.Name { + return ao.Contract +} + +func (ao *AssetObject) SetAssetContract(contract common.Name) { + ao.Contract = contract +} diff --git a/asset/asset_object_test.go b/asset/asset_object_test.go index c801fcf9..eecc1876 100644 --- a/asset/asset_object_test.go +++ b/asset/asset_object_test.go @@ -41,7 +41,7 @@ func Test_newAssetObject(t *testing.T) { wantErr bool }{ // TODO: Add test cases. - {"normal", args{"ft", "ft", big.NewInt(2), 18, common.Name(""), common.Name("a123"), big.NewInt(999999)}, &AssetObject{0, "ft", "ft", big.NewInt(2), 18, common.Name(""), common.Name("a123"), big.NewInt(2), big.NewInt(999999)}, false}, + {"normal", args{"ft", "ft", big.NewInt(2), 18, common.Name(""), common.Name("a123"), big.NewInt(999999)}, &AssetObject{0, 0, "ft", "ft", big.NewInt(2), 18, common.Name(""), common.Name("a123"), big.NewInt(2), big.NewInt(999999), common.Name("")}, false}, {"shortname", args{"z", "z", big.NewInt(2), 18, common.Name("a123"), common.Name("a123"), big.NewInt(999999)}, nil, true}, {"longname", args{"ftt0123456789ftt12", "zz", big.NewInt(2), 18, common.Name("a123"), common.Name("a123"), big.NewInt(999999)}, nil, true}, {"emptyname", args{"", "z", big.NewInt(2), 18, common.Name("a123"), common.Name("a123"), big.NewInt(999999)}, nil, true}, @@ -53,7 +53,7 @@ func Test_newAssetObject(t *testing.T) { {"emptyname", args{"ft", "#ip0123456789ft", big.NewInt(2), 18, common.Name("a123"), common.Name("a123"), big.NewInt(999999)}, nil, true}, } for _, tt := range tests { - got, err := NewAssetObject(tt.args.assetName, tt.args.symbol, tt.args.amount, tt.args.dec, tt.args.founder, tt.args.owner, tt.args.UpperLimit) + got, err := NewAssetObject(tt.args.assetName, 0, tt.args.symbol, tt.args.amount, tt.args.dec, tt.args.founder, tt.args.owner, tt.args.UpperLimit, common.Name("")) if (err != nil) != tt.wantErr { t.Errorf("%q. newAssetObject() error = %v, wantErr %v", tt.name, err, tt.wantErr) continue diff --git a/asset/asset_test.go b/asset/asset_test.go index 52fc3012..5c779e3c 100644 --- a/asset/asset_test.go +++ b/asset/asset_test.go @@ -102,12 +102,15 @@ func TestAsset_GetAssetObjectByName(t *testing.T) { assetName string } - ao, _ := NewAssetObject("ft", "zz", big.NewInt(1000), 10, common.Name(""), common.Name("a123456789aeee"), big.NewInt(9999999999)) + ao, _ := NewAssetObject("ft", 0, "zz", big.NewInt(1000), 10, common.Name(""), common.Name("a123456789aeee"), big.NewInt(9999999999), common.Name("")) ao.SetAssetId(1) ast.addNewAssetObject(ao) - ao1, _ := NewAssetObject("ft2", "zz2", big.NewInt(1000), 10, common.Name(""), common.Name("a123456789aeee"), big.NewInt(9999999999)) + ao1, _ := NewAssetObject("ft2", 0, "zz2", big.NewInt(1000), 10, common.Name(""), common.Name("a123456789aeee"), big.NewInt(9999999999), common.Name("")) ao1.SetAssetId(2) ast.addNewAssetObject(ao1) + ao2, _ := NewAssetObject("ft0", 0, "zz0", big.NewInt(1000), 0, common.Name(""), common.Name("a123456789aeee"), big.NewInt(9999999999), common.Name("")) + ao1.SetAssetId(2) + ast.addNewAssetObject(ao2) tests := []struct { name string fields fields @@ -118,6 +121,7 @@ func TestAsset_GetAssetObjectByName(t *testing.T) { // TODO: Add test cases. {"getall", fields{astdb}, args{"ft"}, ao, false}, {"getall2", fields{astdb}, args{"ft2"}, ao1, false}, + {"getall3", fields{astdb}, args{"ft0"}, ao2, false}, } for _, tt := range tests { a := &Asset{ @@ -131,6 +135,7 @@ func TestAsset_GetAssetObjectByName(t *testing.T) { if !reflect.DeepEqual(got, tt.want) { t.Errorf("%q. Asset.GetAssetObjectByName() = %v, want %v", tt.name, got, tt.want) } + t.Logf("GetAssetObjectByName asset dec=%v", got.Decimals) } } @@ -142,7 +147,7 @@ func TestAsset_addNewAssetObject(t *testing.T) { ao *AssetObject } - ao3, _ := NewAssetObject("ft3", "zz3", big.NewInt(1000), 10, common.Name(""), common.Name("a123456789aeee"), big.NewInt(9999999999)) + ao3, _ := NewAssetObject("ft3", 0, "zz3", big.NewInt(1000), 10, common.Name(""), common.Name("a123456789aeee"), big.NewInt(9999999999), common.Name("")) //ao1.SetAssetId(3) tests := []struct { @@ -154,7 +159,7 @@ func TestAsset_addNewAssetObject(t *testing.T) { }{ // TODO: Add test cases. {"addnil", fields{astdb}, args{nil}, 0, true}, - {"add", fields{astdb}, args{ao3}, 3, false}, + {"add", fields{astdb}, args{ao3}, 4, false}, } for _, tt := range tests { a := &Asset{ @@ -213,7 +218,7 @@ func TestAsset_GetAssetObjectById(t *testing.T) { id uint64 } - ao, _ := NewAssetObject("ft", "zz", big.NewInt(1000), 10, common.Name(""), common.Name("a123456789aeee"), big.NewInt(9999999999)) + ao, _ := NewAssetObject("ft", 0, "zz", big.NewInt(1000), 10, common.Name(""), common.Name("a123456789aeee"), big.NewInt(9999999999), common.Name("")) ao.SetAssetId(1) ast.IssueAssetObject(ao) tests := []struct { @@ -254,7 +259,7 @@ func TestAsset_getAssetCount(t *testing.T) { wantErr bool }{ // TODO: Add test cases. - {"get", fields{astdb}, 3, false}, + {"get", fields{astdb}, 4, false}, } for _, tt := range tests { a := &Asset{ @@ -269,14 +274,14 @@ func TestAsset_getAssetCount(t *testing.T) { t.Errorf("%q. Asset.getAssetCount() = %v, want %v", tt.name, got, tt.want) } } - ao, _ := NewAssetObject("ft2", "zz2", big.NewInt(1000), 10, common.Name(""), common.Name("a123456789aeee"), big.NewInt(9999999999)) + ao, _ := NewAssetObject("ft2", 0, "zz2", big.NewInt(1000), 10, common.Name(""), common.Name("a123456789aeee"), big.NewInt(9999999999), common.Name("")) //ao.SetAssetId(1) ast.IssueAssetObject(ao) num, err := ast.getAssetCount() if err != nil { t.Errorf("get asset count err") } - if num != 3 { + if num != 4 { t.Errorf("test asset count err") } } @@ -325,9 +330,9 @@ func TestAsset_SetAssetObject(t *testing.T) { ao *AssetObject } - ao4, _ := NewAssetObject("ft4", "zz4", big.NewInt(1000), 10, common.Name(""), common.Name("a123456789aeee"), big.NewInt(9999999999)) + ao4, _ := NewAssetObject("ft4", 0, "zz4", big.NewInt(1000), 10, common.Name(""), common.Name("a123456789aeee"), big.NewInt(9999999999), common.Name("")) ao4.SetAssetId(0) - ao5, _ := NewAssetObject("ft5", "zz5", big.NewInt(1000), 10, common.Name(""), common.Name("a123456789aeee"), big.NewInt(9999999999)) + ao5, _ := NewAssetObject("ft5", 0, "zz5", big.NewInt(1000), 10, common.Name(""), common.Name("a123456789aeee"), big.NewInt(9999999999), common.Name("")) ao5.SetAssetId(55) tests := []struct { name string @@ -357,7 +362,7 @@ func TestAsset_IssueAssetObject(t *testing.T) { type args struct { ao *AssetObject } - ao6, _ := NewAssetObject("ft6", "zz6", big.NewInt(1000), 10, common.Name(""), common.Name("a123456789aeee"), big.NewInt(9999999999)) + ao6, _ := NewAssetObject("ft6", 0, "zz6", big.NewInt(1000), 10, common.Name(""), common.Name("a123456789aeee"), big.NewInt(9999999999), common.Name("")) ao6.SetAssetId(11) tests := []struct { name string @@ -402,12 +407,20 @@ func TestAsset_IssueAsset(t *testing.T) { {"nilsym", fields{astdb}, args{"22", "", big.NewInt(2), 2, common.Name(""), common.Name("11")}, true}, {"exist", fields{astdb}, args{"ft", "3", big.NewInt(2), 2, common.Name(""), common.Name("11")}, true}, {"normal", fields{astdb}, args{"ft22", "23", big.NewInt(2), 2, common.Name(""), common.Name("112345698")}, false}, + // {"normal1", fields{astdb}, args{"ft22.ft33", "23", big.NewInt(2), 2, common.Name(""), common.Name("112345698")}, false}, + // {"normal2", fields{astdb}, args{"ft22.ft44.ft55", "23", big.NewInt(2), 2, common.Name(""), common.Name("112345698")}, false}, + // {"erroronwer", fields{astdb}, args{"ft22.ft44.ft55", "23", big.NewInt(2), 2, common.Name(""), common.Name("11234512")}, true}, + // {"erroronwer1", fields{astdb}, args{"ft23.ft34", "23", big.NewInt(2), 2, common.Name(""), common.Name("11234512")}, true}, + // {"erroronwer2", fields{astdb}, args{"ft23", "24", big.NewInt(2), 2, common.Name(""), common.Name("11234512")}, false}, + // {"erroronwer3", fields{astdb}, args{"ft23.ft34", "24", big.NewInt(2), 2, common.Name(""), common.Name("11234512")}, false}, + // {"erroronwer4", fields{astdb}, args{"ft24.", "25", big.NewInt(2), 2, common.Name(""), common.Name("11234523")}, true}, + // {"erroronwer5", fields{astdb}, args{"ft24..", "25", big.NewInt(2), 2, common.Name(""), common.Name("11234523")}, true}, } for _, tt := range tests { a := &Asset{ sdb: tt.fields.sdb, } - if err := a.IssueAsset(tt.args.assetName, tt.args.symbol, tt.args.amount, tt.args.dec, tt.args.founder, tt.args.owner, big.NewInt(9999999999)); (err != nil) != tt.wantErr { + if err := a.IssueAsset(tt.args.assetName, 0, tt.args.symbol, tt.args.amount, tt.args.dec, tt.args.founder, tt.args.owner, big.NewInt(9999999999), common.Name("")); (err != nil) != tt.wantErr { t.Errorf("%q. Asset.IssueAsset() error = %v, wantErr %v", tt.name, err, tt.wantErr) } } @@ -474,3 +487,36 @@ func TestAsset_SetAssetNewOwner(t *testing.T) { } } } + +func TestAsset_UpdateAsset(t *testing.T) { + type fields struct { + sdb *state.StateDB + } + type args struct { + accountName common.Name + assetId uint64 + Owner common.Name + founder common.Name + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + // TODO: Add test cases + {"nilname", fields{astdb}, args{common.Name(""), 1, common.Name(""), common.Name("")}, true}, + {"wrongassetid", fields{astdb}, args{common.Name("11"), 0, common.Name(""), common.Name("")}, true}, + {"wrongamount", fields{astdb}, args{common.Name("11"), 123, common.Name(""), common.Name("")}, true}, + {"nilfounder", fields{astdb}, args{common.Name("a123456789afff"), 1, common.Name("a123456789aeee"), common.Name("")}, false}, + {"normal", fields{astdb}, args{common.Name("a123456789aeee"), 1, common.Name("a123456789afff"), common.Name("a123456789afff")}, false}, + } + for _, tt := range tests { + a := &Asset{ + sdb: tt.fields.sdb, + } + if err := a.UpdateAsset(tt.args.accountName, tt.args.assetId, tt.args.Owner, tt.args.founder, common.Name("")); (err != nil) != tt.wantErr { + t.Errorf("%q. Asset.updateAsset() error = %v, wantErr %v", tt.name, err, tt.wantErr) + } + } +} diff --git a/wallet/errors.go b/asset/config.go similarity index 64% rename from wallet/errors.go rename to asset/config.go index 6cbb93a8..b4678ee2 100644 --- a/wallet/errors.go +++ b/asset/config.go @@ -14,13 +14,20 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package wallet +package asset -import "errors" +// Config Asset name level +type Config struct { + AssetNameLevel uint64 `json:"assetNameLevel"` + AssetNameLength uint64 `json:"assetNameLength"` + SubAssetNameLength uint64 `json:"subAssetNameLength"` +} -var ( - // ErrNoMatch no key for given address or file - ErrNoMatch = errors.New("no key for given address or file") - // ErrAccountExists account already exists - ErrAccountExists = errors.New("account already exists") -) +// DefaultAssetNameConf return asset config +func DefaultAssetNameConf() *Config { + return &Config{ + AssetNameLevel: 0, + AssetNameLength: 16, + SubAssetNameLength: 0, + } +} diff --git a/asset/error.go b/asset/error.go index ad32253c..d5e563df 100644 --- a/asset/error.go +++ b/asset/error.go @@ -19,17 +19,17 @@ package asset import "errors" var ( - ErrAccountNameNull = errors.New("account name is null") - ErrAssetIsExist = errors.New("asset is exist") - ErrAssetNotExist = errors.New("asset not exist") - ErrOwnerMismatch = errors.New("asset owner mismatch") - ErrAssetNameEmpty = errors.New("asset name is empty") - ErrAssetObjectEmpty = errors.New("asset object is empty") - ErrNewAssetObject = errors.New("create asset object input invalid") - ErrAssetAmountZero = errors.New("asset amount is zero") - ErrUpperLimit = errors.New("asset amount over the issuance limit") - ErrDestoryLimit = errors.New("asset destroy exceeding the lower limit") - ErrAssetCountNotExist = errors.New("asset total count not exist") - ErrAssetIdInvalid = errors.New("asset id invalid") - //ErrAddNewAssetId = errors.New("add new asset return id invalid") + ErrAccountNameNull = errors.New("account name is null") + ErrAssetIsExist = errors.New("asset is exist") + ErrAssetNotExist = errors.New("asset not exist") + ErrOwnerMismatch = errors.New("asset owner mismatch") + ErrAssetNameEmpty = errors.New("asset name is empty") + ErrAssetObjectEmpty = errors.New("asset object is empty") + ErrNewAssetObject = errors.New("create asset object input invalid") + ErrAssetAmountZero = errors.New("asset amount is zero") + ErrUpperLimit = errors.New("asset amount over the issuance limit") + ErrDestroyLimit = errors.New("asset destroy exceeding the lower limit") + ErrAssetCountNotExist = errors.New("asset total count not exist") + ErrAssetIdInvalid = errors.New("asset id invalid") + ErrAssetManagerNotExist = errors.New("asset manager name not exist") ) diff --git a/asset/interface.go b/asset/interface.go index 2c6b6b0c..48d0f317 100644 --- a/asset/interface.go +++ b/asset/interface.go @@ -17,8 +17,9 @@ package asset import ( - "github.com/fractalplatform/fractal/common" "math/big" + + "github.com/fractalplatform/fractal/common" ) //export diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index e76b4621..d50b15b8 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -18,7 +18,10 @@ package blockchain import ( "fmt" + "io" "math/big" + mrand "math/rand" + "strings" "sync" "sync/atomic" "time" @@ -47,39 +50,43 @@ const ( maxTimeFutureBlocks = 30 badBlockLimit = 10 - BlockChainVersion = 3 + //BlockChainVersion ensures that an incompatible database forces a resync from scratch. + BlockChainVersion = 0 ) // BlockChain represents the canonical chain given a database with a genesis // block. The Blockchain manages chain imports, reverts, chain reorganisations. type BlockChain struct { - chainConfig *params.ChainConfig // Chain & network configuration - vmConfig vm.Config // vm configuration - genesisBlock *types.Block // genesis block - db fdb.Database // Low level persistent database to store final content in - mu sync.RWMutex // global mutex for locking chain operations - chainmu sync.RWMutex // blockchain insertion lock - procmu sync.RWMutex // block processor lock - currentBlock atomic.Value // Current head of the block chain - currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!) - stateCache state.Database // State database to reuse between imports (contains state cache) - headerCache *lru.Cache // Cache for the most recent block headers - tdCache *lru.Cache // Cache for the most recent block total difficulties - numberCache *lru.Cache // Cache for the most recent block numbers - bodyCache *lru.Cache // Cache for the most recent block bodies - bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format - blockCache *lru.Cache // Cache for the most recent entire blocks - futureBlocks *lru.Cache // future blocks are blocks added for later processing - badBlocks *lru.Cache // Bad block cache - quit chan struct{} // blockchain quit channel - running int32 // running must be called atomically - procInterrupt int32 // procInterrupt must be atomically called, interrupt signaler for block processing - wg sync.WaitGroup // chain processing wait group for shutting down - senderCacher TxSenderCacher // senderCacher is a concurrent tranaction sender recoverer sender cacher. - fcontroller *ForkController - processor processor.Processor // block processor interface - validator processor.Validator // block and state validator interface - station *BlockchainStation // p2p station + chainConfig *params.ChainConfig // Chain & network configuration + vmConfig vm.Config // vm configuration + genesisBlock *types.Block // genesis block + db fdb.Database // Low level persistent database to store final content in + mu sync.RWMutex // global mutex for locking chain operations + chainmu sync.RWMutex // blockchain insertion lock + procmu sync.RWMutex // block processor lock + currentBlock atomic.Value // Current head of the block chain + irreversibleNumber atomic.Value // irreversible Number of the block chain + + stateCache state.Database // State database to reuse between imports (contains state cache) + + running int32 // running must be called atomically + procInterrupt int32 // procInterrupt must be atomically called, interrupt signaler for block processing + wg sync.WaitGroup // chain processing wait group for shutting down + senderCacher TxSenderCacher // senderCacher is a concurrent tranaction sender recoverer sender cacher. + fcontroller *ForkController // fcontroller + processor processor.Processor // block processor interface + validator processor.Validator // block and state validator interface + station *BlockchainStation // p2p station + + headerCache *lru.Cache // Cache for the most recent block headers + tdCache *lru.Cache // Cache for the most recent block total difficulties + numberCache *lru.Cache // Cache for the most recent block numbers + bodyCache *lru.Cache // Cache for the most recent block bodies + bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format + blockCache *lru.Cache // Cache for the most recent entire blocks + futureBlocks *lru.Cache // future blocks are blocks added for later processing + badBlocks *lru.Cache // Bad block cache + quit chan struct{} // blockchain quit channel } // NewBlockChain returns a fully initialised block chain using information available in the database. @@ -108,7 +115,10 @@ func NewBlockChain(db fdb.Database, vmConfig vm.Config, chainConfig *params.Chai futureBlocks: futureBlocks, badBlocks: badBlocks, senderCacher: senderCacher, - fcontroller: NewForkController(defaultForkConfig, chainConfig), + fcontroller: NewForkController(&ForkConfig{ + ForkBlockNum: chainConfig.ForkedCfg.ForkBlockNum, + Forkpercentage: chainConfig.ForkedCfg.Forkpercentage, + }, chainConfig), } bc.genesisBlock = bc.GetBlockByNumber(0) @@ -140,35 +150,28 @@ func (bc *BlockChain) loadLastBlock() error { return bc.Reset() } + // Make sure the state associated with the block is available + if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil { + // Dangling block without a state associated, init from scratch + log.Warn("Head state missing, repairing chain", "number", currentBlock.Number(), "hash", currentBlock.Hash()) + if err := bc.repair(¤tBlock); err != nil { + return err + } + } + // Everything seems to be fine, set as the head block bc.currentBlock.Store(currentBlock) // Restore the last known head header currentHeader := currentBlock.Header() - if head := rawdb.ReadHeadHeaderHash(bc.db); head != (common.Hash{}) { - if header := bc.GetHeaderByHash(head); header != nil { - currentHeader = header - } - } rawdb.WriteHeadHeaderHash(bc.db, currentHeader.Hash()) - - // Restore the last known head fast block - bc.currentFastBlock.Store(currentBlock) - if head := rawdb.ReadHeadFastBlockHash(bc.db); head != (common.Hash{}) { - if block := bc.GetBlockByHash(head); block != nil { - bc.currentFastBlock.Store(block) - } - } - - // Issue a status log for the user - currentFastBlock := bc.CurrentFastBlock() - blockTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) - fastTd := bc.GetTd(currentFastBlock.Hash(), currentFastBlock.NumberU64()) - log.Info("Loaded most recent local full block", "number", currentBlock.Number(), "hash", currentBlock.Hash(), "td", blockTd) - log.Info("Loaded most recent local fast block", "number", currentFastBlock.Number(), "hash", currentFastBlock.Hash(), "td", fastTd) + inum := rawdb.ReadIrreversibleNumber(bc.db) + bc.irreversibleNumber.Store(inum) + + log.Info("Loaded most recent local full block", "number", currentBlock.Number(), "hash", currentBlock.Hash(), "td", blockTd, "irreversible number", inum) return nil } @@ -177,6 +180,22 @@ func (bc *BlockChain) Reset() error { return bc.ResetWithGenesisBlock(bc.genesisBlock) } +func (bc *BlockChain) repair(head **types.Block) error { + for { + // Abort if we've rewound to a head block that does have associated state + if _, err := state.New((*head).Root(), bc.stateCache); err == nil { + log.Info("Rewound blockchain to past state", "number", (*head).Number(), "hash", (*head).Hash()) + return nil + } + // Otherwise rewind one block and recheck state availability there + block := bc.GetBlock((*head).ParentHash(), (*head).NumberU64()-1) + if block == nil { + return fmt.Errorf("missing block %d [%x]", (*head).NumberU64()-1, (*head).ParentHash()) + } + *head = block + } +} + // ResetWithGenesisBlock purges the entire blockchain, restoring it to the specified genesis state. func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error { // Dump the entire block chain and purge the caches @@ -194,8 +213,12 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error { if err := batch.Write(); err != nil { return err } + + rawdb.WriteIrreversibleNumber(bc.db, bc.genesisBlock.NumberU64()) + bc.currentBlock.Store(bc.genesisBlock) - bc.currentFastBlock.Store(bc.genesisBlock) + bc.irreversibleNumber.Store(bc.genesisBlock.NumberU64()) + return nil } @@ -219,40 +242,13 @@ func (bc *BlockChain) SetHead(head uint64) error { if currentBlock := bc.CurrentBlock(); currentBlock == nil { bc.currentBlock.Store(bc.genesisBlock) } - if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock == nil { - bc.currentFastBlock.Store(bc.genesisBlock) - } + currentBlock := bc.CurrentBlock() - currentFastBlock := bc.CurrentFastBlock() rawdb.WriteHeadBlockHash(bc.db, currentBlock.Hash()) - rawdb.WriteHeadFastBlockHash(bc.db, currentFastBlock.Hash()) return bc.loadLastBlock() } -// FastSyncCommitHead sets the current head block to the one defined by the hash -// irrelevant what the chain contents were prior. -func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error { - // Make sure that both the block as well at its state trie exists - block := bc.GetBlockByHash(hash) - if block == nil { - return fmt.Errorf("non existent block [%x…]", hash[:4]) - } - - stateOut := rawdb.ReadBlockStateOut(bc.db, hash) - if stateOut == nil { - return fmt.Errorf("non existent state block [%x…]", hash[:4]) - } - - // If all checks out, manually set the head block - bc.mu.Lock() - bc.currentBlock.Store(block) - bc.mu.Unlock() - - log.Info("Committed new head block", "number", block.Number(), "hash", hash) - return nil -} - // GasLimit returns the gas limit of the current HEAD block. func (bc *BlockChain) GasLimit() uint64 { return bc.CurrentBlock().GasLimit() @@ -263,9 +259,9 @@ func (bc *BlockChain) CurrentBlock() *types.Block { return bc.currentBlock.Load().(*types.Block) } -// CurrentFastBlock retrieves the current fast-sync head block of the canonical chain. -func (bc *BlockChain) CurrentFastBlock() *types.Block { - return bc.currentFastBlock.Load().(*types.Block) +// IrreversibleNumber retrieves the irreversible block number of the canonical chain. +func (bc *BlockChain) IrreversibleNumber() uint64 { + return bc.irreversibleNumber.Load().(uint64) } // SetProcessor sets the processor required for making state modifications. @@ -328,13 +324,14 @@ func (bc *BlockChain) StateAt(hash common.Hash) (*state.StateDB, error) { // insert injects a new head block into the current block chain. func (bc *BlockChain) insert(batch fdb.Batch, block *types.Block) { - updateHeads := rawdb.ReadCanonicalHash(bc.db, block.NumberU64()) != block.Hash() rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64()) rawdb.WriteHeadBlockHash(batch, block.Hash()) + // store cur block // bc.currentBlock.Store(block) - if updateHeads { - rawdb.WriteHeadFastBlockHash(batch, block.Hash()) - bc.currentFastBlock.Store(block) + + if strings.Compare(block.Coinbase().String(), bc.chainConfig.SysName) == 0 { + rawdb.WriteIrreversibleNumber(batch, block.NumberU64()) + bc.irreversibleNumber.Store(block.NumberU64()) } } @@ -488,38 +485,6 @@ func (bc *BlockChain) procFutureBlocks() { } } -// WriteStatus status of write -type WriteStatus byte - -const ( - NonStatTy WriteStatus = iota - CanonStatTy - SideStatTy -) - -// Rollback is designed to remove a chain of links from the database that aren't certain enough to be valid. -func (bc *BlockChain) Rollback(chain []common.Hash) { - bc.mu.Lock() - defer bc.mu.Unlock() - - for i := len(chain) - 1; i >= 0; i-- { - hash := chain[i] - - if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock.Hash() == hash { - newFastBlock := bc.GetBlock(currentFastBlock.ParentHash(), currentFastBlock.NumberU64()-1) - bc.currentFastBlock.Store(newFastBlock) - rawdb.WriteHeadFastBlockHash(bc.db, newFastBlock.Hash()) - } - if currentBlock := bc.CurrentBlock(); currentBlock.Hash() == hash { - newBlock := bc.GetBlock(currentBlock.ParentHash(), currentBlock.NumberU64()-1) - bc.currentBlock.Store(newBlock) - rawdb.WriteHeadBlockHash(bc.db, newBlock.Hash()) - } - } -} - -var lastWrite uint64 - // WriteBlockWithoutState writes only the block and its metadata to the database, but does not write any state. func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (err error) { bc.wg.Add(1) @@ -532,17 +497,13 @@ func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (e } // WriteBlockWithState writes the block and all associated state to the database. -func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (err error) { +func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (isCanon bool, err error) { bc.wg.Add(1) defer bc.wg.Done() - if block.ParentHash() != bc.CurrentBlock().Hash() { - return ErrSideBlock - } - ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1) if ptd == nil { - return processor.ErrUnknownAncestor + return false, processor.ErrUnknownAncestor } // Make sure no inconsistent state is leaked during insertion bc.mu.Lock() @@ -550,7 +511,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types. externTd := new(big.Int).Add(block.Difficulty(), ptd) if err := bc.WriteTd(block.Hash(), block.NumberU64(), externTd); err != nil { - return err + return false, err } // Write other block data using a batch. @@ -559,32 +520,71 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types. root, err := state.Commit(batch, block.Hash(), block.NumberU64()) if err != nil { - return err + return false, err } triedb := bc.stateCache.TrieDB() if err := triedb.Commit(root, false); err != nil { - return err + return false, err } rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receipts) - rawdb.WriteTxLookupEntries(batch, block) - rawdb.WritePreimages(batch, block.NumberU64(), state.Preimages()) - bc.insert(batch, block) + if bc.vmConfig.ContractLogFlag { + detailtxs := make([]*types.DetailTx, len(receipts)) + for i := 0; i < len(receipts); i++ { + detailtxs[i] = receipts[i].GetInternalTxsLog() + } + rawdb.WriteDetailTxs(batch, block.Hash(), block.NumberU64(), detailtxs) + } + + currentBlock := bc.CurrentBlock() + localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) + + reorg := externTd.Cmp(localTd) > 0 || strings.Compare(block.Coinbase().String(), bc.chainConfig.SysName) == 0 + if !reorg && externTd.Cmp(localTd) == 0 { + // Split same-difficulty blocks by number, then at random + reorg = block.NumberU64() < currentBlock.NumberU64() || (block.NumberU64() == currentBlock.NumberU64() && mrand.Float64() < 0.5) + } + + if reorg { + // Reorganise the chain if the parent is not the head block + if block.ParentHash() != currentBlock.Hash() { + if err := bc.reorgChain(currentBlock, block, batch); err != nil { + if err == errReorgSystemBlock { + goto Target + } + return false, err + } + + } + + // Write the positional metadata for transaction/receipt lookups and preimages + rawdb.WriteTxLookupEntries(batch, block) + rawdb.WritePreimages(batch, block.NumberU64(), state.Preimages()) + isCanon = true + } + + if isCanon { + bc.insert(batch, block) + } + +Target: if err := batch.Write(); err != nil { - return err + return false, err + } + + if isCanon { + bc.currentBlock.Store(block) } - bc.currentBlock.Store(block) bc.futureBlocks.Remove(block.Hash()) - log.Debug("Insert new block", "producer", block.Coinbase(), "number", block.Number(), "hash", block.Hash().String(), "time", block.Time().Int64(), "txs", len(block.Txs), "gas", block.GasUsed()) - return nil + + return isCanon, nil } // InsertChain attempts to insert the given batch of blocks in to the canonical chain or, otherwise, create a fork. func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) { - n, events, _, err := bc.insertChain(chain) - event.SendEvents(events) + n, _, err := bc.insertChain(chain) return n, err } @@ -602,13 +602,13 @@ func (bc *BlockChain) sanityCheck(chain types.Blocks) error { } // insertChain will execute the actual chain insertion and event aggregation. -func (bc *BlockChain) insertChain(chain types.Blocks) (int, []*event.Event, []*types.Log, error) { +func (bc *BlockChain) insertChain(chain types.Blocks) (int, []*types.Log, error) { if len(chain) == 0 { - return 0, nil, nil, nil + return 0, nil, nil } if err := bc.sanityCheck(chain); err != nil { - return 0, nil, nil, err + return 0, nil, err } bc.wg.Add(1) @@ -619,8 +619,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []*event.Event, []*t var ( stats = insertStats{startTime: time.Now()} - events = make([]*event.Event, 0, len(chain)) - lastCanon *types.Block coalescedLogs []*types.Log ) @@ -647,7 +645,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []*event.Event, []*t case err == processor.ErrFutureBlock: max := big.NewInt(time.Now().Unix() + maxTimeFutureBlocks) if block.Time().Cmp(max) > 0 { - return i, events, coalescedLogs, fmt.Errorf("future block: %v > %v", block.Time(), max) + return i, coalescedLogs, fmt.Errorf("future block: %v > %v", block.Time(), max) } bc.futureBlocks.Add(block.Hash(), block) stats.ignored++ @@ -659,50 +657,14 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []*event.Event, []*t stats.queued++ continue case err == processor.ErrPrunedAncestor: - // Block competing with the canonical chain, store in the db, but don't process - // until the competitor TD goes above the canonical TD - currentBlock := bc.CurrentBlock() - localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) - externTd := new(big.Int).Add(bc.GetTd(block.ParentHash(), block.NumberU64()-1), block.Difficulty()) - if localTd.Cmp(externTd) >= 0 { - if err = bc.WriteBlockWithoutState(block, externTd); err != nil { - return i, events, coalescedLogs, err - } - } else { - newchain, err := bc.reorgBlock(currentBlock, block) - if err != nil { - return i, events, coalescedLogs, err - } - for j := 0; j < len(newchain)/2; j++ { - newchain[j], newchain[len(newchain)-1-j] = newchain[len(newchain)-1-j], newchain[j] - } - - bc.chainmu.Unlock() - _, evs, logs, err := bc.insertChain(newchain) - bc.chainmu.Lock() - events, coalescedLogs = evs, logs - if err != nil { - return i, events, coalescedLogs, err - } + coalescedLogs, err := bc.insertSideChain(block) + if err != nil { + return i, coalescedLogs, err } continue case err != nil: bc.reportBlock(block, nil, err) - return i, events, coalescedLogs, err - } - - currentBlock := bc.CurrentBlock() - if currentBlock.Hash() != block.ParentHash() { - // check totally difficulty - localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) - externTd := new(big.Int).Add(bc.GetTd(block.ParentHash(), block.NumberU64()-1), block.Difficulty()) - - if localTd.Cmp(externTd) >= 0 { - if err = bc.WriteBlockWithoutState(block, externTd); err != nil { - return i, events, coalescedLogs, err - } - continue - } + return i, coalescedLogs, err } var parent *types.Block @@ -715,39 +677,90 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []*event.Event, []*t state, err := state.New(parent.Root(), bc.stateCache) if err != nil { - return i, events, coalescedLogs, err + return i, coalescedLogs, err } receipts, logs, usedGas, err := bc.processor.Process(block, state, bc.vmConfig) if err != nil { bc.reportBlock(block, receipts, err) - return i, events, coalescedLogs, err + return i, coalescedLogs, err } err = bc.validator.ValidateState(block, parent, state, receipts, usedGas) if err != nil { bc.reportBlock(block, receipts, err) - return i, events, coalescedLogs, err + return i, coalescedLogs, err + } + + isCanon, err := bc.WriteBlockWithState(block, receipts, state) + if err != nil { + return i, coalescedLogs, err } - if err := bc.WriteBlockWithState(block, receipts, state); err != nil { - return i, events, coalescedLogs, err + if isCanon { + log.Debug("Inserted new block", "number", block.Number(), "hash", block.Hash(), + "txs", len(block.Transactions()), "gas", block.GasUsed()) + coalescedLogs = append(coalescedLogs, logs...) + event.SendEvent(&event.Event{Typecode: event.ChainHeadEv, Data: block}) + } else { + log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(), "diff", block.Difficulty(), + "txs", len(block.Transactions()), "gas", block.GasUsed()) } + stats.processed++ stats.txsCnt += len(block.Txs) stats.usedGas += usedGas stats.report(chain, i) - coalescedLogs = append(coalescedLogs, logs...) - lastCanon = block + + } + + return 0, coalescedLogs, nil +} + +func (bc *BlockChain) insertSideChain(block *types.Block) ([]*types.Log, error) { + var systemBlock bool + if strings.Compare(block.Coinbase().String(), bc.chainConfig.SysName) == 0 { + systemBlock = true } - if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() { - events = append(events, &event.Event{Typecode: event.ChainHeadEv, Data: lastCanon}) + + currentBlock := bc.CurrentBlock() + localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) + externTd := new(big.Int).Add(bc.GetTd(block.ParentHash(), block.NumberU64()-1), block.Difficulty()) + if localTd.Cmp(externTd) >= 0 && !systemBlock { + start := time.Now() + if err := bc.WriteBlockWithoutState(block, externTd); err != nil { + return nil, err + } + + log.Debug("Injected sidechain block", "number", block.Number(), "hash", block.Hash(), + "diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)), + "txs", len(block.Transactions()), "gas", block.GasUsed(), "root", block.Root()) + } else { + var blocks []*types.Block + blocks = append(blocks, block) + parent := bc.GetBlock(block.ParentHash(), block.NumberU64()-1) + for parent != nil && !bc.HasState(parent.Root()) { + blocks = append(blocks, parent) + parent = bc.GetBlock(block.ParentHash(), block.NumberU64()-1) + } + + for j := 0; j < len(blocks)/2; j++ { + blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j] + } + + bc.chainmu.Unlock() + log.Info("Importing sidechain segment", "start", blocks[0].NumberU64(), "end", blocks[len(blocks)-1].NumberU64()) + _, logs, err := bc.insertChain(blocks) + bc.chainmu.Lock() + if err != nil { + return logs, err + } } + return nil, nil - return 0, events, coalescedLogs, nil } -func (bc *BlockChain) reorgBlock(oldBlock, newBlock *types.Block) (types.Blocks, error) { +func (bc *BlockChain) reorgChain(oldBlock, newBlock *types.Block, batch fdb.Batch) error { var ( newChain types.Blocks oldChain types.Blocks @@ -767,10 +780,10 @@ func (bc *BlockChain) reorgBlock(oldBlock, newBlock *types.Block) (types.Blocks, } if oldBlock == nil { - return nil, fmt.Errorf("reorg state not found old block , block hash: %v", oldBlock.Hash().Hex()) + return fmt.Errorf("reorg chain not found oldblock ") } if newBlock == nil { - return nil, fmt.Errorf("reorg state not found new block , block hash: %v", newBlock.Hash().Hex()) + return fmt.Errorf("reorg chain not found newblock ") } for { @@ -783,41 +796,42 @@ func (bc *BlockChain) reorgBlock(oldBlock, newBlock *types.Block) (types.Blocks, deletedTxs = append(deletedTxs, oldBlock.Txs...) oldBlock, newBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1), bc.GetBlock(newBlock.ParentHash(), newBlock.NumberU64()-1) if oldBlock == nil { - return nil, fmt.Errorf("reorg state not found old block , block hash: %v", oldBlock.Hash().Hex()) + return fmt.Errorf("reorg chain not found old block ") } if newBlock == nil { - return nil, fmt.Errorf("reorg state not found new block , block hash: %v", newBlock.Hash().Hex()) + return fmt.Errorf("reorg chain not found new block ") } } // Ensure the user sees large reorgs if len(oldChain) > 0 && len(newChain) > 0 { + if oldChain[len(oldChain)-1].NumberU64() <= bc.IrreversibleNumber() { + log.Warn("Do not accept other cadidate fork the system chain", "hash", newBlock.Hash(), "coinbase", newBlock.Coinbase()) + return errReorgSystemBlock + } + logFn := log.Debug if len(oldChain) > 63 { logFn = log.Warn } - logFn("Chain split detected", "number", commonBlock.Number(), "hash", commonBlock.Hash(), "drop", len(oldChain), "dropfrom", oldChain[0].Hash(), "add", len(newChain), "addfrom", newChain[0].Hash()) + logFn("Chain split detected", "number", commonBlock.Number(), "hash", commonBlock.Hash(), "drop", len(oldChain), "dropNum", oldChain[0].NumberU64(), + "dropfrom", oldChain[0].Hash(), "add", len(newChain), "addNum", newChain[0].NumberU64(), "addfrom", newChain[0].Hash()) } else { log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "newnum", newBlock.Number(), "newhash", newBlock.Hash()) } var addedTxs []*types.Transaction for i := len(newChain) - 1; i >= 0; i-- { - rawdb.WriteTxLookupEntries(bc.db, newChain[i]) + bc.insert(batch, newChain[i]) + rawdb.WriteTxLookupEntries(batch, newChain[i]) addedTxs = append(addedTxs, newChain[i].Txs...) } - diff := types.TxDifference(deletedTxs, addedTxs) - batch := bc.db.NewBatch() for _, tx := range diff { rawdb.DeleteTxLookupEntry(batch, tx.Hash()) } - batch.Write() - if len(oldChain) > 0 { - bc.currentBlock.Store(oldBlock) - } - return newChain, nil + return nil } func (bc *BlockChain) update() { @@ -853,7 +867,6 @@ func (bc *BlockChain) addBadBlock(block *types.Block) { // reportBlock logs a bad block error. func (bc *BlockChain) reportBlock(block *types.Block, receipts []*types.Receipt, err error) { bc.addBadBlock(block) - log.Error(fmt.Sprintf(` ########## BAD BLOCK ######### Error: %v @@ -861,10 +874,10 @@ Error: %v Chain config: %v Number: %v -Hash: 0x%x +Hash: %v ############################## -`, err, bc.chainConfig, block.Number(), block.Hash())) +`, err, bc.chainConfig, block.NumberU64(), block.Hash().Hex())) } // GetBlockNumber retrieves the block number belonging to the given hash from the cache or database @@ -1019,6 +1032,39 @@ func (bc *BlockChain) CalcGasLimit(parent *types.Block) uint64 { return params.CalcGasLimit(parent) } +// ForkUpdate . func (bc *BlockChain) ForkUpdate(block *types.Block, statedb *state.StateDB) error { return bc.fcontroller.update(block, statedb) } + +// Export writes the active chain to the given writer. +func (bc *BlockChain) Export(w io.Writer) error { + return bc.ExportN(w, uint64(0), bc.CurrentBlock().NumberU64()) +} + +// ExportN writes a subset of the active chain to the given writer. +func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { + bc.chainmu.RLock() + defer bc.chainmu.RUnlock() + + if first > last { + return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last) + } + log.Info("Exporting batch of blocks", "count", last-first+1) + + start, reported := time.Now(), time.Now() + for nr := first; nr <= last; nr++ { + block := bc.GetBlockByNumber(nr) + if block == nil { + return fmt.Errorf("export failed on #%d: not found", nr) + } + if err := block.ExtEncodeRLP(w); err != nil { + return err + } + if time.Since(reported) >= 8*time.Second { + log.Info("Exporting blocks", "exported", block.NumberU64()-first, "elapsed", common.PrettyDuration(time.Since(start))) + reported = time.Now() + } + } + return nil +} diff --git a/blockchain/blockchain_test.go b/blockchain/blockchain_test.go index 8a54380a..a06b776e 100644 --- a/blockchain/blockchain_test.go +++ b/blockchain/blockchain_test.go @@ -18,69 +18,91 @@ package blockchain import ( "testing" + "time" - "github.com/fractalplatform/fractal/rawdb" + "github.com/fractalplatform/fractal/params" ) func TestTheLastBlock(t *testing.T) { - genesis, db, chain, st, err := newCanonical(t, tengine) - if err != nil { - t.Error("newCanonical err", err) - } + // printLog(log.LvlDebug) + genesis := DefaultGenesis() + genesis.AllocAccounts = append(genesis.AllocAccounts, getDefaultGenesisAccounts()...) + chain := newCanonical(t, genesis) defer chain.Stop() - prods, ht := makeProduceAndTime(st, 10) - _, _, blocks, err := makeNewChain(t, genesis, chain, &db, len(prods), ht, prods, makeTransferTx) - if err != nil { - t.Error("makeNewChain err", err) - } - if blocks[len(blocks)-1].Hash() != rawdb.ReadHeadBlockHash(chain.db) { - t.Fatalf("Write/Get HeadBlockHash failed") - } + allCadidates, allHeaderTimes := genCanonicalCadidatesAndTimes(genesis) + _, blocks := makeNewChain(t, genesis, chain, allCadidates, allHeaderTimes) + + // check chain block hash + checkBlocksInsert(t, chain, blocks) } -func TestForkChain(t *testing.T) { - genesis, db, chain, st, err := newCanonical(t, tengine) - if err != nil { - t.Error("newCanonical err", err) - } - defer chain.Stop() +func TestSystemForkChain(t *testing.T) { + var ( + allCadidates, allCadidates1 []string + allHeaderTimes, allHeaderTimes1 []uint64 + ) + // printLog(log.LvlTrace) + genesis := DefaultGenesis() - prods, ht := makeProduceAndTime(st, 10) - _, _, blocks, err := makeNewChain(t, genesis, chain, &db, len(prods), ht, prods, nil) - if err != nil { - t.Error("makeNewChain err", err) - } + allCadidates, allHeaderTimes = genCanonicalCadidatesAndTimes(genesis) - prods = append(prods[0:3], prods[10:]...) - ht = append(ht[0:3], ht[10:]...) - genesis1, db1, chain1, _, err := newCanonical(t, tengine) - if err != nil { - t.Error("newCanonical err", err) - } - defer chain.Stop() + allCadidates1 = append(allCadidates1, allCadidates...) + allCadidates1 = append(allCadidates1, "syscadidate0") + allCadidates1 = append(allCadidates1, params.DefaultChainconfig.SysName) - _, _, _, err = makeNewChain(t, genesis1, chain1, &db1, len(prods), ht, prods, makeTransferTx) - if err != nil { - t.Error("makeNewChain err", err) - } - _, err = chain1.InsertChain(blocks) - if err != nil { - t.Error(err) - } - if chain1.CurrentBlock().Hash() != blocks[len(blocks)-1].Hash() { - t.Fatalf("fork chain err! actual hash %x, want hash %x ", chain1.CurrentBlock().Hash(), blocks[len(blocks)-1].Hash()) - } + allHeaderTimes1 = append(allHeaderTimes1, allHeaderTimes...) + allHeaderTimes1 = append(allHeaderTimes1, allHeaderTimes[len(allHeaderTimes)-1]+1000*uint64(time.Millisecond)*3*7) + allHeaderTimes1 = append(allHeaderTimes1, allHeaderTimes1[len(allHeaderTimes1)-1]+1000*uint64(time.Millisecond)*3) + + testFork(t, allCadidates, allCadidates1, allHeaderTimes, allHeaderTimes1) } -func TestFullTxChain(t *testing.T) { - genesis, db, chain, st, err := newCanonical(t, tengine) - if err != nil { - t.Error("newCanonical err", err) - } - prods, ht := makeProduceAndTime(st, 100) - _, _, _, err = makeNewChain(t, genesis, chain, &db, len(prods), ht, prods, makeTransferTx) +func genCanonicalCadidatesAndTimes(genesis *Genesis) ([]string, []uint64) { + var ( + dposEpochNum uint64 = 1 + allCadidates []string + allHeaderTimes []uint64 + ) + + // geaerate block's cadidates and block header time + // system's cadidates headertimes + sysCadidates, sysHeaderTimes := makeSystemCadidatesAndTime(genesis.Timestamp, genesis) + allCadidates = append(allCadidates, sysCadidates...) + allHeaderTimes = append(allHeaderTimes, sysHeaderTimes...) + + // elected cadidates headertimes + cadidates, headerTimes := makeCadidatesAndTime(sysHeaderTimes[len(sysHeaderTimes)-1], genesis, dposEpochNum) + allCadidates = append(allCadidates, cadidates[:12]...) + allHeaderTimes = append(allHeaderTimes, headerTimes[:12]...) + + // elected cadidates headertimes + cadidates, headerTimes = makeCadidatesAndTime(headerTimes[len(headerTimes)-1], genesis, dposEpochNum) + allCadidates = append(allCadidates, cadidates[:12]...) + allHeaderTimes = append(allHeaderTimes, headerTimes[:12]...) + + return allCadidates, allHeaderTimes +} + +func testFork(t *testing.T, cadidates, forkCadidates []string, headerTimes, forkHeaderTimes []uint64) { + genesis := DefaultGenesis() + genesis.AllocAccounts = append(genesis.AllocAccounts, getDefaultGenesisAccounts()...) + chain := newCanonical(t, genesis) + defer chain.Stop() + + chain, _ = makeNewChain(t, genesis, chain, cadidates, headerTimes) + + // generate fork blocks + blocks := generateForkBlocks(t, DefaultGenesis(), forkCadidates, forkHeaderTimes) + + _, err := chain.InsertChain(blocks) if err != nil { - t.Error("makeNewChain err", err) + t.Fatal(err) } + + // check chain block hash + checkBlocksInsert(t, chain, blocks) + + // check if is complete block chain + checkCompleteChain(t, chain) } diff --git a/blockchain/blockgenerator.go b/blockchain/blockgenerator.go index 67e20c63..65b6d368 100644 --- a/blockchain/blockgenerator.go +++ b/blockchain/blockgenerator.go @@ -17,6 +17,7 @@ package blockchain import ( + "fmt" "math/big" "github.com/fractalplatform/fractal/accountmanager" @@ -42,7 +43,7 @@ type BlockGenerator struct { config *params.ChainConfig engine consensus.IEngine - bc *BlockChain + *BlockChain } // SetCoinbase sets the coinbase of the generated block. @@ -61,9 +62,9 @@ func (bg *BlockGenerator) SetCoinbase(addr common.Name) { func (bg *BlockGenerator) OffsetTime(seconds int64) { bg.header.Time.Add(bg.header.Time, new(big.Int).SetInt64(seconds)) if bg.header.Time.Cmp(bg.parent.Header().Time) <= 0 { - panic("block time out of range") + panic(fmt.Sprintf("header time %d less than parent header time %v ", bg.header.Time.Uint64(), bg.parent.Time().Uint64())) } - bg.header.Difficulty = bg.engine.CalcDifficulty(bg.bc, bg.header.Time.Uint64(), bg.parent.Header()) + bg.header.Difficulty = bg.engine.CalcDifficulty(bg, bg.header.Time.Uint64(), bg.parent.Header()) } // AddTx adds a transaction to the generated block. @@ -72,20 +73,16 @@ func (bg *BlockGenerator) AddTx(tx *types.Transaction) { } // TxNonce retrun nonce -func (bg *BlockGenerator) TxNonce(addr common.Name) uint64 { +func (bg *BlockGenerator) TxNonce(name common.Name) uint64 { am, _ := accountmanager.NewAccountManager(bg.statedb) - a, err := am.GetAccountByName(addr) + a, err := am.GetAccountByName(name) if err != nil { - return 0 + panic(fmt.Sprintf("name: %v, GetTxNonce failed: %v", name, err)) } if a == nil { panic("Account Not exist") } - nonce := a.GetNonce() - //if err != nil { - // panic(fmt.Sprintf("addr GetTxNonce failed: %v", err)) - //} - return nonce + return a.GetNonce() } type chainContext struct { @@ -99,16 +96,20 @@ func (cc *chainContext) Author(header *types.Header) (common.Name, error) { // AddTxWithChain adds a transaction to the generated block. func (bg *BlockGenerator) AddTxWithChain(tx *types.Transaction) { if bg.gasPool == nil { - bg.SetCoinbase(bg.bc.genesisBlock.Coinbase()) + bg.SetCoinbase(bg.genesisBlock.Coinbase()) } bg.statedb.Prepare(tx.Hash(), common.Hash{}, len(bg.txs)) - receipt, _, err := bg.bc.processor.ApplyTransaction(&bg.header.Coinbase, bg.gasPool, bg.statedb, bg.header, tx, &bg.header.GasUsed, vm.Config{}) + receipt, _, err := bg.processor.ApplyTransaction(&bg.header.Coinbase, bg.gasPool, bg.statedb, bg.header, tx, &bg.header.GasUsed, vm.Config{}) if err != nil { - panic(err) + panic(fmt.Sprintf(" apply transaction hash:%v ,err %v", tx.Hash().Hex(), err)) } bg.txs = append(bg.txs, tx) bg.receipts = append(bg.receipts, receipt) } + +func (bg *BlockGenerator) CurrentHeader() *types.Header { + return bg.parent.Head +} diff --git a/blockchain/error.go b/blockchain/error.go index 9e045dc7..411d02e0 100644 --- a/blockchain/error.go +++ b/blockchain/error.go @@ -28,6 +28,8 @@ var ( ErrNoGenesis = errors.New("Genesis not found in chain") + errReorgSystemBlock = errors.New("not reorg system block") + errGenesisNoConfig = errors.New("genesis has no chain configuration") errGenesisNoDpos = errors.New("genesis has no dpos configuration") @@ -40,5 +42,5 @@ type GenesisMismatchError struct { } func (e *GenesisMismatchError) Error() string { - return fmt.Sprintf("database already contains an incompatible genesis block (have %x, new %x)", e.Stored[:8], e.New[:8]) + return fmt.Sprintf("database already contains an incompatible genesis block (have %x, new %x)", e.Stored[:], e.New[:]) } diff --git a/blockchain/forkcontroller.go b/blockchain/forkcontroller.go index 9214906f..b2a4ea8a 100644 --- a/blockchain/forkcontroller.go +++ b/blockchain/forkcontroller.go @@ -62,7 +62,7 @@ func NewForkController(cfg *ForkConfig, chaincfg *params.ChainConfig) *ForkContr func (fc *ForkController) getForkInfo(statedb *state.StateDB) (ForkInfo, error) { info := ForkInfo{} - infoBytes, err := statedb.Get(fc.chainCfg.SysName.String(), forkInfo) + infoBytes, err := statedb.Get(fc.chainCfg.ChainName, forkInfo) if err != nil { return info, err } @@ -84,22 +84,18 @@ func (fc *ForkController) putForkInfo(info ForkInfo, statedb *state.StateDB) err return err } - statedb.Put(fc.chainCfg.SysName.String(), forkInfo, infoBytes) + statedb.Put(fc.chainCfg.ChainName, forkInfo, infoBytes) return nil } func (fc *ForkController) update(block *types.Block, statedb *state.StateDB) error { - // first hard fork at a specific height - if block.NumberU64() < params.TheForkNum { - return nil - } - info, err := fc.getForkInfo(statedb) if err != nil { return err } - if block.CurForkID() != block.NextForkID() { + // treat older version as oldest version + if block.CurForkID() != block.NextForkID() && info.NextForkID <= block.NextForkID() { if info.NextForkID < block.NextForkID() { // update next forkID info.NextForkID = block.NextForkID() @@ -108,11 +104,7 @@ func (fc *ForkController) update(block *types.Block, statedb *state.StateDB) err info.NextForkIDBlockNum++ if info.CurForkIDBlockNum+info.NextForkIDBlockNum >= fc.cfg.ForkBlockNum { - if info.CurForkIDBlockNum != 0 { - info.CurForkIDBlockNum-- - } else { - info.NextForkIDBlockNum-- - } + info.CurForkIDBlockNum-- } } else { info.CurForkIDBlockNum++ @@ -143,28 +135,22 @@ func (fc *ForkController) currentForkID(statedb *state.StateDB) (uint64, uint64, } func (fc *ForkController) checkForkID(header *types.Header, state *state.StateDB) error { - // first hard fork at a specific height - if header.Number.Uint64() >= params.TheForkNum { - // check current fork id and next fork id - if curForkID, _, err := fc.currentForkID(state); err != nil { - return err - } else if header.CurForkID() != curForkID || header.NextForkID() < curForkID { - return fmt.Errorf("invild header curForkID: %v, header nextForkID: %v,actual curForkID %v, header hash: %v, header number: %v", - header.CurForkID(), header.NextForkID(), curForkID, header.Hash().Hex(), header.Number.Uint64()) - } + // check current fork id and next fork id + if curForkID, _, err := fc.currentForkID(state); err != nil { + return err + } else if header.CurForkID() != curForkID || header.NextForkID() < curForkID { + return fmt.Errorf("invild header curForkID: %v, header nextForkID: %v,actual curForkID %v, header hash: %v, header number: %v", + header.CurForkID(), header.NextForkID(), curForkID, header.Hash().Hex(), header.Number.Uint64()) } return nil } func (fc *ForkController) fillForkID(header *types.Header, state *state.StateDB) error { - // first hard fork at a specific height - if header.Number.Uint64() >= params.TheForkNum { - // check current fork id and next fork id - curForkID, nextForkID, err := fc.currentForkID(state) - if err != nil { - return err - } - header.WithForkID(curForkID, nextForkID) + // check current fork id and next fork id + curForkID, nextForkID, err := fc.currentForkID(state) + if err != nil { + return err } + header.WithForkID(curForkID, nextForkID) return nil } diff --git a/blockchain/forkcontroller_test.go b/blockchain/forkcontroller_test.go index 75530198..3af4141f 100644 --- a/blockchain/forkcontroller_test.go +++ b/blockchain/forkcontroller_test.go @@ -36,7 +36,7 @@ func TestForkController(t *testing.T) { ) fc := NewForkController(testcfg, params.DefaultChainconfig) - var height = int64(params.TheForkNum) + var height int64 for j := 0; j < 2; j++ { for i := 0; i < 8; i++ { @@ -71,7 +71,7 @@ func TestUpdateDifferentForkBlock(t *testing.T) { ) fc := NewForkController(testcfg, params.DefaultChainconfig) - var height = int64(params.TheForkNum) + var height int64 for j := 0; j < 2; j++ { for i := 0; i < 7; i++ { block := &types.Block{Head: &types.Header{Number: big.NewInt(height)}} @@ -99,7 +99,7 @@ func TestFillForkID(t *testing.T) { fc := NewForkController(testcfg, params.DefaultChainconfig) - header := &types.Header{Number: big.NewInt(int64(params.TheForkNum))} + header := &types.Header{Number: big.NewInt(0)} assert.NoError(t, fc.fillForkID(header, statedb)) diff --git a/blockchain/gen_genesis.go b/blockchain/gen_genesis.go index 121d8765..84feb783 100644 --- a/blockchain/gen_genesis.go +++ b/blockchain/gen_genesis.go @@ -16,90 +16,10 @@ package blockchain -import ( - "encoding/json" - "math/big" +// func (g *Genesis) MarshalJSON() ([]byte, error) { +// return json.Marshal(g) +// } - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/common/math" - "github.com/fractalplatform/fractal/asset" - "github.com/fractalplatform/fractal/common" - "github.com/fractalplatform/fractal/consensus/dpos" - "github.com/fractalplatform/fractal/params" -) - -func (g Genesis) MarshalJSON() ([]byte, error) { - type genesisJSON struct { - Config *params.ChainConfig `json:"config"` - Dpos *dpos.Config `json:"dpos"` - Nonce math.HexOrDecimal64 `json:"nonce"` - Timestamp math.HexOrDecimal64 `json:"timestamp"` - ExtraData hexutil.Bytes `json:"extraData"` - GasLimit math.HexOrDecimal64 `json:"gasLimit"` - Difficulty *math.HexOrDecimal256 `json:"difficulty"` - Mixhash common.Hash `json:"mixHash"` - Coinbase common.Name `json:"coinbase"` - AllocAccounts []*GenesisAccount `json:"allocAccounts"` - AllocAssets []*asset.AssetObject `json:"allocAssets"` - } - var enc genesisJSON - enc.Config = g.Config - enc.Dpos = g.Dpos - enc.Timestamp = math.HexOrDecimal64(g.Timestamp) - enc.ExtraData = g.ExtraData - enc.GasLimit = math.HexOrDecimal64(g.GasLimit) - enc.Difficulty = (*math.HexOrDecimal256)(g.Difficulty) - enc.Coinbase = g.Coinbase - enc.AllocAccounts = g.AllocAccounts - enc.AllocAssets = g.AllocAssets - return json.Marshal(&enc) -} - -func (g *Genesis) UnmarshalJSON(input []byte) error { - type genesisJSON struct { - Config *params.ChainConfig `json:"config"` - Dpos *dpos.Config `json:"dpos"` - Nonce *math.HexOrDecimal64 `json:"nonce"` - Timestamp *math.HexOrDecimal64 `json:"timestamp"` - ExtraData *hexutil.Bytes `json:"extraData"` - GasLimit *math.HexOrDecimal64 `json:"gasLimit"` - Difficulty *math.HexOrDecimal256 `json:"difficulty"` - Mixhash *common.Hash `json:"mixHash"` - Coinbase common.Name `json:"coinbase"` - AllocAccounts []*GenesisAccount `json:"allocAccounts"` - AllocAssets []*asset.AssetObject `json:"allocAssets"` - } - var dec genesisJSON - if err := json.Unmarshal(input, &dec); err != nil { - return err - } - if dec.Config != nil { - g.Config = dec.Config - } - if dec.Dpos != nil { - g.Dpos = dec.Dpos - } - if dec.Timestamp != nil { - g.Timestamp = uint64(*dec.Timestamp) - } - if dec.ExtraData != nil { - g.ExtraData = *dec.ExtraData - } - if dec.GasLimit != nil { - g.GasLimit = uint64(*dec.GasLimit) - } - if dec.Difficulty != nil { - g.Difficulty = (*big.Int)(dec.Difficulty) - } - if len(dec.Coinbase) > 0 { - g.Coinbase = dec.Coinbase - } - if len(dec.AllocAccounts) > 0 { - g.AllocAccounts = dec.AllocAccounts - } - - if len(dec.AllocAssets) > 0 { - g.AllocAssets = dec.AllocAssets - } - return nil -} +// func (g *Genesis) UnmarshalJSON(input []byte) error { +// return json.Unmarshal(input, &g) +// } diff --git a/blockchain/gen_genesis_test.go b/blockchain/gen_genesis_test.go index 66ab0150..aee957ba 100644 --- a/blockchain/gen_genesis_test.go +++ b/blockchain/gen_genesis_test.go @@ -16,6 +16,7 @@ package blockchain import ( + "bytes" "encoding/json" "fmt" "testing" @@ -24,64 +25,22 @@ import ( func TestGenGenesis(t *testing.T) { genesis := DefaultGenesis() - j, err := genesis.MarshalJSON() + j, err := json.Marshal(genesis) if err != nil { t.Fatal(fmt.Sprintf("genesis marshal --- %v", err)) } - if err := genesis.UnmarshalJSON(j); err != nil { + //fmt.Println(string(j)) + + if err := json.Unmarshal(j, &genesis); err != nil { t.Fatal(fmt.Sprintf("genesis Unmarshal --- %v", err)) } - _, err = genesis.MarshalJSON() + nj, err := json.Marshal(genesis) if err != nil { t.Fatal(fmt.Sprintf("genesis marshal --- %v", err)) } - s := `{ - "config": { - "chainId": 1, - "sysName": "ftsystemio", - "sysToken": "fractalfoundation" - }, - "dpos": { - "MaxURLLen": 512, - "UnitStake": 1000, - "ProducerMinQuantity": 10000, - "VoterMinQuantity": 1, - "ActivatedMinQuantity": 1000000, - "BlockInterval": 3000000000, - "BlockFrequency": 6, - "ProducerScheduleSize": 21, - "DelayEcho": 0, - "AccountName": "fdpos", - "SystemName": "ft", - "ExtraBlockRewardUnit": 1, - "BlockReward": 20 - }, - "timestamp": "0x0", - "extraData": "0x5a302047656e6573697320426c6f636b", - "gasLimit": "0x5f5e100", - "difficulty": "0x20000", - "coinbase": "systemio", - "allocAccounts": [{ - "name": "ftsystemio", - "pubKey": "0x047db227d7094ce215c3a0f57e1bcc732551fe351f94249471934567e0f5dc1bf795962b8cccb87a2eb56b29fbe37d614e2f4c3c45b789ae4f1f51f4cb21972ffd" - }], - "allocAssets": [{ - "assetname": "ftperfoundation", - "symbol": "ft", - "amount": 100000000, - "decimals": 18, - "owner": "ftsystemio" - }] - }` - if err := json.Unmarshal([]byte(s), genesis); err != nil { - t.Fatal(fmt.Sprintf("genesis unmarshal --- %v", err)) - } - - j, err = json.Marshal(genesis) - if err != nil { - t.Fatal(fmt.Sprintf("genesis marshal --- %v", err)) + if bytes.Compare(j, nj) != 0 { + t.Fatal(fmt.Sprintf("genesis mismatch --- %v", err)) } - } diff --git a/blockchain/genesis.go b/blockchain/genesis.go index 30029ae1..d8316b91 100644 --- a/blockchain/genesis.go +++ b/blockchain/genesis.go @@ -17,16 +17,19 @@ package blockchain import ( + "encoding/json" + "errors" "fmt" "math/big" + "strings" "time" - "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/fractalplatform/fractal/consensus/dpos" + "github.com/ethereum/go-ethereum/log" am "github.com/fractalplatform/fractal/accountmanager" - "github.com/fractalplatform/fractal/asset" + at "github.com/fractalplatform/fractal/asset" "github.com/fractalplatform/fractal/common" - "github.com/fractalplatform/fractal/consensus/dpos" "github.com/fractalplatform/fractal/p2p/enode" "github.com/fractalplatform/fractal/params" "github.com/fractalplatform/fractal/rawdb" @@ -34,34 +37,77 @@ import ( "github.com/fractalplatform/fractal/types" "github.com/fractalplatform/fractal/utils/fdb" memdb "github.com/fractalplatform/fractal/utils/fdb/memdb" + "github.com/fractalplatform/fractal/utils/rlp" ) // GenesisAccount is an account in the state of the genesis block. type GenesisAccount struct { - Name common.Name `json:"name,omitempty"` + Name string `json:"name,omitempty"` PubKey common.PubKey `json:"pubKey,omitempty"` } +// GenesisCadidate is an cadicate in the state of the genesis block. +type GenesisCadidate struct { + Name string `json:"name,omitempty"` + URL string `json:"url,omitempty"` + Stake *big.Int `json:"stake,omitempty"` +} + +// GenesisAsset is an asset in the state of the genesis block. +type GenesisAsset struct { + Name string `json:"name,omitempty"` + Symbol string `json:"symbol,omitempty"` + Amount *big.Int `json:"amount,omitempty"` + Decimals uint64 `json:"decimals,omitempty"` + Founder string `json:"founder,omitempty"` + Owner string `json:"owner,omitempty"` + UpperLimit *big.Int `json:"upperLimit,omitempty"` +} + // Genesis specifies the header fields, state of a genesis block. type Genesis struct { - Config *params.ChainConfig `json:"config"` - Dpos *dpos.Config `json:"dpos"` - Timestamp uint64 `json:"timestamp"` - ExtraData []byte `json:"extraData"` - GasLimit uint64 `json:"gasLimit" ` - Difficulty *big.Int `json:"difficulty" ` - Coinbase common.Name `json:"coinbase"` - AllocAccounts []*GenesisAccount `json:"allocAccounts"` - AllocAssets []*asset.AssetObject `json:"allocAssets"` + Config *params.ChainConfig `json:"config,omitempty"` + Timestamp uint64 `json:"timestamp,omitempty"` + GasLimit uint64 `json:"gasLimit,omitempty" ` + Difficulty *big.Int `json:"difficulty,omitempty" ` + AllocAccounts []*GenesisAccount `json:"allocAccounts,omitempty"` + AllocCadidates []*GenesisCadidate `json:"allocCadidates,omitempty"` + AllocAssets []*GenesisAsset `json:"allocAssets,omitempty"` +} + +func dposConfig(cfg *params.ChainConfig) *dpos.Config { + return &dpos.Config{ + MaxURLLen: cfg.DposCfg.MaxURLLen, + UnitStake: cfg.DposCfg.UnitStake, + CadidateMinQuantity: cfg.DposCfg.CadidateMinQuantity, + VoterMinQuantity: cfg.DposCfg.VoterMinQuantity, + ActivatedMinQuantity: cfg.DposCfg.ActivatedMinQuantity, + BlockInterval: cfg.DposCfg.BlockInterval, + BlockFrequency: cfg.DposCfg.BlockFrequency, + CadidateScheduleSize: cfg.DposCfg.CadidateScheduleSize, + DelayEcho: cfg.DposCfg.DelayEcho, + AccountName: cfg.DposName, + SystemName: cfg.SysName, + SystemURL: cfg.ChainURL, + ExtraBlockReward: cfg.DposCfg.ExtraBlockReward, + BlockReward: cfg.DposCfg.BlockReward, + Decimals: cfg.SysTokenDecimals, + } } // SetupGenesisBlock The returned chain configuration is never nil. -func SetupGenesisBlock(db fdb.Database, genesis *Genesis) (*params.ChainConfig, *dpos.Config, common.Hash, error) { +func SetupGenesisBlock(db fdb.Database, genesis *Genesis) (chainCfg *params.ChainConfig, dcfg *dpos.Config, hash common.Hash, err error) { + chainCfg = params.DefaultChainconfig + dcfg = dpos.DefaultConfig + hash = common.Hash{} + defer func() { + if e := recover(); e != nil { + err = errors.New(e.(string)) + } + }() + if genesis != nil && genesis.Config == nil { - return params.DefaultChainconfig, dpos.DefaultConfig, common.Hash{}, errGenesisNoConfig - } - if genesis != nil && genesis.Dpos == nil { - return params.DefaultChainconfig, dpos.DefaultConfig, common.Hash{}, errGenesisNoDpos + return params.DefaultChainconfig, dposConfig(params.DefaultChainconfig), common.Hash{}, errGenesisNoConfig } // Just commit the new block if there is no stored genesis block. @@ -72,37 +118,64 @@ func SetupGenesisBlock(db fdb.Database, genesis *Genesis) (*params.ChainConfig, } block, err := genesis.Commit(db) log.Info("Writing genesis block", "hash", block.Hash().Hex()) - return genesis.Config, genesis.Dpos, block.Hash(), err + + return genesis.Config, dposConfig(genesis.Config), block.Hash(), err } // Check whether the genesis block is already written. if genesis != nil { - hash := genesis.ToBlock(nil).Hash() + blk, _ := genesis.ToBlock(nil) + hash := blk.Hash() if hash != stored { - return genesis.Config, genesis.Dpos, hash, &GenesisMismatchError{stored, hash} + return genesis.Config, dposConfig(genesis.Config), hash, &GenesisMismatchError{stored, hash} } } - // Get the existing dpos configuration. - newdpos := genesis.dposOrDefault(stored) - - // Get the existing chain configuration. newcfg := genesis.configOrDefault(stored) height := rawdb.ReadHeaderNumber(db, rawdb.ReadHeadHeaderHash(db)) if height == nil { - return newcfg, newdpos, stored, fmt.Errorf("missing block number for head header hash") + return newcfg, dposConfig(newcfg), common.Hash{}, fmt.Errorf("missing block number for head header hash") } - err := newdpos.Write(db, append([]byte("ft-dpos-"), stored.Bytes()...)) - rawdb.WriteChainConfig(db, stored, newcfg) - return newcfg, newdpos, stored, err + + storedcfg := rawdb.ReadChainConfig(db, stored) + if storedcfg == nil { + return newcfg, dposConfig(newcfg), common.Hash{}, fmt.Errorf("Found genesis block without chain config") + } + am.SetAccountNameConfig(&am.Config{ + AccountNameLevel: storedcfg.AccountNameCfg.Level, + AccountNameLength: storedcfg.AccountNameCfg.Length, + SubAccountNameLength: storedcfg.AccountNameCfg.SubLength, + }) + at.SetAssetNameConfig(&at.Config{ + AssetNameLength: storedcfg.AssetNameCfg.Length, + AssetNameLevel: storedcfg.AssetNameCfg.Level, + SubAssetNameLength: storedcfg.AssetNameCfg.SubLength, + }) + am.SetSysName(common.StrToName(storedcfg.AccountName)) + return storedcfg, dposConfig(storedcfg), stored, nil } // ToBlock creates the genesis block and writes state of a genesis specification // to the given database (or discards it if nil). -func (g *Genesis) ToBlock(db fdb.Database) *types.Block { +func (g *Genesis) ToBlock(db fdb.Database) (*types.Block, []*types.Receipt) { + verify := true if db == nil { + verify = false db = memdb.NewMemDatabase() } + detailTx := &types.DetailTx{} + var internals []*types.DetailAction + am.SetAccountNameConfig(&am.Config{ + AccountNameLevel: g.Config.AccountNameCfg.Level, + AccountNameLength: g.Config.AccountNameCfg.Length, + SubAccountNameLength: g.Config.AccountNameCfg.SubLength, + }) + at.SetAssetNameConfig(&at.Config{ + AssetNameLength: g.Config.AssetNameCfg.Length, + AssetNameLevel: g.Config.AssetNameCfg.Level, + SubAssetNameLength: g.Config.AssetNameCfg.SubLength, + }) + am.SetSysName(common.StrToName(g.Config.AccountName)) number := big.NewInt(0) statedb, err := state.New(common.Hash{}, state.NewDatabase(db)) if err != nil { @@ -114,37 +187,139 @@ func (g *Genesis) ToBlock(db fdb.Database) *types.Block { } //p2p for _, node := range g.Config.BootNodes { + if len(node) == 0 { + continue + } if _, err := enode.ParseV4(node); err != nil { panic(fmt.Sprintf("genesis bootnodes err: %v in %v", err, node)) } } - // dpos - if !common.IsValidName(g.Dpos.SystemName) { - panic(fmt.Sprintf("genesis invalid dpos account name %v", g.Dpos.SystemName)) + actActions := []*types.Action{} + if err := dpos.Genesis(dposConfig(g.Config), statedb, number.Uint64()); err != nil { + panic(fmt.Sprintf("genesis dpos err %v", err)) } - g.AllocAccounts = append(g.AllocAccounts, &GenesisAccount{ - Name: common.StrToName(g.Dpos.AccountName), - PubKey: common.PubKey{}, - }) - if err := dpos.Genesis(g.Dpos, statedb, number.Uint64()); err != nil { - panic(fmt.Sprintf("genesis dpos err %v", g.Dpos.SystemName)) + + chainName := common.Name(g.Config.ChainName) + accoutName := common.Name(g.Config.AccountName) + // chain name + act := &am.AccountAction{ + AccountName: chainName, + PublicKey: common.PubKey{}, } + payload, _ := rlp.EncodeToBytes(act) + actActions = append(actActions, types.NewAction( + types.CreateAccount, + common.Name(""), + accoutName, + 0, + 0, + 0, + big.NewInt(0), + payload, + )) for _, account := range g.AllocAccounts { - if err := accountManager.CreateAccount(account.Name, common.Name(""), 0, account.PubKey); err != nil { - panic(fmt.Sprintf("genesis create account err %v", err)) + pname := common.Name("") + slt := strings.Split(account.Name, ".") + if len(slt) > 1 { + pname = common.Name(slt[0]) } + act := &am.AccountAction{ + AccountName: common.StrToName(account.Name), + PublicKey: account.PubKey, + } + payload, _ := rlp.EncodeToBytes(act) + actActions = append(actActions, types.NewAction( + types.CreateAccount, + pname, + accoutName, + 0, + 0, + 0, + big.NewInt(0), + payload, + )) } + for index, action := range actActions { + internalLogs, err := accountManager.Process(&types.AccountManagerContext{Action: action, Number: 0}) + if err != nil { + panic(fmt.Sprintf("genesis create account %v,err %v", index, err)) + } + internals = append(internals, &types.DetailAction{InternalActions: internalLogs}) + } + + astActions := []*types.Action{} for _, asset := range g.AllocAssets { - if err := accountManager.IssueAsset(asset); err != nil { - panic(fmt.Sprintf("genesis issue asset err %v", err)) + pname := common.Name("") + slt := strings.Split(asset.Name, ".") + if len(slt) > 1 { + if ast, _ := accountManager.GetAssetInfoByName(slt[0]); ast == nil { + panic(fmt.Sprintf("parent asset not exist %v", ast.AssetName)) + } else { + pname = ast.Owner + } + } + ast := &at.AssetObject{ + AssetName: asset.Name, + Symbol: asset.Symbol, + Amount: asset.Amount, + Decimals: asset.Decimals, + Founder: common.StrToName(asset.Founder), + Owner: common.StrToName(asset.Owner), + UpperLimit: asset.UpperLimit, } + payload, _ := rlp.EncodeToBytes(ast) + astActions = append(astActions, types.NewAction( + types.IssueAsset, + pname, + accoutName, + 0, + 0, + 0, + big.NewInt(0), + payload, + )) + } + + for index, action := range astActions { + internalLogs, err := accountManager.Process(&types.AccountManagerContext{Action: action, Number: 0}) + if err != nil { + panic(fmt.Sprintf("genesis create asset %v,err %v", index, err)) + } + internals = append(internals, &types.DetailAction{InternalActions: internalLogs}) + } + + if verify { + if ok, err := accountManager.AccountIsExist(common.StrToName(g.Config.SysName)); !ok { + panic(fmt.Sprintf("system is not exist %v", err)) + } + if ok, err := accountManager.AccountIsExist(common.StrToName(g.Config.AccountName)); !ok { + panic(fmt.Sprintf("account is not exist %v", err)) + } + if ok, err := accountManager.AccountIsExist(common.StrToName(g.Config.DposName)); !ok { + panic(fmt.Sprintf("dpos is not exist %v", err)) + } + assetInfo, err := accountManager.GetAssetInfoByName(g.Config.SysToken) + if err != nil { + panic(fmt.Sprintf("genesis system asset err %v", err)) + } + g.Config.SysTokenID = assetInfo.AssetId + g.Config.SysTokenDecimals = assetInfo.Decimals + } + sys := dpos.NewSystem(statedb, dposConfig(g.Config)) + for _, cadidate := range g.AllocCadidates { + _ = cadidate + _ = sys } root := statedb.IntermediateRoot() - gjson, _ := g.MarshalJSON() + gjson, err := json.Marshal(g) + if err != nil { + panic(fmt.Sprintf("genesis json marshal json err %v", err)) + } + head := &types.Header{ Number: number, Time: new(big.Int).SetUint64(g.Timestamp), @@ -153,11 +328,30 @@ func (g *Genesis) ToBlock(db fdb.Database) *types.Block { GasLimit: g.GasLimit, GasUsed: 0, Difficulty: g.Difficulty, - Coinbase: g.Coinbase, + Coinbase: common.StrToName(g.Config.SysName), Root: root, } - block := types.NewBlock(head, nil, nil) + actions := []*types.Action{} + actions = append(actions, actActions...) + actions = append(actions, astActions...) + tx := types.NewTransaction(0, big.NewInt(0), actions...) + receipt := types.NewReceipt(root[:], 0, 0) + receipt.TxHash = tx.Hash() + for index := range actions { + receipt.ActionResults = append(receipt.ActionResults, &types.ActionResult{ + Status: 1, + Index: uint64(index), + GasUsed: 0, + }) + } + + detailTx.TxHash = receipt.TxHash + detailTx.Actions = internals + receipt.SetInternalTxsLog(detailTx) + + receipts := []*types.Receipt{receipt} + block := types.NewBlock(head, []*types.Transaction{tx}, receipts) batch := db.NewBatch() roothash, err := statedb.Commit(batch, block.Hash(), block.NumberU64()) if err != nil { @@ -167,43 +361,30 @@ func (g *Genesis) ToBlock(db fdb.Database) *types.Block { if err := batch.Write(); err != nil { panic(fmt.Sprintf("genesis batch write err: %v", err)) } - return block + return block, receipts } // Commit writes the block and state of a genesis specification to the database. // The block is committed as the canonical head block. func (g *Genesis) Commit(db fdb.Database) (*types.Block, error) { - block := g.ToBlock(db) + block, receipts := g.ToBlock(db) if block.Number().Sign() != 0 { return nil, fmt.Errorf("can't commit genesis block with number > 0") } rawdb.WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty) rawdb.WriteBlock(db, block) - rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil) + rawdb.WriteTxLookupEntries(db, block) + rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), receipts) rawdb.WriteCanonicalHash(db, block.Hash(), block.NumberU64()) rawdb.WriteHeadBlockHash(db, block.Hash()) rawdb.WriteHeadHeaderHash(db, block.Hash()) - config := g.Config - if config == nil { - config = params.DefaultChainconfig - } - dposConfig := g.Dpos - if dposConfig == nil { - dposConfig = dpos.DefaultConfig - } + rawdb.WriteChainConfig(db, block.Hash(), g.Config) + rawdb.WriteIrreversibleNumber(db, uint64(0)) - rawdb.WriteChainConfig(db, block.Hash(), config) return block, nil } -func (g *Genesis) dposOrDefault(ghash common.Hash) *dpos.Config { - if g != nil { - return g.Dpos - } - return dpos.DefaultConfig -} - func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig { if g != nil { return g.Config @@ -215,36 +396,46 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig { func DefaultGenesis() *Genesis { gtime, _ := time.Parse("2006-01-02 15:04:05.999999999", "2019-01-16 00:00:00") return &Genesis{ - Config: params.DefaultChainconfig, - Dpos: dpos.DefaultConfig, - Timestamp: uint64(gtime.UnixNano()), - ExtraData: hexutil.MustDecode(hexutil.Encode([]byte("ft Genesis Block"))), - GasLimit: params.GenesisGasLimit, - Difficulty: params.GenesisDifficulty, - Coinbase: params.DefaultChainconfig.SysName, - AllocAccounts: DefaultGenesisAccounts(), - AllocAssets: DefaultGenesisAssets(), + Config: params.DefaultChainconfig, + Timestamp: uint64(gtime.UnixNano()), + GasLimit: params.GenesisGasLimit, + Difficulty: params.GenesisDifficulty, + AllocAccounts: DefaultGenesisAccounts(), + AllocCadidates: DefaultGenesisCadidates(), + AllocAssets: DefaultGenesisAssets(), } } // DefaultGenesisAccounts returns the ft net genesis accounts. func DefaultGenesisAccounts() []*GenesisAccount { - pubKey := common.HexToPubKey(params.DefaultPubkeyHex) return []*GenesisAccount{ &GenesisAccount{ Name: params.DefaultChainconfig.SysName, - PubKey: pubKey, + PubKey: common.HexToPubKey("047db227d7094ce215c3a0f57e1bcc732551fe351f94249471934567e0f5dc1bf795962b8cccb87a2eb56b29fbe37d614e2f4c3c45b789ae4f1f51f4cb21972ffd"), + }, + &GenesisAccount{ + Name: params.DefaultChainconfig.AccountName, + PubKey: common.HexToPubKey(""), + }, + &GenesisAccount{ + Name: params.DefaultChainconfig.DposName, + PubKey: common.HexToPubKey(""), }, } } +// DefaultGenesisCadidates returns the ft net genesis cadidates. +func DefaultGenesisCadidates() []*GenesisCadidate { + return []*GenesisCadidate{} +} + // DefaultGenesisAssets returns the ft net genesis assets. -func DefaultGenesisAssets() []*asset.AssetObject { +func DefaultGenesisAssets() []*GenesisAsset { supply := new(big.Int) supply.SetString("100000000000000000000000000000", 10) - return []*asset.AssetObject{ - &asset.AssetObject{ - AssetName: params.DefaultChainconfig.SysToken, + return []*GenesisAsset{ + &GenesisAsset{ + Name: params.DefaultChainconfig.SysToken, Symbol: "ft", Amount: supply, Decimals: 18, diff --git a/blockchain/genesis_test.go b/blockchain/genesis_test.go index 145bcafa..e09edd9d 100644 --- a/blockchain/genesis_test.go +++ b/blockchain/genesis_test.go @@ -17,6 +17,8 @@ package blockchain import ( + "bytes" + "encoding/json" "math/big" "reflect" "testing" @@ -30,10 +32,10 @@ import ( memdb "github.com/fractalplatform/fractal/utils/fdb/memdb" ) -var defaultgenesisBlockHash = common.HexToHash("0x045dae5bce93fa1e02abba85ee6f645bf45ed54b2c60fdf238b03ffeaca46792") +var defaultgenesisBlockHash = common.HexToHash("0x7c65665abff5ddcdeed945b7e373e742f0ceac50040bef2d71e2bbaf8e982d91") func TestDefaultGenesisBlock(t *testing.T) { - block := DefaultGenesis().ToBlock(nil) + block, _ := DefaultGenesis().ToBlock(nil) if block.Hash() != defaultgenesisBlockHash { t.Errorf("wrong mainnet genesis hash, got %v, want %v", block.Hash().Hex(), defaultgenesisBlockHash.Hex()) } @@ -41,18 +43,19 @@ func TestDefaultGenesisBlock(t *testing.T) { func TestSetupGenesis(t *testing.T) { var ( - customghash = common.HexToHash("0x0a7c7f5747fc4c0820fc3322f9367383c827a3417448f5e0f870b69689c6646f") + customghash = common.HexToHash("0x1395829e52af8d75022d363a1645467235f3862fd1dbed71de90fb6cf1f8aeff") customg = Genesis{ - Config: ¶ms.ChainConfig{ChainID: big.NewInt(3), SysName: "systemio", SysToken: "fractalfoundation"}, - Dpos: dpos.DefaultConfig, - Coinbase: "coinbase", - AllocAccounts: DefaultGenesisAccounts(), - AllocAssets: DefaultGenesisAssets(), + Config: params.DefaultChainconfig.Copy(), + AllocAccounts: DefaultGenesisAccounts(), + AllocAssets: DefaultGenesisAssets(), + AllocCadidates: DefaultGenesisCadidates(), } oldcustomg = customg - oldcustomghash = common.HexToHash("0xd713f99f8172e387789aafcfb3bd5b9a3101f1a3dfcce7b696dc44f716c17ad5") + oldcustomghash = common.HexToHash("18212fd24fb4a27d4a35731d85615a0a050970e6b0443db6011c46e6258c5e66") ) - oldcustomg.Config = ¶ms.ChainConfig{ChainID: big.NewInt(2), SysName: "ftsystem", SysToken: "ftoken"} + customg.Config.ChainID = big.NewInt(5) + oldcustomg.Config = customg.Config.Copy() + oldcustomg.Config.ChainID = big.NewInt(6) tests := []struct { name string @@ -69,7 +72,6 @@ func TestSetupGenesis(t *testing.T) { }, wantErr: errGenesisNoConfig, wantConfig: params.DefaultChainconfig, - wantDpos: dpos.DefaultConfig, }, { name: "no block in DB, genesis == nil", @@ -78,7 +80,6 @@ func TestSetupGenesis(t *testing.T) { }, wantHash: defaultgenesisBlockHash, wantConfig: params.DefaultChainconfig, - wantDpos: dpos.DefaultConfig, }, { name: "mainnet block in DB, genesis == nil", @@ -90,7 +91,6 @@ func TestSetupGenesis(t *testing.T) { }, wantHash: defaultgenesisBlockHash, wantConfig: params.DefaultChainconfig, - wantDpos: dpos.DefaultConfig, }, { name: "compatible config in DB", @@ -106,35 +106,33 @@ func TestSetupGenesis(t *testing.T) { }, wantHash: customghash, wantConfig: customg.Config, - wantDpos: customg.Dpos, }, } for _, test := range tests { db := memdb.NewMemDatabase() - config, dpos, hash, err := test.fn(db) + config, _, hash, err := test.fn(db) // Check the return values. if !reflect.DeepEqual(err, test.wantErr) { spew := spew.ConfigState{DisablePointerAddresses: true, DisableCapacities: true} - t.Errorf("%s: returned error %#v, want %#v", test.name, spew.NewFormatter(err), spew.NewFormatter(test.wantErr)) - } - if !reflect.DeepEqual(config, test.wantConfig) { - t.Errorf("%s:\n returned %v\nwant %v", test.name, config, test.wantConfig) + t.Errorf("%s: 1 returned error %#v, want %#v", test.name, spew.NewFormatter(err), spew.NewFormatter(test.wantErr)) } - if !reflect.DeepEqual(dpos, test.wantDpos) { - t.Errorf("%s:\nreturned %v\nwant %v", test.name, config, test.wantConfig) + bts, _ := json.Marshal(config) + wbts, _ := json.Marshal(test.wantConfig) + if bytes.Compare(bts, wbts) != 0 { + t.Errorf("%s:\n 2 returned %v\nwant %v", test.name, config, test.wantConfig) } if hash != test.wantHash { - t.Errorf("%s: returned hash %s, want %s", test.name, hash.Hex(), test.wantHash.Hex()) + t.Errorf("%s: 4 returned hash %s, want %s", test.name, hash.Hex(), test.wantHash.Hex()) } else if err == nil { // Check database content. stored := rawdb.ReadBlock(db, test.wantHash, 0) if stored.Hash() != test.wantHash { - t.Errorf("%s: block in DB has hash %s, want %s", test.name, stored.Hash(), test.wantHash) + t.Errorf("%s: 5 block in DB has hash %s, want %s", test.name, stored.Hash(), test.wantHash) } } } diff --git a/blockchain/test_utils.go b/blockchain/test_utils.go index 43a60a7d..7ac2c707 100644 --- a/blockchain/test_utils.go +++ b/blockchain/test_utils.go @@ -21,10 +21,14 @@ import ( "errors" "fmt" "math/big" + "os" + "strconv" "testing" "time" + "github.com/ethereum/go-ethereum/log" "github.com/fractalplatform/fractal/accountmanager" + am "github.com/fractalplatform/fractal/accountmanager" "github.com/fractalplatform/fractal/common" "github.com/fractalplatform/fractal/consensus" "github.com/fractalplatform/fractal/consensus/dpos" @@ -32,6 +36,7 @@ import ( "github.com/fractalplatform/fractal/params" "github.com/fractalplatform/fractal/processor" "github.com/fractalplatform/fractal/processor/vm" + "github.com/fractalplatform/fractal/rawdb" "github.com/fractalplatform/fractal/state" "github.com/fractalplatform/fractal/txpool" "github.com/fractalplatform/fractal/types" @@ -40,250 +45,265 @@ import ( "github.com/fractalplatform/fractal/utils/rlp" ) -type tdpos struct { - *dpos.Dpos -} - -func (tds *tdpos) VerifySeal(chain consensus.IChainReader, header *types.Header) error { - return nil -} - -func (tds *tdpos) Engine() consensus.IEngine { - return tds -} - var ( - ds = dpos.New(dpos.DefaultConfig, nil) - tengine = &tdpos{ds} - issurevalue = "500000000000000000000000" - sysnameprikey, _ = crypto.HexToECDSA("289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032") - producerScheduleSize = dpos.DefaultConfig.ProducerScheduleSize - blockInterval = dpos.DefaultConfig.BlockInterval - blockFrequency = dpos.DefaultConfig.BlockFrequency - epochInterval = producerScheduleSize * blockFrequency * blockInterval * uint64(time.Millisecond) + issurevalue = "10000000000000000000000000000" + syscadidatePrefix = "syscadidate" + systemPrikey, _ = crypto.HexToECDSA("289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032") ) -type producerInfo struct { +type cadidateInfo struct { name string prikey *ecdsa.PrivateKey } -var producers []*producerInfo +func getCadidates() map[string]*cadidateInfo { + cadidates := make(map[string]*cadidateInfo) + pri0, _ := crypto.HexToECDSA("189c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032") + pri1, _ := crypto.HexToECDSA("9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658") + pri2, _ := crypto.HexToECDSA("8605cf6e76c9fc8ac079d0f841bd5e99bd3ad40fdd56af067993ed14fc5bfca8") + + cadidates["syscadidate0"] = &cadidateInfo{"syscadidate0", pri0} + cadidates["syscadidate1"] = &cadidateInfo{"syscadidate1", pri1} + cadidates["syscadidate2"] = &cadidateInfo{"syscadidate2", pri2} + return cadidates +} -func init() { - producers = make([]*producerInfo, 3) - pri1, _ := crypto.HexToECDSA("189c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032") - pri2, _ := crypto.HexToECDSA("9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658") - pri3, _ := crypto.HexToECDSA("8605cf6e76c9fc8ac079d0f841bd5e99bd3ad40fdd56af067993ed14fc5bfca8") - producers[0] = &producerInfo{"sysproducer1", pri1} - producers[1] = &producerInfo{"sysproducer2", pri2} - producers[2] = &producerInfo{"sysproducer3", pri3} +func getDefaultGenesisAccounts() (gas []*GenesisAccount) { + for i := 0; i < len(getCadidates()); i++ { + cadidate := getCadidates()[syscadidatePrefix+strconv.Itoa(i)] + gas = append(gas, &GenesisAccount{ + Name: cadidate.name, + PubKey: common.BytesToPubKey(crypto.FromECDSAPub(&cadidate.prikey.PublicKey)), + }) + } + return +} +func calculateEpochInterval(dposCfg *dpos.Config) uint64 { + return dposCfg.CadidateScheduleSize * dposCfg.BlockFrequency * dposCfg.BlockInterval * uint64(time.Millisecond) } -func makeProduceAndTime(st uint64, rounds int) ([]string, []uint64) { - baseproducers := []string{"sysproducer1", "sysproducer2", "sysproducer3"} - firsttime := blockInterval*1000*uint64(time.Microsecond) + st - offset := firsttime % epochInterval - offset = offset / (blockInterval * uint64(time.Millisecond)) - var pros []string - for i := 0; i < rounds; i++ { - for j := 0; j < len(baseproducers); j++ { - for k := 0; k < int(blockFrequency); k++ { - pros = append(pros, baseproducers[j]) +func makeSystemCadidatesAndTime(parentTime uint64, genesis *Genesis) ([]string, []uint64) { + var ( + cadidates []string + baseCadidates = getCadidates() + ) + + for i := 0; i < int(genesis.Config.DposCfg.DelayEcho)+1; i++ { + for j := 0; j < len(baseCadidates); j++ { + for k := 0; k < int(genesis.Config.DposCfg.BlockFrequency); k++ { + cadidates = append(cadidates, genesis.Config.SysName) } } - } - pros = pros[offset:] - ht := make([]uint64, len(pros)) - for i := 0; i < len(pros); i++ { - ht[i] = blockInterval*1000*uint64(time.Microsecond)*uint64(i+1) + st + cadidates = cadidates[1:] + headerTimes := make([]uint64, len(cadidates)) + for i := 0; i < len(cadidates); i++ { + headerTimes[i] = genesis.Config.DposCfg.BlockInterval*1000*uint64(time.Microsecond)*uint64(i+1) + parentTime } - return pros, ht + return cadidates, headerTimes } -func newCanonical(t *testing.T, engine consensus.IEngine) (*Genesis, fdb.Database, *BlockChain, uint64, error) { - +func makeCadidatesAndTime(parentTime uint64, genesis *Genesis, rounds uint64) ([]string, []uint64) { var ( - db = memdb.NewMemDatabase() - gspec = DefaultGenesis() - genesis, _ = gspec.Commit(db) + cadidates []string + baseCadidates = getCadidates() ) + for i := 0; uint64(i) < rounds; i++ { + for j := 0; j < len(baseCadidates); j++ { + for k := 0; k < int(genesis.Config.DposCfg.BlockFrequency); k++ { + cadidates = append(cadidates, baseCadidates[syscadidatePrefix+strconv.Itoa(j)].name) + } + } + } + headerTimes := make([]uint64, len(cadidates)) + for i := 0; i < len(cadidates); i++ { + headerTimes[i] = genesis.Config.DposCfg.BlockInterval*1000*uint64(time.Microsecond)*uint64(i+1) + parentTime + } + return cadidates, headerTimes +} +func newCanonical(t *testing.T, genesis *Genesis) *BlockChain { // Initialize a fresh chain with only a genesis block - blockchain, _ := NewBlockChain(db, vm.Config{}, params.DefaultChainconfig, txpool.SenderCacher) + chainDb := memdb.NewMemDatabase() + chainCfg, dposCfg, _, err := SetupGenesisBlock(chainDb, genesis) + if err != nil { + t.Fatal(err) + } - type bc struct { - *BlockChain - consensus.IEngine + blockchain, err := NewBlockChain(chainDb, vm.Config{}, chainCfg, txpool.SenderCacher) + if err != nil { + t.Fatal(err) + } + + statedb, err := blockchain.State() + if err != nil { + t.Fatalf("state db err %v", err) + } + accountManager, err := am.NewAccountManager(statedb) + if err != nil { + t.Fatalf("genesis accountManager new err: %v", err) + } + if ok, err := accountManager.AccountIsExist(common.StrToName(chainCfg.SysName)); !ok { + t.Fatalf("system account is not exist %v", err) } - // Create validator and txProcessor - cfg := blockchain.Config() - cfg.SysTokenID = 1 - validator := processor.NewBlockValidator(&bc{blockchain, engine}, engine) - txProcessor := processor.NewStateProcessor(&bc{blockchain, engine}, engine) + assetInfo, err := accountManager.GetAssetInfoByName(chainCfg.SysToken) + if err != nil { + t.Fatalf("genesis system asset err %v", err) + } + + chainCfg.SysTokenID = assetInfo.AssetId + chainCfg.SysTokenDecimals = assetInfo.Decimals + + engine := dpos.New(dposCfg, blockchain) + bc := struct { + *BlockChain + consensus.IEngine + }{blockchain, engine} + validator := processor.NewBlockValidator(&bc, engine) + txProcessor := processor.NewStateProcessor(&bc, engine) blockchain.SetValidator(validator) blockchain.SetProcessor(txProcessor) - tmpdb, err := deepCopyDB(db) - if err != nil { - return nil, nil, nil, uint64(0), fmt.Errorf("failed to deep copy db: %v", err) - } - starttime := uint64(0) - blocks, _ := generateChain(gspec.Config, genesis, tengine, blockchain, tmpdb, 1, func(i int, block *BlockGenerator) { - genesisname := genesis.Coinbase() - block.SetCoinbase(genesisname) - tengine.SetSignFn(func(content []byte, state *state.StateDB) ([]byte, error) { - return crypto.Sign(content, sysnameprikey) - }) - st := blockInterval*uint64(time.Millisecond) + block.parent.Head.Time.Uint64() - starttime = st - block.OffsetTime(int64(tengine.Slot(st))) - state, err := state.New(genesis.Root(), state.NewDatabase(tmpdb)) - if err != nil { - t.Error("new state failed") - } - txs := makeProducersTx(t, params.DefaultChainconfig.SysName.String(), sysnameprikey, producers, state) - for _, tx := range txs { - block.AddTx(tx) - } - }) - _, err = blockchain.InsertChain(blocks) + return blockchain +} + +func makeNewChain(t *testing.T, genesis *Genesis, chain *BlockChain, cadidates []string, headerTimes []uint64) (*BlockChain, []*types.Block) { + + tmpdb, err := deepCopyDB(chain.db) if err != nil { - t.Error("insert chain err", err) + t.Fatal(err) } - cnt := int(blockFrequency*producerScheduleSize*3) - 2 - for index := 0; index < cnt; index++ { - blocks1, _ := generateChain(gspec.Config, blockchain.CurrentBlock(), tengine, blockchain, tmpdb, 1, func(i int, block *BlockGenerator) { - genesisname := genesis.Coinbase() - block.SetCoinbase(genesisname) - tengine.SetSignFn(func(content []byte, state *state.StateDB) ([]byte, error) { - return crypto.Sign(content, sysnameprikey) + + engine := dpos.New(dposConfig(genesis.Config), chain) + + newblocks, _ := generateChain(genesis.Config, chain.CurrentBlock(), engine, chain, tmpdb, + len(headerTimes), func(i int, b *BlockGenerator) { + + baseCadidates := getCadidates() + + baseCadidates[genesis.Config.SysName] = &cadidateInfo{genesis.Config.SysName, systemPrikey} + + minerInfo := baseCadidates[cadidates[i]] + + b.SetCoinbase(common.StrToName(minerInfo.name)) + engine.SetSignFn(func(content []byte, state *state.StateDB) ([]byte, error) { + return crypto.Sign(content, minerInfo.prikey) }) - st := blockInterval*uint64(time.Millisecond) + starttime - starttime = st - block.OffsetTime(int64(tengine.Slot(st))) + b.OffsetTime(int64(engine.Slot(headerTimes[i]))) + + if i == 0 { + txs := makeCadidatesTx(t, genesis.Config.SysName, systemPrikey, b.statedb) + for _, tx := range txs { + b.AddTx(tx) + } + } + }) - _, err = blockchain.InsertChain(blocks1) - if err != nil { - t.Error("insert chain err", err) - } + + _, err = chain.InsertChain(newblocks) + if err != nil { + t.Fatal("insert chain err", err) } - return gspec, db, blockchain, starttime, err + + return chain, newblocks } -func makeNewChain(t *testing.T, gspec *Genesis, chain *BlockChain, db *fdb.Database, h int, headertime []uint64, miners []string, f MakeTransferTx) (*fdb.Database, *BlockChain, []*types.Block, error) { - var newgenerateblocks []*types.Block - tmpdb, err := deepCopyDB(*db) +func generateForkBlocks(t *testing.T, genesis *Genesis, cadidates []string, headerTimes []uint64) []*types.Block { + genesis.AllocAccounts = append(genesis.AllocAccounts, getDefaultGenesisAccounts()...) + chain := newCanonical(t, genesis) + + tmpdb, err := deepCopyDB(chain.db) if err != nil { - t.Error("copy db err", err) - return nil, nil, nil, err + t.Fatal(err) } - for j := 0; j < h; j++ { - newblocks, _ := generateChain(gspec.Config, chain.CurrentBlock(), tengine, chain, tmpdb, 1, func(i int, b *BlockGenerator) { - var minerInfo *producerInfo - for k := 0; k < len(producers); k++ { - if producers[k].name == miners[j] { - minerInfo = producers[k] - } - } + + engine := dpos.New(dposConfig(genesis.Config), chain) + + newblocks, _ := generateChain(genesis.Config, chain.CurrentBlock(), engine, chain, tmpdb, + len(headerTimes), func(i int, b *BlockGenerator) { + baseCadidates := getCadidates() + + baseCadidates[genesis.Config.SysName] = &cadidateInfo{genesis.Config.SysName, systemPrikey} + minerInfo := baseCadidates[cadidates[i]] + b.SetCoinbase(common.StrToName(minerInfo.name)) - tengine.SetSignFn(func(content []byte, state *state.StateDB) ([]byte, error) { + engine.SetSignFn(func(content []byte, state *state.StateDB) ([]byte, error) { return crypto.Sign(content, minerInfo.prikey) }) - b.OffsetTime(int64(tengine.Slot(headertime[j]))) - state, err := state.New(b.parent.Root(), state.NewDatabase(tmpdb)) - if err != nil { - t.Error("new state failed", err) - } - if f != nil { - tx := f(t, params.DefaultChainconfig.SysName.String(), minerInfo.name, sysnameprikey, state) - b.AddTx(tx) + + b.OffsetTime(int64(engine.Slot(headerTimes[i]))) + + if i == 0 { + txs := makeCadidatesTx(t, genesis.Config.SysName, systemPrikey, b.statedb) + for _, tx := range txs { + b.AddTx(tx) + } } }) - _, err := chain.InsertChain(newblocks) - if err != nil { - t.Error("insert chain err", err) - } - newgenerateblocks = append(newgenerateblocks, newblocks...) - } - return db, chain, newgenerateblocks, err + return newblocks } -func makeProducersTx(t *testing.T, from string, fromprikey *ecdsa.PrivateKey, newaccount []*producerInfo, state *state.StateDB) []*types.Transaction { + +func makeCadidatesTx(t *testing.T, from string, fromprikey *ecdsa.PrivateKey, state *state.StateDB) []*types.Transaction { var txs []*types.Transaction signer := types.NewSigner(params.DefaultChainconfig.ChainID) am, err := accountmanager.NewAccountManager(state) if err != nil { - t.Error("new accountmanager failed") + t.Fatal("new accountmanager failed") } nonce, err := am.GetNonce(common.StrToName(from)) if err != nil { - t.Errorf("get name %s nonce failed", from) + t.Fatalf("get name %s nonce failed", from) } + delegateValue := new(big.Int) - delegateValue.SetString(issurevalue, 0) + delegateValue.SetString(issurevalue, 10) + var actions []*types.Action - for _, to := range newaccount { + for i := 0; i < len(getCadidates()); i++ { amount := new(big.Int).Mul(delegateValue, big.NewInt(2)) - pub := common.BytesToPubKey(crypto.FromECDSAPub(&to.prikey.PublicKey)) - action := types.NewAction(types.CreateAccount, common.StrToName(from), common.StrToName(to.name), nonce, uint64(1), uint64(210000), amount, pub[:]) + action := types.NewAction(types.Transfer, common.StrToName(from), common.StrToName(getCadidates()[syscadidatePrefix+strconv.Itoa(i)].name), nonce, uint64(1), uint64(210000), amount, nil) actions = append(actions, action) nonce++ } tx := types.NewTransaction(uint64(1), big.NewInt(2), actions...) + keyPair := types.MakeKeyPair(fromprikey, []uint64{0}) for _, action := range actions { - err := types.SignAction(action, tx, signer, fromprikey) + err := types.SignActionWithMultiKey(action, tx, signer, []*types.KeyPair{keyPair}) if err != nil { - t.Errorf(fmt.Sprintf("SignAction err %v", err)) + t.Fatalf(fmt.Sprintf("SignAction err %v", err)) } } + txs = append(txs, tx) + var actions1 []*types.Action - for _, to := range newaccount { - value := big.NewInt(1e5) + for i := 0; i < len(getCadidates()); i++ { + to := getCadidates()[syscadidatePrefix+strconv.Itoa(i)] url := "www." + to.name + ".io" - arg := &dpos.RegisterProducer{ - Url: url, - Stake: delegateValue, + arg := &dpos.RegisterCadidate{ + Url: url, } payload, _ := rlp.EncodeToBytes(arg) - action := types.NewAction(types.RegProducer, common.StrToName(to.name), common.StrToName(to.name), 0, uint64(1), uint64(210000), value, payload) + action := types.NewAction(types.RegCadidate, common.StrToName(to.name), common.StrToName(params.DefaultChainconfig.DposName), 0, uint64(1), uint64(210000), delegateValue, payload) actions1 = append(actions1, action) } + tx1 := types.NewTransaction(uint64(1), big.NewInt(2), actions1...) - for i, action := range actions1 { - err := types.SignAction(action, tx1, signer, newaccount[i].prikey) + for _, action := range actions1 { + keyPair = types.MakeKeyPair(getCadidates()[action.Sender().String()].prikey, []uint64{0}) + err := types.SignActionWithMultiKey(action, tx1, signer, []*types.KeyPair{keyPair}) if err != nil { - t.Errorf(fmt.Sprintf("SignAction err %v", err)) + t.Fatalf(fmt.Sprintf("SignAction err %v", err)) } } - txs = append(txs, tx1) - return txs -} -type MakeTransferTx func(t *testing.T, from, to string, fromprikey *ecdsa.PrivateKey, state *state.StateDB) *types.Transaction + txs = append(txs, tx1) -func makeTransferTx(t *testing.T, from, to string, fromprikey *ecdsa.PrivateKey, state *state.StateDB) *types.Transaction { - signer := types.NewSigner(params.DefaultChainconfig.ChainID) - am, err := accountmanager.NewAccountManager(state) - if err != nil { - t.Error("new accountmanager failed") - } - nonce, err := am.GetNonce(common.StrToName(from)) - if err != nil { - t.Errorf("get name %s nonce failed", from) - } - action := types.NewAction(types.Transfer, common.StrToName(from), common.StrToName(to), nonce, uint64(1), uint64(210000), big.NewInt(1), nil) - tx := types.NewTransaction(uint64(1), big.NewInt(2), action) - err = types.SignAction(action, tx, signer, fromprikey) - if err != nil { - t.Errorf(fmt.Sprintf("SignAction err %v", err)) - } - return tx + return txs } func deepCopyDB(db fdb.Database) (fdb.Database, error) { @@ -298,10 +318,13 @@ func generateChain(config *params.ChainConfig, parent *types.Block, engine conse if config == nil { config = params.DefaultChainconfig } + + chain.db = db + blocks, receipts := make(types.Blocks, n), make([][]*types.Receipt, n) genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, []*types.Receipt) { - b := &BlockGenerator{i: i, parent: parent, statedb: statedb, config: config, engine: engine, bc: chain} - b.header = makeHeader(b.bc, parent, statedb, b.engine) + b := &BlockGenerator{i: i, parent: parent, statedb: statedb, config: config, engine: engine, BlockChain: chain} + b.header = makeHeader(b, parent, statedb, b.engine) // Execute any user modifications to the block if gen != nil { @@ -310,33 +333,47 @@ func generateChain(config *params.ChainConfig, parent *types.Block, engine conse if b.engine != nil { // Finalize and seal the block - b.engine.Prepare(b.bc, b.header, b.txs, nil, nil) - block, _ := b.engine.Finalize(b.bc, b.header, b.txs, b.receipts, statedb) - block, err := b.engine.Seal(b.bc, block, nil) + if err := b.engine.Prepare(b, b.header, b.txs, nil, nil); err != nil { + panic(fmt.Sprintf("engine prepare error: %v", err)) + } + + block, err := b.engine.Finalize(b, b.header, b.txs, b.receipts, b.statedb) + if err != nil { + panic(fmt.Sprintf("engine finalize error: %v", err)) + } + + block, err = b.engine.Seal(b, block, nil) + if err != nil { + panic(fmt.Sprintf("engine seal error: %v", err)) + } block.Head.ReceiptsRoot = types.DeriveReceiptsMerkleRoot(b.receipts) block.Head.TxsRoot = types.DeriveTxsMerkleRoot(b.txs) block.Head.Bloom = types.CreateBloom(b.receipts) - batch := db.NewBatch() - root, err := statedb.Commit(batch, block.Hash(), block.NumberU64()) + root, err := b.statedb.Commit(batch, block.Hash(), block.NumberU64()) if err != nil { panic(fmt.Sprintf("state Commit error: %v", err)) } - triedb := statedb.Database().TrieDB() - triedb.Commit(root, false) - if batch.Write() != nil { - panic(fmt.Sprintf("batch Write error: %v", err)) + if err := b.statedb.Database().TrieDB().Commit(root, false); err != nil { + panic(fmt.Sprintf("trie write error: %v", err)) } + + if err := batch.Write(); err != nil { + panic(fmt.Sprintf("batch Write error: %v", err)) + } + + rawdb.WriteHeader(db, block.Head) + return block, b.receipts } return nil, nil } for i := 0; i < n; i++ { - statedb, err := state.New(parent.Root(), state.NewDatabase(db)) + statedb, err := chain.StateAt(parent.Root()) if err != nil { panic(err) } @@ -345,6 +382,7 @@ func generateChain(config *params.ChainConfig, parent *types.Block, engine conse receipts[i] = receipt parent = block } + return blocks, receipts } @@ -358,3 +396,35 @@ func makeHeader(chain consensus.IChainReader, parent *types.Block, state *state. Time: big.NewInt(0), } } + +func printLog(level log.Lvl) { + glogger := log.NewGlogHandler(log.StreamHandler(os.Stdout, log.TerminalFormat(false))) + glogger.Verbosity(level) + log.Root().SetHandler(log.Handler(glogger)) +} + +func checkBlocksInsert(t *testing.T, chain *BlockChain, blocks []*types.Block) { + block := chain.CurrentBlock() + index := 0 + for block.NumberU64() != 0 { + index++ + if blocks[len(blocks)-index].Hash() != block.Hash() { + t.Fatalf("Write/Get HeadBlockHash failed") + } + block = chain.GetBlockByNumber(block.NumberU64() - 1) + } + +} + +func checkCompleteChain(t *testing.T, chain *BlockChain) { + block := chain.CurrentBlock() + for block.NumberU64() != 0 { + parentBlock := chain.GetBlockByNumber(block.NumberU64() - 1) + if block.ParentHash() != parentBlock.Hash() { + t.Fatalf("is not complete chain block num: %v,hash:%v, parent hash: %v \n parent block num: %v, hash: %v,", + block.NumberU64(), block.Hash().Hex(), block.ParentHash().Hex(), + parentBlock.NumberU64(), parentBlock.Hash().Hex()) + } + block = parentBlock + } +} diff --git a/build/eg_config.yaml b/build/eg_config.yaml new file mode 100644 index 00000000..81dc611e --- /dev/null +++ b/build/eg_config.yaml @@ -0,0 +1,127 @@ +# Genesis json file +genesis: "./build/genesis.json" + +# log configuration table +log: + # Prepends log messages with call-site location (file and line number) + printorigins: false + # Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail + level: 4 + # Per-module verbosity: comma-separated list of = (e.g. ft/*=5,p2p=4) + vmodule: "" + # Request a stack trace at a specific logging statement (e.g. \"block.go:271\") + backtraceat: "" + +# node the fractal node configuration table +node: + # the node datadir + datadir: "./build/testdatadir" + # Reduce key-derivation RAM & CPU usage at some expense of KDF strength + lightkdf: false + # RPC:ipc file name + ipcpath: "ft.ipc" + # RPC:http host address + httphost: "localhost" + # RPC:http host port + httpport: 8888 + # RPC:http api's offered over the HTTP-RPC interface + httpmodules: ["ft"] + # RPC:Which to accept cross origin + httpcors: ["localhost"] + # RPC:http virtual hostnames from which to accept requests + httpvirtualhosts: ["*"] + # RPC:websocket host address + wshost: "localhost" + # RPC:websocket host port + wsport: 8889 + # RPC:ws api's offered over the WS-RPC interface + wsmodules: ["ft"] + # RPC:ws origins from which to accept websockets requests + wsorigins: [] + # RPC:ws exposes all API modules via the WebSocket RPC interface rather than just the public ones. + wsexposall: true + # Node list file. BootstrapNodes are used to establish connectivity with the rest of the network + bootnodes: "./build/bootnodes.txt" + # Node list file. Static nodes are used as pre-configured connections which are always maintained and re-connected on disconnects + staticnodes: "./build/staticnodes.txt" + # Node list file. Trusted nodes are usesd as pre-configured connections which are always allowed to connect, even above the peer limit + trustnodes: "./build/trustnodes.txt" + # P2P configuration table + p2p: + # The ID of the p2p network. Nodes have different ID cannot communicate, even if they have same chainID and block data. + networkid: 1 + # The name sets the p2p node name of this server + name: “ft_p2p” + # Maximum number of network peers + maxpeers: 20 + # Maximum number of pending connection attempts + maxpendpeers: 10 + # DialRatio controls the ratio of inbound to dialed connections + dialratio: 10 + # Disables the peer discovery mechanism (manual peer addition) + nodiscover: true + # The path to the database containing the previously seen live nodes in the network + nodedb: "./build/nodedb" + # Network listening address + listenaddr: ":8000" + # The server will not dial any peers. + nodial: false + +# ftservice the fractal service configuration table +ftservice: + # Megabytes of memory allocated to internal database caching + databasecache: 1024 + # txpool configuration table + txpool: + # Disables price exemptions for locally submitted transactions + nolocals: false + # Disk journal for local transaction to survive node restarts + journal: "transactions.rlp" + # Time interval to regenerate the local transaction journal + rejournal: 1h + # Minimum gas price limit to enforce for acceptance into the pool + pricelimit: 2 + # Price bump percentage to replace an already existing transaction + pricebump: 20 + # Number of executable transaction slots guaranteed per account + accountslots: 256 + # Maximum number of executable transaction slots for all accounts + globalslots: 1024 + # Maximum number of non-executable transaction slots permitted per account + accountqueue: 1024 + # Maximum number of non-executable transaction slots for all accounts + globalqueue: 2048 + # Maximum amount of time non-executable transaction are queued + lifetime: 1h + # gas price oracle + gpo: + # Number of recent blocks to check for gas prices + blocks: 30 + # Suggested gas price is the given percentile of a set of recent transaction gas prices + percentile: 50 + miner: + # Start miner generate block and process transaction + start: false + # Name for block mining rewards + name: "testminername" + # Hex of private key for block mining rewards + private: [] + # Block extra data set by the miner + extra: "system" + metrics: + # flag that open statistical metrics + metrics: false + # flag that open influxdb thad store statistical metrics + influxdb: false + # URL that connect influxdb + influxdburl: "http://localhost:8086" + # Influxdb database name + influxdbname: "metrics" + # Indluxdb user name + influxdbuser: "test" + # Influxdb user passwd + influxdbpasswd: "test" + # Influxdb namespace + influxdbnamespace: "fractal/" + # flag for db to store contrat internal transaction log + contractlog: false \ No newline at end of file diff --git a/build/genesis.json b/build/genesis.json index b174d273..80336809 100644 --- a/build/genesis.json +++ b/build/genesis.json @@ -1,48 +1,66 @@ -{ - "config": { - "chainId": 1, - "bootnodes": [], - "sysName": "ftsystemio", - "sysToken": "ftoken", - "assetChargeRatio": 80, - "contractChargeRatio": 80 - }, - "dpos": { - "MaxURLLen": 512, - "UnitStake": 1000, - "ProducerMinQuantity": 10, - "VoterMinQuantity": 1, - "ActivatedMinQuantity": 1000, - "BlockInterval": 3000, - "BlockFrequency": 6, - "ProducerScheduleSize": 3, - "DelayEcho": 2, - "AccountName": "ftsystemdpos", - "SystemName": "ftsystemio", - "SystemURL": "www.fractalproject.com", - "ExtraBlockReward": 1, - "BlockReward": 5, - "Decimals": 18 - }, - "timestamp": "0x0", - "gasLimit": "0x5f5e100", - "difficulty": "0x20000", - "coinbase": "ftsystemio", - "allocAccounts": [ - { - "name": "ftsystemio", - "pubKey": "0x047db227d7094ce215c3a0f57e1bcc732551fe351f94249471934567e0f5dc1bf795962b8cccb87a2eb56b29fbe37d614e2f4c3c45b789ae4f1f51f4cb21972ffd" - } - ], - "allocAssets": [ - { - "assetname": "ftoken", - "symbol": "ft", - "amount": 10000000000000000000000000000, - "decimals": 18, - "owner": "ftsystemio", - "founder": "ftsystemio", - "UpperLimit":10000000000000000000000000000 - } - ] +{ + "config": { + "bootnodes": [], + "chainId": 1, + "chainName": "fractal", + "chainUrl": "https://fractalproject.com", + "accountParams": { + "level": 1, + "length": 16, + "subLength": 8 + }, + "assetParams": { + "level": 1, + "length": 16, + "subLength": 8 + }, + "chargeParams": { + "assetRatio": 80, + "contractRatio": 80 + }, + "upgradeParams": { + "blockCnt": 10000, + "upgradeRatio": 80 + }, + "dposParams": { + "maxURLLen": 512, + "unitStake": 1000, + "cadidateMinQuantity": 10, + "voterMinQuantity": 1, + "activatedMinQuantity": 100, + "blockInterval": 3000, + "blockFrequency": 6, + "cadidateScheduleSize": 3, + "delayEcho": 2, + "extraBlockReward": 1, + "blockReward": 5 + }, + "systemName": "fractal.admin", + "accountName": "fractal.account", + "dposName": "fractal.dpos", + "systemToken": "ftoken" + }, + "timestamp": 1547596800000000000, + "gasLimit": 100000000, + "difficulty": 131072, + "allocAccounts": [{ + "name": "fractal.admin", + "pubKey": "0x047db227d7094ce215c3a0f57e1bcc732551fe351f94249471934567e0f5dc1bf795962b8cccb87a2eb56b29fbe37d614e2f4c3c45b789ae4f1f51f4cb21972ffd" + }, { + "name": "fractal.account", + "pubKey": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, { + "name": "fractal.dpos", + "pubKey": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }], + "allocCadidates": [], + "allocAssets": [{ + "name": "ftoken", + "symbol": "ft", + "amount": 100000000000000000000000000000, + "decimals": 18, + "founder": "fractal.admin", + "owner": "fractal.admin", + "upperLimit": 100000000000000000000000000000 + }] } \ No newline at end of file diff --git a/build/testnet.json b/build/testnet.json index 0c153c89..970cdd91 100644 --- a/build/testnet.json +++ b/build/testnet.json @@ -1,48 +1,66 @@ -{ - "config": { - "chainId": 45, - "bootnodes": ["enode://938597139178830dac79a3c1bcc7be70d41a79a1a1f479cde73862a3d2b808e83f60c6b7d9bd4ff999403828d2b4b5a27ff7323b3467ba3f45db6150db943c32@120.131.7.228:2018","enode://e30016f87e9f32895039c5b4fbbb40fbbf04fc2ef93299124a115df8344d81fff070591b2e455f043d2ccc9ffab3b881ce89c426ae111a5400034bb0fe64e1fa@120.131.7.101:2018"], - "sysName": "ftsystemio", - "sysToken": "ftoken", - "assetChargeRatio": 80, - "contractChargeRatio": 80 - }, - "dpos": { - "MaxURLLen": 512, - "UnitStake": 1000, - "ProducerMinQuantity": 10, - "VoterMinQuantity": 1, - "ActivatedMinQuantity": 1000, - "BlockInterval": 3000, - "BlockFrequency": 6, - "ProducerScheduleSize": 15, - "DelayEcho": 2, - "AccountName": "ftsystemdpos", - "SystemName": "ftsystemio", - "SystemURL": "www.fractalproject.com", - "ExtraBlockReward": 1, - "BlockReward": 5, - "Decimals": 18 - }, - "timestamp": "0x0", - "gasLimit": "0x5f5e100", - "difficulty": "0x20000", - "coinbase": "ftsystemio", - "allocAccounts": [ - { - "name": "ftsystemio", - "pubKey": "0x04474da0e3024d2089888dd60b7cdd79784725c4074f207b09dd7caa63a5b6ceb226b7141f4720c921003b54be581c84a05072c2d4752a1f5fae698684753424c5" - } - ], - "allocAssets": [ - { - "assetname": "ftoken", - "symbol": "ft", - "amount": 10000000000000000000000000000, - "decimals": 18, - "owner": "ftsystemio", - "founder": "ftsystemio", - "UpperLimit":10000000000000000000000000000 - } - ] +{ + "config": { + "bootnodes": ["enode://938597139178830dac79a3c1bcc7be70d41a79a1a1f479cde73862a3d2b808e83f60c6b7d9bd4ff999403828d2b4b5a27ff7323b3467ba3f45db6150db943c32@120.131.7.228:2018","enode://e30016f87e9f32895039c5b4fbbb40fbbf04fc2ef93299124a115df8344d81fff070591b2e455f043d2ccc9ffab3b881ce89c426ae111a5400034bb0fe64e1fa@120.131.7.101:2018"], + "chainId": 1, + "chainName": "fractal", + "chainUrl": "https://fractalproject.com", + "accountParams": { + "level": 1, + "length": 16, + "subLength": 8 + }, + "assetParams": { + "level": 1, + "length": 16, + "subLength": 8 + }, + "chargeParams": { + "assetRatio": 80, + "contractRatio": 80 + }, + "upgradeParams": { + "blockCnt": 10000, + "upgradeRatio": 80 + }, + "dposParams": { + "maxURLLen": 512, + "unitStake": 1000, + "cadidateMinQuantity": 10, + "voterMinQuantity": 1, + "activatedMinQuantity": 100, + "blockInterval": 3000, + "blockFrequency": 6, + "cadidateScheduleSize": 3, + "delayEcho": 2, + "extraBlockReward": 1, + "blockReward": 5 + }, + "systemName": "fractal.admin", + "accountName": "fractal.account", + "dposName": "fractal.dpos", + "systemToken": "ftoken" + }, + "timestamp": 1547596800000000000, + "gasLimit": 100000000, + "difficulty": 131072, + "allocAccounts": [{ + "name": "fractal.admin", + "pubKey": "0x047db227d7094ce215c3a0f57e1bcc732551fe351f94249471934567e0f5dc1bf795962b8cccb87a2eb56b29fbe37d614e2f4c3c45b789ae4f1f51f4cb21972ffd" + }, { + "name": "fractal.account", + "pubKey": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, { + "name": "fractal.dpos", + "pubKey": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }], + "allocCadidates": [], + "allocAssets": [{ + "name": "ftoken", + "symbol": "ft", + "amount": 100000000000000000000000000000, + "decimals": 18, + "founder": "fractal.admin", + "owner": "fractal.admin", + "upperLimit": 100000000000000000000000000000 + }] } \ No newline at end of file diff --git a/cmd/ft/accountcmd.go b/cmd/ft/accountcmd.go deleted file mode 100644 index 0e28f72e..00000000 --- a/cmd/ft/accountcmd.go +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package main - -import ( - "fmt" - "io/ioutil" - "os" - "path/filepath" - - "github.com/spf13/cobra" - - "github.com/fractalplatform/fractal/common" - "github.com/fractalplatform/fractal/crypto" - "github.com/fractalplatform/fractal/utils/console" - "github.com/fractalplatform/fractal/wallet" - "github.com/fractalplatform/fractal/wallet/cache" - "github.com/fractalplatform/fractal/wallet/keystore" -) - -var walletCmd = &cobra.Command{ - Use: "wallet", - Short: "Manage Fractal presale wallets", - Long: ` - fractal wallet import /path/to/my/presale.wallet - -will prompt for your password and imports your presale account.`, - Run: func(cmd *cobra.Command, args []string) { - }, -} - -var importWalletCmd = &cobra.Command{ - Use: "import", - Short: "Import Fractal presale wallet", - Long: ` - fractal wallet [options] /path/to/my/presale.wallet - -will prompt for your password and imports your presale account.`, - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - keyfile := args[0] - keyJSON, err := ioutil.ReadFile(keyfile) - if err != nil { - fmt.Println("Could not read wallet file: ", err) - return - } - - passphrase := getPassPhrase("", false) - w, err := getWallet() - if err != nil { - fmt.Println("get wallet error ", err) - return - } - acct, err := w.Import(keyJSON, passphrase, passphrase) - if err != nil { - fmt.Println("import wallet error ", err) - } - fmt.Printf("Address: {%x}\n", acct.Addr) - }, -} - -var accountCmd = &cobra.Command{ - Use: "account", - Short: "Manage accounts", - Long: ` - - Manage accounts, list all existing accounts, import a private key into a new - account, create a new account or update an existing account. - - It supports interactive mode, when you are prompted for password. - - Make sure you remember the password you gave when creating a new account (with - either new or import). Without it you are not able to unlock your account. - - Note that exporting your key in unencrypted format is NOT supported. - - Keys are stored under /keystore. - It is safe to transfer the entire directory or the individual keys therein - between fractal nodes by simply copying. - - Make sure you backup your keys regularly.`, - Run: func(cmd *cobra.Command, args []string) { - }, -} - -var listAccountCmd = &cobra.Command{ - Use: "list", - Short: "Print summary of existing accounts", - Long: "Print a short summary of all accounts", - Args: cobra.ExactArgs(0), - Run: func(cmd *cobra.Command, args []string) { - wallet, err := getWallet() - if err != nil { - fmt.Println("get wallet error ", err) - } - for _, account := range wallet.Accounts() { - fmt.Printf("Account: {%x} %s\n", account.Addr, account.Path) - } - }, -} - -var newAccountCmd = &cobra.Command{ - Use: "new", - Short: "Create a new account", - Long: ` - fractal account new - -Creates a new account and prints the address. - -The account is saved in encrypted format, you are prompted for a passphrase. - -You must remember this passphrase to unlock your account in the future.`, - Args: cobra.ExactArgs(0), - Run: func(cmd *cobra.Command, args []string) { - password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true) - wallet, err := getWallet() - if err != nil { - fmt.Println("get wallet error ", err) - return - } - account, err := wallet.NewAccount(password) - if err != nil { - fmt.Println("new account error ", err) - return - } - - fmt.Printf("Address: {%x}\n", account.Addr) - }, -} - -var updateAccountCmd = &cobra.Command{ - Use: "update", - Short: "Update an existing account", - Long: ` - fractal account update
- -Update an existing account. - -The account is saved in the newest version in encrypted format, you are prompted -for a passphrase to unlock the account and another to save the updated file. - -This same command can therefore be used to migrate an account of a deprecated -format to the newest format or change the password for an account. -`, - Args: cobra.MinimumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { - w, err := getWallet() - if err != nil { - fmt.Println("get wallet error ", err) - return - } - for _, addr := range args { - account := &cache.Account{Addr: common.HexToAddress(addr)} - oldPassword := getPassPhrase("Please give old password.", true) - newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true) - if err := w.Update(*account, oldPassword, newPassword); err != nil { - fmt.Println("Could not update the account: ", addr, " ", err) - } - } - }, -} - -var importAccountCmd = &cobra.Command{ - Use: "import", - Short: "Import a private key into a new account", - Long: ` - fractal account import - -Imports an unencrypted private key from and creates a new account. -Prints the address. - -The keyfile is assumed to contain an unencrypted private key in hexadecimal format. - -The account is saved in encrypted format, you are prompted for a passphrase. - -You must remember this passphrase to unlock your account in the future. - -Note: -As you can directly copy your encrypted accounts to another ethereum instance, -this import mechanism is not needed when you transfer an account between -nodes. -`, - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - keyfile := args[0] - key, err := crypto.LoadECDSA(keyfile) - if err != nil { - fmt.Println("Failed to load the private key: ", err) - return - } - passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true) - - w, err := getWallet() - if err != nil { - fmt.Println("get wallet error ", err) - return - } - acct, err := w.ImportECDSA(key, passphrase) - if err != nil { - fmt.Println("Could not create the account: ", err) - return - } - fmt.Printf("Address: {%x}\n", acct.Addr) - }, -} - -func init() { - walletCmd.PersistentFlags().StringVarP(&ftconfig.NodeCfg.DataDir, "datadir", "d", ftconfig.NodeCfg.DataDir, "Data directory for the databases and keystore") - walletCmd.PersistentFlags().StringVar(&ftconfig.NodeCfg.KeyStoreDir, "keystore", ftconfig.NodeCfg.KeyStoreDir, "Directory for the keystore") - walletCmd.PersistentFlags().BoolVar(&ftconfig.NodeCfg.UseLightweightKDF, "lightkdf", ftconfig.NodeCfg.UseLightweightKDF, "Reduce key-derivation RAM & CPU usage at some expense of KDF strength") - - accountCmd.PersistentFlags().StringVarP(&ftconfig.NodeCfg.DataDir, "datadir", "d", ftconfig.NodeCfg.DataDir, "Data directory for the databases and keystore") - accountCmd.PersistentFlags().StringVar(&ftconfig.NodeCfg.KeyStoreDir, "keystore", ftconfig.NodeCfg.KeyStoreDir, "Directory for the keystore") - accountCmd.PersistentFlags().BoolVar(&ftconfig.NodeCfg.UseLightweightKDF, "lightkdf", ftconfig.NodeCfg.UseLightweightKDF, "Reduce key-derivation RAM & CPU usage at some expense of KDF strength") - - walletCmd.AddCommand(importWalletCmd) - accountCmd.AddCommand(listAccountCmd, newAccountCmd, updateAccountCmd, importAccountCmd) - RootCmd.AddCommand(walletCmd, accountCmd) -} - -// getPassPhrase retrieves the password associated with an account, either fetched -// from a list of preloaded passphrases, or requested interactively from the user. -func getPassPhrase(prompt string, confirmation bool) string { - // Otherwise prompt the user for the password - if prompt != "" { - fmt.Println(prompt) - } - password, err := console.Stdin.PromptPassword("Passphrase: ") - if err != nil { - fmt.Println("Failed to read passphrase: ", err) - os.Exit(1) - } - if confirmation { - confirm, err := console.Stdin.PromptPassword("Repeat passphrase: ") - if err != nil { - fmt.Println("Failed to read passphrase confirmation: ", err) - os.Exit(1) - } - if password != confirm { - fmt.Println("Passphrases do not match") - os.Exit(1) - } - } - return password -} - -func getWallet() (*wallet.Wallet, error) { - scryptN := keystore.StandardScryptN - scryptP := keystore.StandardScryptP - if ftconfig.NodeCfg.UseLightweightKDF { - scryptN = keystore.LightScryptN - scryptP = keystore.LightScryptP - } - - var ( - keydir string - err error - ) - switch { - case filepath.IsAbs(ftconfig.NodeCfg.KeyStoreDir): - keydir = ftconfig.NodeCfg.KeyStoreDir - case ftconfig.NodeCfg.DataDir != "": - if ftconfig.NodeCfg.KeyStoreDir == "" { - keydir = filepath.Join(ftconfig.NodeCfg.DataDir, "keystore") - } else { - keydir, err = filepath.Abs(ftconfig.NodeCfg.KeyStoreDir) - } - case ftconfig.NodeCfg.KeyStoreDir != "": - keydir, err = filepath.Abs(ftconfig.NodeCfg.KeyStoreDir) - } - if err != nil { - fmt.Println("get keydir error ", err) - return nil, err - } - - if keydir == "" { - keydir, err = ioutil.TempDir("", "tmpkeystore") - if err != nil { - fmt.Println("form keydir error ", err) - return nil, err - } - } - fmt.Println("keydir ", keydir) - if err := os.MkdirAll(keydir, 0700); err != nil { - fmt.Println("mkdir keydir error ", err) - return nil, err - } - return wallet.NewWallet(keydir, scryptN, scryptP), nil -} diff --git a/cmd/ft/chain.go b/cmd/ft/chain.go new file mode 100644 index 00000000..69c497a1 --- /dev/null +++ b/cmd/ft/chain.go @@ -0,0 +1,358 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package main + +import ( + "compress/gzip" + "errors" + "fmt" + "io" + "os" + "os/signal" + "runtime" + "strconv" + "strings" + "sync/atomic" + "syscall" + "time" + + "github.com/ethereum/go-ethereum/log" + "github.com/fractalplatform/fractal/blockchain" + "github.com/fractalplatform/fractal/ftservice" + "github.com/fractalplatform/fractal/types" + ldb "github.com/fractalplatform/fractal/utils/fdb/leveldb" + "github.com/fractalplatform/fractal/utils/rlp" + "github.com/spf13/cobra" + "github.com/syndtr/goleveldb/leveldb/util" +) + +const ( + importBatchSize = 2500 +) + +var ( + importCommnad = &cobra.Command{ + Use: "import -d -g ", + Short: "Import a blockchain file", + Long: "Import a blockchain file", + Run: func(cmd *cobra.Command, args []string) { + ftCfgInstance.LogCfg.Setup() + if err := importChain(args); err != nil { + fmt.Println(err) + } + }, + } + exportCommand = &cobra.Command{ + Use: "export -d ", + Short: "Export blockchain to file", + Long: "Export blockchain to file", + Run: func(cmd *cobra.Command, args []string) { + ftCfgInstance.LogCfg.Setup() + if err := exportChain(args); err != nil { + fmt.Println(err) + } + }, + } +) + +func init() { + RootCmd.AddCommand(importCommnad, exportCommand) + importCommnad.Flags().StringVarP(&ftCfgInstance.NodeCfg.DataDir, "datadir", "d", ftCfgInstance.NodeCfg.DataDir, "Data directory for the databases ") + importCommnad.Flags().StringVarP(&ftCfgInstance.GenesisFile, "genesis", "g", "", "genesis json file") + exportCommand.Flags().StringVarP(&ftCfgInstance.NodeCfg.DataDir, "datadir", "d", ftCfgInstance.NodeCfg.DataDir, "Data directory for the databases ") +} + +func exportChain(args []string) error { + if len(args) < 1 { + return errors.New("This command requires an argument") + } + + start := time.Now() + + stack, err := makeNode() + if err != nil { + return err + } + + ctx := stack.GetNodeConfig() + ftsrv, err := ftservice.New(ctx, ftCfgInstance.FtServiceCfg) + if err != nil { + return err + } + + fp := args[0] + if len(args) < 3 { + err = exportBlockChain(ftsrv.BlockChain(), fp) + } else { + first, ferr := strconv.ParseInt(args[1], 10, 64) + last, lerr := strconv.ParseInt(args[2], 10, 64) + if ferr != nil || lerr != nil { + return errors.New("Export error in parsing parameters: block number not an integer") + } + if first < 0 || last < 0 { + return errors.New("Export error: block number must be greater than 0") + } + err = exportAppendBlockChain(ftsrv.BlockChain(), fp, uint64(first), uint64(last)) + } + log.Info("Export done in ", "time", time.Since(start)) + if err != nil { + return err + } + + return nil +} + +func exportBlockChain(b *blockchain.BlockChain, fn string) error { + log.Info("Exporting blockchain", "file", fn) + // Open the file handle and potentially wrap with a gzip stream + fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) + if err != nil { + return err + } + defer fh.Close() + + var writer io.Writer = fh + if strings.HasSuffix(fn, ".gz") { + writer = gzip.NewWriter(writer) + defer writer.(*gzip.Writer).Close() + } + // Iterate over the blocks and export them + if err := b.Export(writer); err != nil { + return err + } + log.Info("Exported blockchain", "file", fn) + + return nil +} + +// ExportAppendChain exports a blockchain into the specified file, appending to +// the file if data already exists in it. +func exportAppendBlockChain(b *blockchain.BlockChain, fn string, first uint64, last uint64) error { + log.Info("Exporting blockchain", "file", fn) + // Open the file handle and potentially wrap with a gzip stream + fh, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm) + if err != nil { + return err + } + defer fh.Close() + + var writer io.Writer = fh + if strings.HasSuffix(fn, ".gz") { + writer = gzip.NewWriter(writer) + defer writer.(*gzip.Writer).Close() + } + // Iterate over the blocks and export them + if err := b.ExportN(writer, first, last); err != nil { + return err + } + log.Info("Exported blockchain to", "file", fn) + return nil +} + +func importChain(args []string) error { + if len(args) < 1 { + return errors.New("This command requires an argument") + } + + stack, err := makeNode() + if err != nil { + return err + } + + ctx := stack.GetNodeConfig() + ftsrv, err := ftservice.New(ctx, ftCfgInstance.FtServiceCfg) + if err != nil { + return err + } + + // Start periodically gathering memory profiles + var peakMemAlloc, peakMemSys uint64 + go func() { + stats := new(runtime.MemStats) + for { + runtime.ReadMemStats(stats) + if atomic.LoadUint64(&peakMemAlloc) < stats.Alloc { + atomic.StoreUint64(&peakMemAlloc, stats.Alloc) + } + if atomic.LoadUint64(&peakMemSys) < stats.Sys { + atomic.StoreUint64(&peakMemSys, stats.Sys) + } + time.Sleep(5 * time.Second) + } + }() + + start := time.Now() + + fp := args[0] + if len(args) == 1 { + if err := importBlockchain(ftsrv.BlockChain(), fp); err != nil { + return fmt.Errorf("Import error: %v", err) + } + } else { + for i := 0; i < len(args); i++ { + if err := importBlockchain(ftsrv.BlockChain(), args[i]); err != nil { + return fmt.Errorf("Import error: %v, %v", err, args[i]) + } + } + } + + log.Info("Import done in ", "time", time.Since(start)) + + db := ftsrv.ChainDb().(*ldb.LDBDatabase) + stats, err := db.LDB().GetProperty("leveldb.stats") + if err != nil { + return fmt.Errorf("Failed to read database stats: %v", err) + } + fmt.Println(stats) + + ioStats, err := db.LDB().GetProperty("leveldb.iostats") + if err != nil { + return fmt.Errorf("Failed to read database iostats: %v", err) + } + fmt.Println(ioStats) + + mem := new(runtime.MemStats) + runtime.ReadMemStats(mem) + + fmt.Printf("Object memory: %.3f MB current, %.3f MB peak\n", float64(mem.Alloc)/1024/1024, float64(atomic.LoadUint64(&peakMemAlloc))/1024/1024) + fmt.Printf("System memory: %.3f MB current, %.3f MB peak\n", float64(mem.Sys)/1024/1024, float64(atomic.LoadUint64(&peakMemSys))/1024/1024) + fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000) + fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs)) + + // Compact the entire database to more accurately measure disk io and print the stats + start = time.Now() + fmt.Println("Compacting entire database...") + if err = db.LDB().CompactRange(util.Range{}); err != nil { + return fmt.Errorf("Compaction failed: %v", err) + } + fmt.Printf("Compaction done in %v.\n\n", time.Since(start)) + + stats, err = db.LDB().GetProperty("leveldb.stats") + if err != nil { + return fmt.Errorf("Failed to read database stats: %v", err) + } + fmt.Println(stats) + + ioStats, err = db.LDB().GetProperty("leveldb.iostats") + if err != nil { + return fmt.Errorf("Failed to read database iostats: %v", err) + } + fmt.Println(ioStats) + + return nil +} + +func importBlockchain(chain *blockchain.BlockChain, fn string) error { + // Watch for Ctrl-C while the import is running. + // If a signal is received, the import will stop at the next batch. + interrupt := make(chan os.Signal, 1) + stop := make(chan struct{}) + signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) + defer signal.Stop(interrupt) + defer close(interrupt) + go func() { + if _, ok := <-interrupt; ok { + log.Info("Interrupted during import, stopping at next batch") + } + close(stop) + }() + checkInterrupt := func() bool { + select { + case <-stop: + return true + default: + return false + } + } + + log.Info("Importing blockchain", "file", fn) + + // Open the file handle and potentially unwrap the gzip stream + fh, err := os.Open(fn) + if err != nil { + return err + } + defer fh.Close() + + var reader io.Reader = fh + if strings.HasSuffix(fn, ".gz") { + if reader, err = gzip.NewReader(reader); err != nil { + return err + } + } + stream := rlp.NewStream(reader, 0) + + // Run actual the import. + blocks := make(types.Blocks, importBatchSize) + n := 0 + for batch := 0; ; batch++ { + // Load a batch of RLP blocks. + if checkInterrupt() { + return fmt.Errorf("interrupted") + } + i := 0 + for ; i < importBatchSize; i++ { + var b types.Block + if err := stream.Decode(&b); err == io.EOF { + break + } else if err != nil { + return fmt.Errorf("at block %d: %v", n, err) + } + // don't import first block + if b.NumberU64() == 0 { + i-- + continue + } + blocks[i] = &b + n++ + } + if i == 0 { + break + } + // Import the batch. + if checkInterrupt() { + return fmt.Errorf("interrupted") + } + missing := missingBlocks(chain, blocks[:i]) + if len(missing) == 0 { + log.Info("Skipping batch as all blocks present", "batch", batch, "first", blocks[0].Hash(), "last", blocks[i-1].Hash()) + continue + } + if _, err := chain.InsertChain(missing); err != nil { + return fmt.Errorf("invalid block %d: %v", n, err) + } + } + return nil +} + +func missingBlocks(chain *blockchain.BlockChain, blocks []*types.Block) []*types.Block { + head := chain.CurrentBlock() + for i, block := range blocks { + // If we're behind the chain head, only check block, state is available at head + if head.NumberU64() > block.NumberU64() { + if !chain.HasBlock(block.Hash(), block.NumberU64()) { + return blocks[i:] + } + continue + } + // If we're above the chain head, state availability is a must + if !chain.HasBlockAndState(block.Hash(), block.NumberU64()) { + return blocks[i:] + } + } + return nil +} diff --git a/cmd/ft/config.go b/cmd/ft/config.go index 7dd199a9..a468447f 100644 --- a/cmd/ft/config.go +++ b/cmd/ft/config.go @@ -13,59 +13,101 @@ // // You should have received a copy of the GNU General Public License // along with this program. If not, see . + package main import ( - "io" - "os" - "github.com/ethereum/go-ethereum/log" + "github.com/fractalplatform/fractal/cmd/utils" "github.com/fractalplatform/fractal/ftservice" + "github.com/fractalplatform/fractal/ftservice/gasprice" + "github.com/fractalplatform/fractal/metrics" "github.com/fractalplatform/fractal/node" - colorable "github.com/mattn/go-colorable" - "github.com/mattn/go-isatty" + "github.com/fractalplatform/fractal/p2p" + "github.com/fractalplatform/fractal/params" + "github.com/fractalplatform/fractal/txpool" ) -var glogger *log.GlogHandler +var ( + //ft config instance + ftCfgInstance = defaultFtConfig() +) type ftConfig struct { - ConfigFileFlag string - GenesisFileFlag string - NodeCfg *node.Config - FtServiceCfg *ftservice.Config + GenesisFile string `mapstructure:"genesis"` + LogCfg *utils.LogConfig `mapstructure:"log"` + NodeCfg *node.Config `mapstructure:"node"` + FtServiceCfg *ftservice.Config `mapstructure:"ftservice"` +} + +func defaultFtConfig() *ftConfig { + return &ftConfig{ + LogCfg: utils.DefaultLogConfig(), + NodeCfg: defaultNodeConfig(), + FtServiceCfg: defaultFtServiceConfig(), + } } -// LogConfig log config -type LogConfig struct { - PrintOrigins bool `mapstructure:"log-printorigins"` - Level int `mapstructure:"log-level"` - Vmodule string `mapstructure:"log-vmodule"` - BacktraceAt string `mapstructure:"log-backtraceat"` +func defaultNodeConfig() *node.Config { + return &node.Config{ + Name: params.ClientIdentifier, + DataDir: defaultDataDir(), + UseLightweightKDF: false, + IPCPath: params.ClientIdentifier + ".ipc", + HTTPHost: "localhost", + HTTPPort: 8545, + HTTPModules: []string{"ft", "miner", "dpos", "account", "txpool"}, + HTTPVirtualHosts: []string{"localhost"}, + HTTPCors: []string{"*"}, + WSHost: "localhost", + WSPort: 8546, + WSModules: []string{"ft"}, + Logger: log.New(), + P2PConfig: defaultP2pConfig(), + } } -func defaultLogConfig() *LogConfig { - return &LogConfig{ - PrintOrigins: false, - Level: 3, +func defaultP2pConfig() *p2p.Config { + cfg := &p2p.Config{ + MaxPeers: 10, + Name: "Fractal-P2P", + ListenAddr: ":2018", } + return cfg } -func init() { - usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" - output := io.Writer(os.Stderr) - if usecolor { - output = colorable.NewColorableStderr() +func defaultFtServiceConfig() *ftservice.Config { + return &ftservice.Config{ + DatabaseHandles: makeDatabaseHandles(), + DatabaseCache: 768, + TxPool: txpool.DefaultTxPoolConfig, + Miner: defaultMinerConfig(), + GasPrice: gasprice.Config{ + Blocks: 20, + Percentile: 60, + }, + MetricsConf: defaultMetricsConfig(), + ContractLogFlag: false, + Snapshot: true, } +} - glogger = log.NewGlogHandler(log.StreamHandler(output, log.TerminalFormat(usecolor))) +func defaultMinerConfig() *ftservice.MinerConfig { + return &ftservice.MinerConfig{ + Name: params.DefaultChainconfig.SysName, + PrivateKeys: []string{"289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"}, + ExtraData: "system", + } } -//Setup initializes logging based on the LogConfig -func (lc *LogConfig) Setup() { - // logging - log.PrintOrigins(lc.PrintOrigins) - glogger.Verbosity(log.Lvl(lc.Level)) - glogger.Vmodule(lc.Vmodule) - glogger.BacktraceAt(lc.BacktraceAt) - log.Root().SetHandler(glogger) +func defaultMetricsConfig() *metrics.Config { + return &metrics.Config{ + MetricsFlag: false, + InfluxDBFlag: false, + URL: "http://localhost:8086", + DataBase: "metrics", + UserName: "", + PassWd: "", + NameSpace: "fractal/", + } } diff --git a/cmd/ft/config.yaml b/cmd/ft/config.yaml deleted file mode 100644 index d35cdbad..00000000 --- a/cmd/ft/config.yaml +++ /dev/null @@ -1,49 +0,0 @@ -log-printorigins: false -log-level: 4 -log-vmodule: "" -log-backtraceat: "" - -#node-datadir: "" -#node-ipcpath: "" -#node-keystore: "" -#node-lightkdf: false -node-httphost: "localhost" -node-httpport: 8545 -node-httpmodules: ["ft"] -#node-httpcors: ["", ""] -node-httpvirtualhosts: ["localhost"] -node-wshost: "localhost" -node-wsport: 8546 -node-wsmodules: ["ft"] - -#node-wsorigins: ["", ""] -#node-wsexposall: false - -ftservice-databasecache: 768 - -ethash-cachedir: "zethash" -ethash-cachesinmem: 2 -ethash-cachesondisk: 3 -#ethash-datasetdir: "" -ethash-datasetsinmem: 1 -ethash-datasetsondisk: 2 -#ethash-powmode: 0 - -#txpool-nolocals: false -txpool-journal: "transactions.rlp" -#txpool-rejournal: 0 -txpool-pricebump: 10 -txpool-pricelimit: 1 -txpool-accountslots: 16 -txpool-accountqueue: 64 -txpool-globalslots: 4096 -txpool-globalqueue: 1024 -#txpool-lifetime: 0 - -#test-metricsflag: false -#test-influxdbflag: false -#test-influxdburl: "" -#test-influxdbname: "" -#test-influxdbuser: "" -#test-influxdbpasswd: "" -#test-influxdbnamespace: "" \ No newline at end of file diff --git a/cmd/ft/default.go b/cmd/ft/default.go deleted file mode 100644 index 9a84afe5..00000000 --- a/cmd/ft/default.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package main - -import ( - "time" - - "github.com/ethereum/go-ethereum/log" - "github.com/fractalplatform/fractal/ftservice" - "github.com/fractalplatform/fractal/ftservice/gasprice" - "github.com/fractalplatform/fractal/metrics" - "github.com/fractalplatform/fractal/node" - "github.com/fractalplatform/fractal/p2p" - "github.com/fractalplatform/fractal/params" - "github.com/fractalplatform/fractal/txpool" -) - -var ( - // log config - logConfig = defaultLogConfig() - - //ft config - ftconfig = defaultFtConfig() -) - -func defaultFtConfig() *ftConfig { - return &ftConfig{ - NodeCfg: defaultNodeConfig(), - FtServiceCfg: defaultFtServiceConfig(), - } -} - -func defaultFtServiceConfig() *ftservice.Config { - return &ftservice.Config{ - DatabaseHandles: makeDatabaseHandles(), - DatabaseCache: 768, - TxPool: defaultTxPoolConfig(), - Miner: defaultMinerConfig(), - GasPrice: gasprice.Config{ - Blocks: 20, - Percentile: 60, - }, - MetricsConf: defaultMetricsConfig(), - } -} - -func defaultNodeConfig() *node.Config { - return &node.Config{ - Name: params.ClientIdentifier, - DataDir: defaultDataDir(), - UseLightweightKDF: false, - IPCPath: params.ClientIdentifier + ".ipc", - - HTTPHost: "localhost", - HTTPPort: 8545, - HTTPModules: []string{"ft", "miner", "dpos", "account", "txpool", "keystore"}, - HTTPVirtualHosts: []string{"localhost"}, - HTTPCors: []string{"*"}, - - WSHost: "localhost", - WSPort: 8546, - WSModules: []string{"ft"}, - Logger: log.New(), - - P2PConfig: defaultP2pConfig(), - } -} - -func defaultP2pConfig() *p2p.Config { - cfg := &p2p.Config{ - MaxPeers: 10, - Name: "Fractal-P2P", - ListenAddr: ":2018", - } - return cfg -} - -func defaultTxPoolConfig() *txpool.Config { - return &txpool.Config{ - Journal: "transactions.rlp", - Rejournal: time.Hour, - - PriceLimit: 1, - PriceBump: 10, - - AccountSlots: 128, - GlobalSlots: 4096, - AccountQueue: 1280, - GlobalQueue: 40960, - - Lifetime: 3 * time.Hour, - GasAssetID: 1, - } -} - -func defaultMinerConfig() *ftservice.MinerConfig { - return &ftservice.MinerConfig{ - Name: params.DefaultChainconfig.SysName.String(), - PrivateKeys: []string{"289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"}, - ExtraData: "system", - } -} - -func defaultMetricsConfig() *metrics.Config { - return &metrics.Config{ - MetricsFlag: false, - InfluxDBFlag: false, - Url: "http://localhost:8086", - DataBase: "metrics", - UserName: "", - PassWd: "", - NameSpace: "fractal/", - } -} diff --git a/cmd/ft/flags.go b/cmd/ft/flags.go new file mode 100644 index 00000000..585e0158 --- /dev/null +++ b/cmd/ft/flags.go @@ -0,0 +1,488 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package main + +import ( + flag "github.com/spf13/pflag" + "github.com/spf13/viper" +) + +var ( + // ConfigFile the Fractal config file + ConfigFile string +) + +func addFlags(flags *flag.FlagSet) { + // log + flags.BoolVar( + &ftCfgInstance.LogCfg.PrintOrigins, + "log_debug", + ftCfgInstance.LogCfg.PrintOrigins, + "Prepends log messages with call-site location (file and line number)", + ) + viper.BindPFlag("log.debug", flags.Lookup("log_debug")) + + flags.IntVar( + &ftCfgInstance.LogCfg.Level, + "log_level", + ftCfgInstance.LogCfg.Level, + "Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail", + ) + viper.BindPFlag("log.level", flags.Lookup("log_level")) + + flags.StringVar( + &ftCfgInstance.LogCfg.Vmodule, + "log_module", + ftCfgInstance.LogCfg.Vmodule, + "Per-module verbosity: comma-separated list of = (e.g. ft/*=5,p2p=4)", + ) + viper.BindPFlag("log.module", flags.Lookup("log_module")) + + flags.StringVar( + &ftCfgInstance.LogCfg.BacktraceAt, + "log_backtrace", + ftCfgInstance.LogCfg.BacktraceAt, + "Request a stack trace at a specific logging statement (e.g. \"block.go:271\")", + ) + viper.BindPFlag("log.backtrace", flags.Lookup("log_backtrace")) + + // config file + flags.StringVarP( + &ConfigFile, + "config", "c", + "", + "TOML/YAML configuration file", + ) + + // Genesis File + flags.StringVarP( + &ftCfgInstance.GenesisFile, + "genesis", + "g", "", + "Genesis json file", + ) + viper.BindPFlag("genesis", flags.Lookup("genesis")) + + // node datadir + flags.StringVarP( + &ftCfgInstance.NodeCfg.DataDir, + "datadir", "d", + ftCfgInstance.NodeCfg.DataDir, + "Data directory for the databases ", + ) + viper.BindPFlag("node.datadir", flags.Lookup("datadir")) + + // node + flags.BoolVar( + &ftCfgInstance.NodeCfg.UseLightweightKDF, + "lightkdf", + ftCfgInstance.NodeCfg.UseLightweightKDF, + "Reduce key-derivation RAM & CPU usage at some expense of KDF strength", + ) + viper.BindPFlag("node.lightkdf", flags.Lookup("lightkdf")) + + flags.StringVar( + &ftCfgInstance.NodeCfg.IPCPath, + "ipcpath", + ftCfgInstance.NodeCfg.IPCPath, + "RPC:ipc file name", + ) + viper.BindPFlag("node.ipcpath", flags.Lookup("ipcpath")) + + flags.StringVar( + &ftCfgInstance.NodeCfg.HTTPHost, + "http_host", + ftCfgInstance.NodeCfg.HTTPHost, + "RPC:http host address", + ) + viper.BindPFlag("node.httphost", flags.Lookup("http_host")) + + flags.IntVar( + &ftCfgInstance.NodeCfg.HTTPPort, + "http_port", + ftCfgInstance.NodeCfg.HTTPPort, + "RPC:http host port", + ) + viper.BindPFlag("node.httpport", flags.Lookup("http_port")) + + flags.StringSliceVar( + &ftCfgInstance.NodeCfg.HTTPModules, + "http_modules", + ftCfgInstance.NodeCfg.HTTPModules, + "RPC:http api's offered over the HTTP-RPC interface", + ) + viper.BindPFlag("node.httpmodules", flags.Lookup("http_modules")) + + flags.StringSliceVar( + &ftCfgInstance.NodeCfg.HTTPCors, + "http_cors", + ftCfgInstance.NodeCfg.HTTPCors, + "RPC:Which to accept cross origin", + ) + viper.BindPFlag("node.httpcors", flags.Lookup("http_cors")) + + flags.StringSliceVar( + &ftCfgInstance.NodeCfg.HTTPVirtualHosts, + "http_vhosts", + ftCfgInstance.NodeCfg.HTTPVirtualHosts, + "RPC:http virtual hostnames from which to accept requests", + ) + viper.BindPFlag("node.httpvirtualhosts", flags.Lookup("http_vhosts")) + + flags.StringVar( + &ftCfgInstance.NodeCfg.WSHost, + "ws_host", + ftCfgInstance.NodeCfg.WSHost, + "RPC:websocket host address", + ) + viper.BindPFlag("node.wshost", flags.Lookup("ws_host")) + + flags.IntVar( + &ftCfgInstance.NodeCfg.WSPort, + "ws_port", + ftCfgInstance.NodeCfg.WSPort, + "RPC:websocket host port", + ) + viper.BindPFlag("node.wsport", flags.Lookup("ws_port")) + + flags.StringSliceVar( + &ftCfgInstance.NodeCfg.WSModules, + "ws_modules", + ftCfgInstance.NodeCfg.WSModules, + "RPC:ws api's offered over the WS-RPC interface", + ) + viper.BindPFlag("node.wsmodules", flags.Lookup("ws_modules")) + + flags.StringSliceVar( + &ftCfgInstance.NodeCfg.WSOrigins, + "ws_origins", + ftCfgInstance.NodeCfg.WSOrigins, + "RPC:ws origins from which to accept websockets requests", + ) + viper.BindPFlag("node.wsorigins", flags.Lookup("ws_origins")) + + flags.BoolVar( + &ftCfgInstance.NodeCfg.WSExposeAll, + "ws_exposeall", + ftCfgInstance.NodeCfg.WSExposeAll, + "RPC:ws exposes all API modules via the WebSocket RPC interface rather than just the public ones.", + ) + viper.BindPFlag("node.wsexposeall", flags.Lookup("ws_exposeall")) + + // ftservice database options + flags.IntVar( + &ftCfgInstance.FtServiceCfg.DatabaseCache, + "database_cache", + ftCfgInstance.FtServiceCfg.DatabaseCache, + "Megabytes of memory allocated to internal database caching", + ) + viper.BindPFlag("ftservice.databasecache", flags.Lookup("database_cache")) + + flags.BoolVar( + &ftCfgInstance.FtServiceCfg.ContractLogFlag, + "contractlog", + ftCfgInstance.FtServiceCfg.ContractLogFlag, + "flag for db to store contrat internal transaction log.", + ) + viper.BindPFlag("ftservice.contractlog", flags.Lookup("contractlog")) + + // txpool + flags.BoolVar( + &ftCfgInstance.FtServiceCfg.TxPool.NoLocals, + "txpool_nolocals", + ftCfgInstance.FtServiceCfg.TxPool.NoLocals, + "Disables price exemptions for locally submitted transactions", + ) + viper.BindPFlag("ftservice.txpool.nolocals", flags.Lookup("txpool_nolocals")) + + flags.StringVar( + &ftCfgInstance.FtServiceCfg.TxPool.Journal, + "txpool_journal", + ftCfgInstance.FtServiceCfg.TxPool.Journal, + "Disk journal for local transaction to survive node restarts", + ) + viper.BindPFlag("ftservice.txpool.journal", flags.Lookup("txpool_journal")) + + flags.DurationVar( + &ftCfgInstance.FtServiceCfg.TxPool.Rejournal, + "txpool_rejournal", + ftCfgInstance.FtServiceCfg.TxPool.Rejournal, + "Time interval to regenerate the local transaction journal", + ) + viper.BindPFlag("ftservice.txpool.rejournal", flags.Lookup("txpool_rejournal")) + + flags.Uint64Var( + &ftCfgInstance.FtServiceCfg.TxPool.PriceBump, + "txpool_pricebump", + ftCfgInstance.FtServiceCfg.TxPool.PriceBump, + "Price bump percentage to replace an already existing transaction", + ) + viper.BindPFlag("ftservice.txpool.pricebump", flags.Lookup("txpool_pricebump")) + + flags.Uint64Var( + &ftCfgInstance.FtServiceCfg.TxPool.PriceLimit, + "txpool_pricelimit", + ftCfgInstance.FtServiceCfg.TxPool.PriceLimit, + "Minimum gas price limit to enforce for acceptance into the pool", + ) + viper.BindPFlag("ftservice.txpool.pricelimit", flags.Lookup("txpool_pricelimit")) + + flags.Uint64Var( + &ftCfgInstance.FtServiceCfg.TxPool.AccountSlots, + "txpool_accountslots", + ftCfgInstance.FtServiceCfg.TxPool.AccountSlots, + "Number of executable transaction slots guaranteed per account", + ) + viper.BindPFlag("ftservice.txpool.accountslots", flags.Lookup("txpool_accountslots")) + + flags.Uint64Var( + &ftCfgInstance.FtServiceCfg.TxPool.AccountQueue, + "txpool_accountqueue", + ftCfgInstance.FtServiceCfg.TxPool.AccountQueue, + "Maximum number of non-executable transaction slots permitted per account", + ) + viper.BindPFlag("ftservice.txpool.accountqueue", flags.Lookup("txpool_accountqueue")) + + flags.Uint64Var( + &ftCfgInstance.FtServiceCfg.TxPool.GlobalSlots, + "txpool_globalslots", + ftCfgInstance.FtServiceCfg.TxPool.GlobalSlots, + "Maximum number of executable transaction slots for all accounts", + ) + viper.BindPFlag("ftservice.txpool.globalslots", flags.Lookup("txpool_globalslots")) + + flags.Uint64Var( + &ftCfgInstance.FtServiceCfg.TxPool.GlobalQueue, + "txpool_globalqueue", + ftCfgInstance.FtServiceCfg.TxPool.GlobalQueue, + "Minimum number of non-executable transaction slots for all accounts", + ) + viper.BindPFlag("ftservice.txpool.globalqueue", flags.Lookup("txpool_globalqueue")) + + flags.DurationVar( + &ftCfgInstance.FtServiceCfg.TxPool.Lifetime, + "txpool_lifetime", + ftCfgInstance.FtServiceCfg.TxPool.Lifetime, + "Maximum amount of time non-executable transaction are queued", + ) + viper.BindPFlag("ftservice.txpool.lifetime", flags.Lookup("txpool_lifetime")) + + // miner + flags.BoolVar( + &ftCfgInstance.FtServiceCfg.Miner.Start, + "miner_start", + ftCfgInstance.FtServiceCfg.Miner.Start, + "Start miner generate block and process transaction", + ) + viper.BindPFlag("ftservice.miner.start", flags.Lookup("miner_start")) + + flags.StringVar( + &ftCfgInstance.FtServiceCfg.Miner.Name, + "miner_name", + ftCfgInstance.FtServiceCfg.Miner.Name, + "Name for block mining rewards", + ) + viper.BindPFlag("ftservice.miner.name", flags.Lookup("miner_name")) + + flags.StringSliceVar( + &ftCfgInstance.FtServiceCfg.Miner.PrivateKeys, + "miner_private", + ftCfgInstance.FtServiceCfg.Miner.PrivateKeys, + "Hex of private key for block mining rewards", + ) + viper.BindPFlag("ftservice.miner.private", flags.Lookup("miner_private")) + + flags.StringVar( + &ftCfgInstance.FtServiceCfg.Miner.ExtraData, + "miner_extra", + ftCfgInstance.FtServiceCfg.Miner.ExtraData, + "Block extra data set by the miner", + ) + viper.BindPFlag("ftservice.miner.name", flags.Lookup("miner_extra")) + + // gas price oracle + flags.IntVar( + &ftCfgInstance.FtServiceCfg.GasPrice.Blocks, + "gpo_blocks", + ftCfgInstance.FtServiceCfg.GasPrice.Blocks, + "Number of recent blocks to check for gas prices", + ) + viper.BindPFlag("ftservice.gpo.blocks", flags.Lookup("gpo_blocks")) + + flags.IntVar( + &ftCfgInstance.FtServiceCfg.GasPrice.Percentile, + "gpo_percentile", + ftCfgInstance.FtServiceCfg.GasPrice.Percentile, + "Suggested gas price is the given percentile of a set of recent transaction gas prices", + ) + viper.BindPFlag("ftservice.gpo.percentile", flags.Lookup("gpo_percentile")) + + // metrics + flags.BoolVar( + &ftCfgInstance.FtServiceCfg.MetricsConf.MetricsFlag, + "metrics_start", + ftCfgInstance.FtServiceCfg.MetricsConf.MetricsFlag, + "flag that open statistical metrics", + ) + viper.BindPFlag("ftservice.metrics.start", flags.Lookup("metrics_start")) + + flags.BoolVar( + &ftCfgInstance.FtServiceCfg.MetricsConf.InfluxDBFlag, + "metrics_influxdb", + ftCfgInstance.FtServiceCfg.MetricsConf.InfluxDBFlag, + "flag that open influxdb thad store statistical metrics", + ) + viper.BindPFlag("ftservice.metrics.influxdb", flags.Lookup("metrics_influxdb")) + + flags.StringVar( + &ftCfgInstance.FtServiceCfg.MetricsConf.URL, + "metrics_influxdb_URL", + ftCfgInstance.FtServiceCfg.MetricsConf.URL, + "URL that connect influxdb", + ) + viper.BindPFlag("ftservice.metrics.influxdbURL", flags.Lookup("metrics_influxdb_URL")) + + flags.StringVar( + &ftCfgInstance.FtServiceCfg.MetricsConf.DataBase, + "metrics_influxdb_name", + ftCfgInstance.FtServiceCfg.MetricsConf.DataBase, + "Influxdb database name", + ) + viper.BindPFlag("ftservice.metrics.influxdbname", flags.Lookup("metrics_influxdb_name")) + + flags.StringVar( + &ftCfgInstance.FtServiceCfg.MetricsConf.UserName, + "metrics_influxdb_user", + ftCfgInstance.FtServiceCfg.MetricsConf.UserName, + "Indluxdb user name", + ) + viper.BindPFlag("ftservice.metrics.influxdbuser", flags.Lookup("metrics_influxdb_user")) + + flags.StringVar( + &ftCfgInstance.FtServiceCfg.MetricsConf.PassWd, + "metrics_influxdb_passwd", + ftCfgInstance.FtServiceCfg.MetricsConf.PassWd, + "Influxdb user passwd", + ) + viper.BindPFlag("ftservice.metrics.influxdbpasswd", flags.Lookup("metrics_influxdb_passwd")) + + flags.StringVar( + &ftCfgInstance.FtServiceCfg.MetricsConf.NameSpace, + "metrics_influxdb_namespace", + ftCfgInstance.FtServiceCfg.MetricsConf.NameSpace, + "Influxdb namespace", + ) + viper.BindPFlag("ftservice.metrics.influxdbnamepace", flags.Lookup("metrics_influxdb_namespace")) + + // p2p + flags.UintVar( + &ftCfgInstance.NodeCfg.P2PConfig.NetworkID, + "p2p_id", + ftCfgInstance.NodeCfg.P2PConfig.NetworkID, + "The ID of the p2p network. Nodes have different ID cannot communicate, even if they have same chainID and block data.", + ) + viper.BindPFlag("ftservice.p2p.networkid", flags.Lookup("p2p_id")) + + flags.StringVar( + &ftCfgInstance.NodeCfg.P2PConfig.Name, + "p2p_name", + ftCfgInstance.NodeCfg.P2PConfig.Name, + "The name sets the p2p node name of this server", + ) + viper.BindPFlag("ftservice.p2p.name", flags.Lookup("p2p_name")) + + flags.IntVar( + &ftCfgInstance.NodeCfg.P2PConfig.MaxPeers, + "p2p_maxpeers", + ftCfgInstance.NodeCfg.P2PConfig.MaxPeers, + "Maximum number of network peers ", + ) + viper.BindPFlag("ftservice.p2p.maxpeers", flags.Lookup("p2p_maxpeers")) + + flags.IntVar( + &ftCfgInstance.NodeCfg.P2PConfig.MaxPendingPeers, + "p2p_maxpendpeers", + ftCfgInstance.NodeCfg.P2PConfig.MaxPendingPeers, + "Maximum number of pending connection attempts ", + ) + viper.BindPFlag("ftservice.p2p.maxpendpeers", flags.Lookup("p2p_maxpendpeers")) + + flags.IntVar( + &ftCfgInstance.NodeCfg.P2PConfig.DialRatio, + "p2p_dialratio", + ftCfgInstance.NodeCfg.P2PConfig.DialRatio, + "DialRatio controls the ratio of inbound to dialed connections", + ) + viper.BindPFlag("ftservice.p2p.dialratio", flags.Lookup("p2p_dialratio")) + + flags.StringVar( + &ftCfgInstance.NodeCfg.P2PConfig.ListenAddr, + "p2p_listenaddr", + ftCfgInstance.NodeCfg.P2PConfig.ListenAddr, + "Network listening address", + ) + viper.BindPFlag("ftservice.p2p.listenaddr", flags.Lookup("p2p_listenaddr")) + + flags.StringVar( + &ftCfgInstance.NodeCfg.P2PConfig.NodeDatabase, + "p2p_nodedb", + ftCfgInstance.NodeCfg.P2PConfig.NodeDatabase, + "The path to the database containing the previously seen live nodes in the network", + ) + viper.BindPFlag("ftservice.p2p.nodedb", flags.Lookup("p2p_nodedb")) + + flags.BoolVar( + &ftCfgInstance.NodeCfg.P2PConfig.NoDiscovery, + "p2p_nodiscovery", + ftCfgInstance.NodeCfg.P2PConfig.NoDiscovery, + "Disables the peer discovery mechanism (manual peer addition)", + ) + viper.BindPFlag("ftservice.p2p.nodiscovery", flags.Lookup("p2p_nodiscovery")) + + flags.BoolVar( + &ftCfgInstance.NodeCfg.P2PConfig.NoDial, + "p2p_nodial", + ftCfgInstance.NodeCfg.P2PConfig.NoDial, + "The server will not dial any peers.", + ) + viper.BindPFlag("ftservice.p2p.nodial", flags.Lookup("p2p_nodial")) + + flags.StringVar( + &ftCfgInstance.NodeCfg.P2PBootNodes, + "p2p_bootnodes", + ftCfgInstance.NodeCfg.P2PBootNodes, + "Node list file. BootstrapNodes are used to establish connectivity with the rest of the network", + ) + viper.BindPFlag("ftservice.p2p.bootnodes", flags.Lookup("p2p_bootnodes")) + + flags.StringVar( + &ftCfgInstance.NodeCfg.P2PStaticNodes, + "p2p_staticnodes", + ftCfgInstance.NodeCfg.P2PStaticNodes, + "Node list file. Static nodes are used as pre-configured connections which are always maintained and re-connected on disconnects", + ) + viper.BindPFlag("ftservice.p2p.staticnodes", flags.Lookup("p2p_staticnodes")) + + flags.StringVar( + &ftCfgInstance.NodeCfg.P2PTrustNodes, + "p2p_trustnodes", + ftCfgInstance.NodeCfg.P2PStaticNodes, + "Node list file. Trusted nodes are usesd as pre-configured connections which are always allowed to connect, even above the peer limit", + ) + viper.BindPFlag("ftservice.p2p.trustnodes", flags.Lookup("p2p_trustnodes")) + +} diff --git a/cmd/ft/init.go b/cmd/ft/init.go index 4f4cfc19..2a298e4a 100644 --- a/cmd/ft/init.go +++ b/cmd/ft/init.go @@ -17,10 +17,12 @@ package main import ( - "errors" + "encoding/json" "fmt" "os" + "github.com/fractalplatform/fractal/blockchain" + "github.com/fractalplatform/fractal/ftservice" "github.com/spf13/cobra" ) @@ -28,12 +30,12 @@ var dataDir string // initCmd represents the init command var initCmd = &cobra.Command{ - Use: "init ", + Use: "init -g -d ", Short: "Bootstrap and initialize a new genesis block", Long: `Bootstrap and initialize a new genesis block`, - Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { - if err := initGenesis(args); err != nil { + ftCfgInstance.LogCfg.Setup() + if err := initGenesis(); err != nil { fmt.Println(err) } }, @@ -41,23 +43,35 @@ var initCmd = &cobra.Command{ func init() { RootCmd.AddCommand(initCmd) - initCmd.Flags().StringVarP(&ftconfig.NodeCfg.DataDir, "datadir", "d", defaultDataDir(), "Data directory for the databases and keystore") + initCmd.Flags().StringVarP(&ftCfgInstance.GenesisFile, "genesis", "g", "", "Genesis json file") + initCmd.Flags().StringVarP(&ftCfgInstance.NodeCfg.DataDir, "datadir", "d", ftCfgInstance.NodeCfg.DataDir, "Data directory for the databases ") } // initGenesis will initialise the given JSON format genesis file and writes it as // the zero'd block (i.e. genesis) or will fail hard if it can't succeed. -func initGenesis(args []string) error { +func initGenesis() error { // Make sure we have a valid genesis JSON - genesisPath := args[0] - if len(genesisPath) == 0 { - return errors.New("Must supply path to genesis JSON file") + genesis := new(blockchain.Genesis) + if len(ftCfgInstance.GenesisFile) != 0 { + file, err := os.Open(ftCfgInstance.GenesisFile) + if err != nil { + return fmt.Errorf("Failed to read genesis file: %v(%v)", ftCfgInstance.GenesisFile, err) + } + defer file.Close() + + if err := json.NewDecoder(file).Decode(genesis); err != nil { + return fmt.Errorf("invalid genesis file: %v(%v)", ftCfgInstance.GenesisFile, err) + } } - file, err := os.Open(genesisPath) + + stack, err := makeNode() if err != nil { - return fmt.Errorf("Failed to read genesis file: %v", err) + return err } - defer file.Close() - // todo init genesis + _, err = ftservice.New(stack.GetNodeConfig(), ftCfgInstance.FtServiceCfg) + if err != nil { + return err + } return nil } diff --git a/cmd/ft/root.go b/cmd/ft/root.go index dc1c3c59..f8a820bd 100644 --- a/cmd/ft/root.go +++ b/cmd/ft/root.go @@ -43,11 +43,14 @@ var RootCmd = &cobra.Command{ // Uncomment the following line if your bare application // has an action associated with it: Run: func(cmd *cobra.Command, args []string) { + var err error if viper.ConfigFileUsed() != "" { - viperUmarshalConfig() + err = viperUmarshalConfig() + } + ftCfgInstance.LogCfg.Setup() + if err != nil { + log.Error("viper umarshal config file faild", "err", err) } - - logConfig.Setup() node, err := makeNode() if err != nil { @@ -66,85 +69,48 @@ var RootCmd = &cobra.Command{ } node.Wait() - }, } -func viperUmarshalConfig() { - err := viper.Unmarshal(logConfig) - if err != nil { - fmt.Println("Unmarshal logConfig err: ", err) - os.Exit(-1) - } - - err = viper.Unmarshal(ftconfig.NodeCfg) - if err != nil { - fmt.Println("Unmarshal NodeCfg err: ", err) - os.Exit(-1) - } - - err = viper.Unmarshal(ftconfig.FtServiceCfg.TxPool) - if err != nil { - fmt.Println("Unmarshal TxPool err: ", err) - os.Exit(-1) - } - - err = viper.Unmarshal(&ftconfig.FtServiceCfg.Miner) - if err != nil { - fmt.Println("Unmarshal miner err: ", err) - os.Exit(-1) - } - - err = viper.Unmarshal(ftconfig.FtServiceCfg) - if err != nil { - fmt.Println("Unmarshal FtServiceCfg err: ", err) - os.Exit(-1) - } - - err = viper.Unmarshal(ftconfig.FtServiceCfg.Miner) - if err != nil { - fmt.Println("Unmarshal MinerConfig err: ", err) - os.Exit(-1) - } - - err = viper.Unmarshal(ftconfig.NodeCfg.P2PConfig) +func viperUmarshalConfig() error { + err := viper.Unmarshal(ftCfgInstance) if err != nil { - fmt.Println("Unmarshal MinerConfig err: ", err) - os.Exit(-1) + return err } - + return nil } func makeNode() (*node.Node, error) { // set miner config SetupMetrics() - // Make sure we have a valid genesis JSON - if len(ftconfig.GenesisFileFlag) != 0 { - file, err := os.Open(ftconfig.GenesisFileFlag) + if len(ftCfgInstance.GenesisFile) != 0 { + file, err := os.Open(ftCfgInstance.GenesisFile) if err != nil { - return nil, fmt.Errorf("Failed to read genesis file: %v(%v)", ftconfig.GenesisFileFlag, err) + return nil, fmt.Errorf("Failed to read genesis file: %v(%v)", ftCfgInstance.GenesisFile, err) } defer file.Close() genesis := new(blockchain.Genesis) if err := json.NewDecoder(file).Decode(genesis); err != nil { - return nil, fmt.Errorf("invalid genesis file: %v(%v)", ftconfig.GenesisFileFlag, err) + return nil, fmt.Errorf("invalid genesis file: %v(%v)", ftCfgInstance.GenesisFile, err) } - ftconfig.FtServiceCfg.Genesis = genesis + ftCfgInstance.FtServiceCfg.Genesis = genesis } - return node.New(ftconfig.NodeCfg) + + return node.New(ftCfgInstance.NodeCfg) } +// SetupMetrics set metrics func SetupMetrics() { //need to set metrice.Enabled = true in metrics source code - if ftconfig.FtServiceCfg.MetricsConf.MetricsFlag { + if ftCfgInstance.FtServiceCfg.MetricsConf.MetricsFlag { log.Info("Enabling metrics collection") - if ftconfig.FtServiceCfg.MetricsConf.InfluxDBFlag { + if ftCfgInstance.FtServiceCfg.MetricsConf.InfluxDBFlag { log.Info("Enabling influxdb collection") - go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, ftconfig.FtServiceCfg.MetricsConf.Url, - ftconfig.FtServiceCfg.MetricsConf.DataBase, ftconfig.FtServiceCfg.MetricsConf.UserName, ftconfig.FtServiceCfg.MetricsConf.PassWd, - ftconfig.FtServiceCfg.MetricsConf.NameSpace, map[string]string{}) + go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, ftCfgInstance.FtServiceCfg.MetricsConf.URL, + ftCfgInstance.FtServiceCfg.MetricsConf.DataBase, ftCfgInstance.FtServiceCfg.MetricsConf.UserName, ftCfgInstance.FtServiceCfg.MetricsConf.PassWd, + ftCfgInstance.FtServiceCfg.MetricsConf.NameSpace, map[string]string{}) } } @@ -173,115 +139,27 @@ func startNode(stack *node.Node) error { } func registerService(stack *node.Node) error { - var err error - // register ftservice - err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { - return ftservice.New(ctx, ftconfig.FtServiceCfg) + return stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { + return ftservice.New(ctx, ftCfgInstance.FtServiceCfg) }) - return err } func initConfig() { - if ftconfig.ConfigFileFlag != "" { - viper.SetConfigFile(ftconfig.ConfigFileFlag) + if ConfigFile != "" { + viper.SetConfigFile(ConfigFile) } else { - log.Info("No config file , use default configuration.") + fmt.Println("No config file , use default configuration.") return } if err := viper.ReadInConfig(); err != nil { - log.Error("Can't read config: %v, use default configuration.", err) + fmt.Printf("Can't read config: %v, use default configuration.", err) } } func init() { - RootCmd.AddCommand(utils.VersionCmd) cobra.OnInitialize(initConfig) - falgs := RootCmd.Flags() - // logging - falgs.BoolVar(&logConfig.PrintOrigins, "log_debug", logConfig.PrintOrigins, "Prepends log messages with call-site location (file and line number)") - falgs.IntVar(&logConfig.Level, "log_level", logConfig.Level, "Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail") - falgs.StringVar(&logConfig.Vmodule, "log_vmodule", logConfig.Vmodule, "Per-module verbosity: comma-separated list of = (e.g. ft/*=5,p2p=4)") - falgs.StringVar(&logConfig.BacktraceAt, "log_backtrace", logConfig.BacktraceAt, "Request a stack trace at a specific logging statement (e.g. \"block.go:271\")") - - // config file - falgs.StringVarP(&ftconfig.ConfigFileFlag, "config", "c", "", "TOML configuration file") - falgs.StringVarP(&ftconfig.GenesisFileFlag, "genesis", "g", "", "genesis json file") - - // node - falgs.StringVarP(&ftconfig.NodeCfg.DataDir, "datadir", "d", ftconfig.NodeCfg.DataDir, "Data directory for the databases and keystore") - falgs.BoolVar(&ftconfig.NodeCfg.UseLightweightKDF, "lightkdf", ftconfig.NodeCfg.UseLightweightKDF, "Reduce key-derivation RAM & CPU usage at some expense of KDF strength") - falgs.StringVar(&ftconfig.NodeCfg.IPCPath, "ipcpath", ftconfig.NodeCfg.IPCPath, "RPC:ipc file name") - falgs.StringVar(&ftconfig.NodeCfg.HTTPHost, "http_host", ftconfig.NodeCfg.HTTPHost, "RPC:http host address") - falgs.IntVar(&ftconfig.NodeCfg.HTTPPort, "http_port", ftconfig.NodeCfg.HTTPPort, "RPC:http host port") - falgs.StringSliceVar(&ftconfig.NodeCfg.HTTPModules, "http_api", ftconfig.NodeCfg.HTTPModules, "RPC:http api's offered over the HTTP-RPC interface") - falgs.StringSliceVar(&ftconfig.NodeCfg.HTTPCors, "http_cors", ftconfig.NodeCfg.HTTPCors, "RPC:Which to accept cross origin") - falgs.StringSliceVar(&ftconfig.NodeCfg.HTTPVirtualHosts, "http_vhosts", ftconfig.NodeCfg.HTTPVirtualHosts, "virtual hostnames from which to accept requests") - falgs.StringVar(&ftconfig.NodeCfg.WSHost, "ws_host", ftconfig.NodeCfg.WSHost, "RPC:websocket host address") - falgs.IntVar(&ftconfig.NodeCfg.WSPort, "ws_port", ftconfig.NodeCfg.WSPort, "RPC:websocket host port") - falgs.StringSliceVar(&ftconfig.NodeCfg.WSModules, "ws_api", ftconfig.NodeCfg.HTTPModules, "RPC:ws api's offered over the WS-RPC interface") - falgs.StringSliceVar(&ftconfig.NodeCfg.WSOrigins, "ws_origins", ftconfig.NodeCfg.WSOrigins, "RPC:ws origins from which to accept websockets requests") - falgs.BoolVar(&ftconfig.NodeCfg.WSExposeAll, "ws_exposeall", ftconfig.NodeCfg.WSExposeAll, "RPC:ws exposes all API modules via the WebSocket RPC interface rather than just the public ones.") - - // ftservice - falgs.IntVar(&ftconfig.FtServiceCfg.DatabaseCache, "databasecache", ftconfig.FtServiceCfg.DatabaseCache, "Megabytes of memory allocated to internal database caching") - - // txpool - falgs.BoolVar(&ftconfig.FtServiceCfg.TxPool.NoLocals, "txpool_nolocals", ftconfig.FtServiceCfg.TxPool.NoLocals, "Disables price exemptions for locally submitted transactions") - falgs.StringVar(&ftconfig.FtServiceCfg.TxPool.Journal, "txpool_journal", ftconfig.FtServiceCfg.TxPool.Journal, "Disk journal for local transaction to survive node restarts") - falgs.DurationVar(&ftconfig.FtServiceCfg.TxPool.Rejournal, "txpool_rejournal", ftconfig.FtServiceCfg.TxPool.Rejournal, "Time interval to regenerate the local transaction journal") - falgs.Uint64Var(&ftconfig.FtServiceCfg.TxPool.PriceBump, "txpool_pricebump", ftconfig.FtServiceCfg.TxPool.PriceBump, "Price bump percentage to replace an already existing transaction") - falgs.Uint64Var(&ftconfig.FtServiceCfg.TxPool.PriceLimit, "txpool_pricelimit", ftconfig.FtServiceCfg.TxPool.PriceLimit, "Minimum gas price limit to enforce for acceptance into the pool") - falgs.Uint64Var(&ftconfig.FtServiceCfg.TxPool.AccountSlots, "txpool_accountslots", ftconfig.FtServiceCfg.TxPool.AccountSlots, "Minimum number of executable transaction slots guaranteed per account") - falgs.Uint64Var(&ftconfig.FtServiceCfg.TxPool.AccountQueue, "txpool_accountqueue", ftconfig.FtServiceCfg.TxPool.AccountQueue, "Maximum number of non-executable transaction slots permitted per account") - falgs.Uint64Var(&ftconfig.FtServiceCfg.TxPool.GlobalSlots, "txpool_globalslots", ftconfig.FtServiceCfg.TxPool.GlobalSlots, "Maximum number of executable transaction slots for all accounts") - falgs.Uint64Var(&ftconfig.FtServiceCfg.TxPool.GlobalQueue, "txpool_globalqueue", ftconfig.FtServiceCfg.TxPool.GlobalQueue, "Minimum number of non-executable transaction slots for all accounts") - falgs.DurationVar(&ftconfig.FtServiceCfg.TxPool.Lifetime, "txpool_lifetime", ftconfig.FtServiceCfg.TxPool.Lifetime, "Maximum amount of time non-executable transaction are queued") - - // miner - falgs.BoolVar(&ftconfig.FtServiceCfg.Miner.Start, "miner_start", false, "miner start") - falgs.StringVar(&ftconfig.FtServiceCfg.Miner.Name, "miner_coinbase", ftconfig.FtServiceCfg.Miner.Name, "name for block mining rewards") - falgs.StringSliceVar(&ftconfig.FtServiceCfg.Miner.PrivateKeys, "miner_private", ftconfig.FtServiceCfg.Miner.PrivateKeys, "hex of private key for block mining rewards") - falgs.StringVar(&ftconfig.FtServiceCfg.Miner.ExtraData, "miner_extra", ftconfig.FtServiceCfg.Miner.ExtraData, "Block extra data set by the miner") - - // gas price oracle - falgs.IntVar(&ftconfig.FtServiceCfg.GasPrice.Blocks, "gpo_blocks", ftconfig.FtServiceCfg.GasPrice.Blocks, "Number of recent blocks to check for gas prices") - falgs.IntVar(&ftconfig.FtServiceCfg.GasPrice.Percentile, "gpo_percentile", ftconfig.FtServiceCfg.GasPrice.Percentile, "Suggested gas price is the given percentile of a set of recent transaction gas prices") - falgs.BoolVar(&ftconfig.FtServiceCfg.MetricsConf.MetricsFlag, "test_metricsflag", ftconfig.FtServiceCfg.MetricsConf.MetricsFlag, "flag that open statistical metrics") - falgs.BoolVar(&ftconfig.FtServiceCfg.MetricsConf.InfluxDBFlag, "test_influxdbflag", ftconfig.FtServiceCfg.MetricsConf.InfluxDBFlag, "flag that open influxdb thad store statistical metrics") - falgs.StringVar(&ftconfig.FtServiceCfg.MetricsConf.Url, "test_influxdburl", ftconfig.FtServiceCfg.MetricsConf.Url, "url that connect influxdb") - falgs.StringVar(&ftconfig.FtServiceCfg.MetricsConf.DataBase, "test_influxdbname", ftconfig.FtServiceCfg.MetricsConf.DataBase, "influxdb database name") - falgs.StringVar(&ftconfig.FtServiceCfg.MetricsConf.UserName, "test_influxdbuser", ftconfig.FtServiceCfg.MetricsConf.UserName, "indluxdb user name") - falgs.StringVar(&ftconfig.FtServiceCfg.MetricsConf.PassWd, "test_influxdbpasswd", ftconfig.FtServiceCfg.MetricsConf.PassWd, "influxdb user passwd") - falgs.StringVar(&ftconfig.FtServiceCfg.MetricsConf.NameSpace, "test_influxdbnamespace", ftconfig.FtServiceCfg.MetricsConf.NameSpace, "influxdb namespace") - - // p2p - falgs.IntVar(&ftconfig.NodeCfg.P2PConfig.MaxPeers, "p2p_maxpeers", ftconfig.NodeCfg.P2PConfig.MaxPeers, - "Maximum number of network peers (network disabled if set to 0)") - falgs.IntVar(&ftconfig.NodeCfg.P2PConfig.MaxPendingPeers, "p2p_maxpendpeers", ftconfig.NodeCfg.P2PConfig.MaxPendingPeers, - "Maximum number of pending connection attempts (defaults used if set to 0)") - falgs.IntVar(&ftconfig.NodeCfg.P2PConfig.DialRatio, "p2p_dialratio", ftconfig.NodeCfg.P2PConfig.DialRatio, - "DialRatio controls the ratio of inbound to dialed connections") - falgs.StringVar(&ftconfig.NodeCfg.P2PConfig.ListenAddr, "p2p_listenaddr", ftconfig.NodeCfg.P2PConfig.ListenAddr, - "Network listening address") - falgs.StringVar(&ftconfig.NodeCfg.P2PConfig.NodeDatabase, "p2p_nodedb", ftconfig.NodeCfg.P2PConfig.NodeDatabase, - "The path to the database containing the previously seen live nodes in the network") - falgs.StringVar(&ftconfig.NodeCfg.P2PConfig.Name, "p2p_nodename", ftconfig.NodeCfg.P2PConfig.Name, - "The node name of this server") - falgs.BoolVar(&ftconfig.NodeCfg.P2PConfig.NoDiscovery, "p2p_nodiscover", ftconfig.NodeCfg.P2PConfig.NoDiscovery, - "Disables the peer discovery mechanism (manual peer addition)") - falgs.BoolVar(&ftconfig.NodeCfg.P2PConfig.NoDial, "p2p_nodial", ftconfig.NodeCfg.P2PConfig.NoDial, - "The server will not dial any peers.") - falgs.UintVar(&ftconfig.NodeCfg.P2PConfig.NetworkID, "p2p_id", ftconfig.NodeCfg.P2PConfig.NetworkID, - "The ID of the p2p network. Nodes have different ID cannot communicate, even if they have same chainID and block data.") - falgs.StringVar(&ftconfig.NodeCfg.P2PBootNodes, "p2p_bootnodes", ftconfig.NodeCfg.P2PBootNodes, - "Node list file. BootstrapNodes are used to establish connectivity with the rest of the network") - falgs.StringVar(&ftconfig.NodeCfg.P2PStaticNodes, "p2p_staticnodes", ftconfig.NodeCfg.P2PStaticNodes, - "Node list file. Static nodes are used as pre-configured connections which are always maintained and re-connected on disconnects") - falgs.StringVar(&ftconfig.NodeCfg.P2PTrustNodes, "p2p_trustnodes", ftconfig.NodeCfg.P2PStaticNodes, - "Node list file. Trusted nodes are usesd as pre-configured connections which are always allowed to connect, even above the peer limit") - - // snapshot - falgs.BoolVar(&ftconfig.FtServiceCfg.Snapshot, "snapshot_enable", false, "snapshot enable") + RootCmd.AddCommand(utils.VersionCmd) + addFlags(RootCmd.Flags()) } // Execute adds all child commands to the root command sets flags appropriately. diff --git a/cmd/ftfinder/root.go b/cmd/ftfinder/root.go index eea47016..acc69816 100644 --- a/cmd/ftfinder/root.go +++ b/cmd/ftfinder/root.go @@ -35,9 +35,9 @@ var nodeConfig = node.Config{ // RootCmd represents the base command when called without any subcommands var RootCmd = &cobra.Command{ - // Use: "ftfinder", - // Short: "ftfinder is a fractal node discoverer", - // Long: `ftfinder is a fractal node discoverer`, + Use: "ftfinder", + Short: "ftfinder is a fractal node discoverer", + Long: `ftfinder is a fractal node discoverer`, // Uncomment the following line if your bare application // has an action associated with it: @@ -62,18 +62,42 @@ var RootCmd = &cobra.Command{ func init() { RootCmd.AddCommand(utils.VersionCmd) - falgs := RootCmd.Flags() + flags := RootCmd.Flags() // p2p - falgs.StringVarP(&nodeConfig.DataDir, "datadir", "d", nodeConfig.DataDir, "Data directory for the databases and keystore") - falgs.StringVar(&nodeConfig.P2PConfig.ListenAddr, "p2p_listenaddr", nodeConfig.P2PConfig.ListenAddr, - "Network listening address") - falgs.StringVar(&nodeConfig.P2PConfig.NodeDatabase, "p2p_nodedb", nodeConfig.P2PConfig.NodeDatabase, - "The path to the database containing the previously seen live nodes in the network") + flags.StringVarP( + &nodeConfig.DataDir, + "datadir", "d", + nodeConfig.DataDir, + "Data directory for the databases ", + ) - falgs.UintVar(&nodeConfig.P2PConfig.NetworkID, "p2p_id", nodeConfig.P2PConfig.NetworkID, - "The ID of the p2p network. Nodes have different ID cannot communicate, even if they have same chainID and block data.") - falgs.StringVar(&nodeConfig.P2PBootNodes, "p2p_bootnodes", nodeConfig.P2PBootNodes, - "Node list file. BootstrapNodes are used to establish connectivity with the rest of the network") + flags.StringVar( + &nodeConfig.P2PConfig.ListenAddr, + "p2p_listenaddr", + nodeConfig.P2PConfig.ListenAddr, + "Network listening address", + ) + + flags.StringVar( + &nodeConfig.P2PConfig.NodeDatabase, + "p2p_nodedb", + nodeConfig.P2PConfig.NodeDatabase, + "The path to the database containing the previously seen live nodes in the network", + ) + + flags.UintVar( + &nodeConfig.P2PConfig.NetworkID, + "p2p_id", + nodeConfig.P2PConfig.NetworkID, + "The ID of the p2p network. Nodes have different ID cannot communicate, even if they have same chainID and block data.", + ) + + flags.StringVar( + &nodeConfig.P2PBootNodes, + "p2p_bootnodes", + nodeConfig.P2PBootNodes, + "Node list file. BootstrapNodes are used to establish connectivity with the rest of the network", + ) defaultLogConfig().Setup() } diff --git a/cmd/ftkey/changepassphrase.go b/cmd/ftkey/changepassphrase.go deleted file mode 100644 index a4426732..00000000 --- a/cmd/ftkey/changepassphrase.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package main - -import ( - "fmt" - "io/ioutil" - "strings" - - "github.com/spf13/cobra" - - "github.com/fractalplatform/fractal/wallet/keystore" -) - -var ( - newPassphraseFlag string -) - -var changePassphraseCmd = &cobra.Command{ - Use: "changepassphrase", - Short: "change the passphrase on a keyfile", - Long: ` -Change the passphrase of a keyfile.`, - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - keyfilepath := args[0] - // Read key from file. - keyjson, err := ioutil.ReadFile(keyfilepath) - if err != nil { - fmt.Println("Failed to read the keyfile at ", keyfilepath, " : ", err) - return - } - - // Decrypt key with passphrase. - passphrase := getPassphrase() - key, err := keystore.DecryptKey(keyjson, passphrase) - if err != nil { - fmt.Println("Error decrypting key: ", err) - return - } - - // Get a new passphrase. - fmt.Println("Please provide a new passphrase") - var newPhrase string - if newPassphraseFlag != "" { - content, err := ioutil.ReadFile(newPassphraseFlag) - if err != nil { - fmt.Println("Failed to read new passphrase file ", newPassphraseFlag, " : ", err) - } - newPhrase = strings.TrimRight(string(content), "\r\n") - } else { - newPhrase = promptPassphrase(true) - } - - // Encrypt the key with the new passphrase. - newJSON, err := keystore.EncryptKey(key, newPhrase, keystore.StandardScryptN, keystore.StandardScryptP) - if err != nil { - fmt.Println("Error encrypting with new passphrase: ", err) - return - } - - // Then write the new keyfile in place of the old one. - if err := ioutil.WriteFile(keyfilepath, newJSON, 600); err != nil { - fmt.Println("Error writing new keyfile to disk: ", err) - return - } - - // Don't print anything. Just return successfully, - // producing a positive exit code. - }, -} - -func init() { - changePassphraseCmd.Flags().StringVar(&newPassphraseFlag, "newpasswordfile", "", "the file that contains the new passphrase for the keyfile") - RootCmd.AddCommand(changePassphraseCmd) -} diff --git a/cmd/ftkey/generate.go b/cmd/ftkey/generate.go deleted file mode 100644 index 3d02757e..00000000 --- a/cmd/ftkey/generate.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package main - -import ( - "crypto/ecdsa" - "fmt" - "io/ioutil" - "os" - "path/filepath" - - "github.com/fractalplatform/fractal/crypto" - "github.com/fractalplatform/fractal/wallet/keystore" - "github.com/spf13/cobra" -) - -const ( - defaultKeyfileName = "keyfile.json" -) - -type outputGenerate struct { - Address string - AddressEIP55 string -} - -var ( - privatekeyFlag string -) - -var generateCmd = &cobra.Command{ - Use: "generate", - Short: "generate new keyfile", - Long: ` - Generate a new keyfile. - - If you want to encrypt an existing private key, it can be specified by setting - --privatekey with the location of the file containing the private key. - `, - Args: cobra.RangeArgs(0, 1), - Run: func(cmd *cobra.Command, args []string) { - keyfilepath := defaultKeyfileName - if len(args) > 0 && args[0] != "" { - keyfilepath = args[0] - } - - if _, err := os.Stat(keyfilepath); err == nil { - fmt.Println("Keyfile already exists at ", keyfilepath) - return - } else if !os.IsNotExist(err) { - fmt.Println("Error checking if keyfile exists: ", err) - return - } - - var privateKey *ecdsa.PrivateKey - var err error - if privatekeyFlag != "" { - // Load private key from file. - privateKey, err = crypto.LoadECDSA(privatekeyFlag) - if err != nil { - fmt.Println("Can't load private key: ", err) - return - } - } else { - // If not loaded, generate random. - privateKey, err = crypto.GenerateKey() - if err != nil { - fmt.Println("Failed to generate random private key: ", err) - return - } - } - - // Create the keyfile object with a random UUID. - key := &keystore.Key{ - Addr: crypto.PubkeyToAddress(privateKey.PublicKey), - PrivateKey: privateKey, - } - - // Encrypt key with passphrase. - passphrase := promptPassphrase(true) - keyjson, err := keystore.EncryptKey(key, passphrase, keystore.StandardScryptN, keystore.StandardScryptP) - if err != nil { - fmt.Println("Error encrypting key: ", err) - return - } - - // Store the file to disk. - if err := os.MkdirAll(filepath.Dir(keyfilepath), 0700); err != nil { - fmt.Println("Could not create directory ", filepath.Dir(keyfilepath)) - return - } - if err := ioutil.WriteFile(keyfilepath, keyjson, 0600); err != nil { - fmt.Println("Failed to write keyfile to : ", keyfilepath, " ", err) - return - } - - // Output some information. - out := outputGenerate{ - Address: key.Addr.Hex(), - } - - if jsonFlag { - mustPrintJSON(out) - } else { - fmt.Println("Address:", out.Address) - } - }, -} - -func init() { - generateCmd.Flags().StringVar(&privatekeyFlag, "privatekey", "", "file containing a raw private key to encrypt") - RootCmd.AddCommand(generateCmd) -} diff --git a/cmd/ftkey/inspect.go b/cmd/ftkey/inspect.go deleted file mode 100644 index 58c8e525..00000000 --- a/cmd/ftkey/inspect.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package main - -import ( - "encoding/hex" - "fmt" - "io/ioutil" - - "github.com/fractalplatform/fractal/crypto" - "github.com/fractalplatform/fractal/wallet/keystore" - "github.com/spf13/cobra" -) - -var ( - privateFlag bool -) - -type outputInspect struct { - Address string - PublicKey string - PrivateKey string -} - -var inspectCmd = &cobra.Command{ - Use: "inspect", - Short: "inspect a keyfile", - Long: ` -Print various information about the keyfile. - -Private key information can be printed by using the --private flag; -make sure to use this feature with great caution!`, - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - keyfilepath := args[0] - - // Read key from file. - keyjson, err := ioutil.ReadFile(keyfilepath) - if err != nil { - fmt.Println("Failed to read the keyfile at ", keyfilepath, " : ", err) - return - } - - // Decrypt key with passphrase. - passphrase := getPassphrase() - key, err := keystore.DecryptKey(keyjson, passphrase) - if err != nil { - fmt.Println("Error decrypting key: ", err) - return - } - - // Output all relevant information we can retrieve. - out := outputInspect{ - Address: key.Addr.Hex(), - PublicKey: hex.EncodeToString( - crypto.FromECDSAPub(&key.PrivateKey.PublicKey)), - } - if privateFlag { - out.PrivateKey = hex.EncodeToString(crypto.FromECDSA(key.PrivateKey)) - } - - if jsonFlag { - mustPrintJSON(out) - } else { - fmt.Println("Address: ", out.Address) - fmt.Println("Public key: ", out.PublicKey) - if privateFlag { - fmt.Println("Private key: ", out.PrivateKey) - } - } - }, -} - -func init() { - inspectCmd.Flags().BoolVar(&privateFlag, "private", false, "include the private key in the output") - RootCmd.AddCommand(inspectCmd) -} diff --git a/cmd/ftkey/message.go b/cmd/ftkey/message.go deleted file mode 100644 index cefb4d36..00000000 --- a/cmd/ftkey/message.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package main - -import ( - "encoding/hex" - "fmt" - "io/ioutil" - "os" - - "github.com/spf13/cobra" - - "github.com/fractalplatform/fractal/common" - "github.com/fractalplatform/fractal/crypto" - "github.com/fractalplatform/fractal/wallet/keystore" -) - -type outputSign struct { - Signature string -} - -var ( - msgfileFlag string -) - -var signMessageCmd = &cobra.Command{ - Use: "signmessage", - Short: "sign a message", - Long: ` -Sign the message with a keyfile. - -To sign a message contained in a file, use the --msgfile flag. -`, - Args: cobra.RangeArgs(1, 2), - Run: func(cmd *cobra.Command, args []string) { - message := getMessage(args, 1) - - // Load the keyfile. - keyfilepath := args[0] - keyjson, err := ioutil.ReadFile(keyfilepath) - if err != nil { - fmt.Println("Failed to read the keyfile at ", keyfilepath, " : ", err) - return - } - - // Decrypt key with passphrase. - passphrase := getPassphrase() - key, err := keystore.DecryptKey(keyjson, passphrase) - if err != nil { - fmt.Println("Error decrypting key: ", err) - return - } - - signature, err := crypto.Sign(signHash(message), key.PrivateKey) - if err != nil { - fmt.Println("Failed to sign message: ", err) - return - } - out := outputSign{Signature: hex.EncodeToString(signature)} - if jsonFlag { - mustPrintJSON(out) - } else { - fmt.Println("Signature:", out.Signature) - } - }, -} - -type outputVerify struct { - Success bool - RecoveredAddress string - RecoveredPublicKey string -} - -var verifyMessageCmd = &cobra.Command{ - Use: "verifymessage", - Short: "verify the signature of a signed message", - Long: ` -Verify the signature of the message. -It is possible to refer to a file containing the message.`, - Args: cobra.RangeArgs(2, 3), - Run: func(cmd *cobra.Command, args []string) { - addressStr := args[0] - signatureHex := args[1] - message := getMessage(args, 2) - - if !common.IsHexAddress(addressStr) { - fmt.Println("Invalid address: ", addressStr) - return - } - address := common.HexToAddress(addressStr) - signature, err := hex.DecodeString(signatureHex) - if err != nil { - fmt.Println("Signature encoding is not hexadecimal: ", err) - return - } - - recoveredPubkey, err := crypto.SigToPub(signHash(message), signature) - if err != nil || recoveredPubkey == nil { - fmt.Println("Signature verification failed: ", err) - return - } - recoveredPubkeyBytes := crypto.FromECDSAPub(recoveredPubkey) - recoveredAddress := crypto.PubkeyToAddress(*recoveredPubkey) - success := address == recoveredAddress - - out := outputVerify{ - Success: success, - RecoveredPublicKey: hex.EncodeToString(recoveredPubkeyBytes), - RecoveredAddress: recoveredAddress.Hex(), - } - if jsonFlag { - mustPrintJSON(out) - } else { - if out.Success { - fmt.Println("Signature verification successful!") - } else { - fmt.Println("Signature verification failed!") - } - fmt.Println("Recovered public key:", out.RecoveredPublicKey) - fmt.Println("Recovered address:", out.RecoveredAddress) - } - }, -} - -func getMessage(args []string, msgarg int) []byte { - if msgfileFlag != "" { - if len(args) > msgarg { - fmt.Println("Can't use --msgfile and message argument at the same time.") - os.Exit(1) - } - msg, err := ioutil.ReadFile(msgfileFlag) - if err != nil { - fmt.Println("Can't read message file: ", err) - os.Exit(1) - } - return msg - } else if len(args) == msgarg+1 { - return []byte(args[msgarg]) - } - fmt.Println("Invalid number of arguments: want ", msgarg+1, ", got ", len(args)) - return nil -} - -func init() { - signMessageCmd.Flags().StringVar(&msgfileFlag, "msgfile", "", "file containing the message to sign/verify") - RootCmd.AddCommand(signMessageCmd) - RootCmd.AddCommand(verifyMessageCmd) -} diff --git a/cmd/ftkey/root.go b/cmd/ftkey/root.go deleted file mode 100644 index e3522493..00000000 --- a/cmd/ftkey/root.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package main - -import ( - "fmt" - "os" - - "github.com/fractalplatform/fractal/cmd/utils" - "github.com/spf13/cobra" -) - -var ( - passphraseFlag string - jsonFlag bool -) - -// RootCmd represents the base command when called without any subcommands -var RootCmd = &cobra.Command{ - Use: "fkey", - Short: "fkey is a fractal key manager", - Long: `fkey is a fractal key manager`, - - // Uncomment the following line if your bare application - // has an action associated with it: - Run: func(cmd *cobra.Command, args []string) { - cmd.HelpFunc()(cmd, args) - }, -} - -func init() { - RootCmd.AddCommand(utils.VersionCmd) - RootCmd.PersistentFlags().StringVar(&passphraseFlag, "passwordfile", "", "the file that contains the passphrase for the keyfile") - RootCmd.PersistentFlags().BoolVar(&jsonFlag, "json", false, "output JSON instead of human-readable format") -} - -// Execute adds all child commands to the root command sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the rootCmd. -func Execute() { - if err := RootCmd.Execute(); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(-1) - } -} diff --git a/cmd/ftkey/utils.go b/cmd/ftkey/utils.go deleted file mode 100644 index 14e2ad19..00000000 --- a/cmd/ftkey/utils.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "strings" - - "github.com/ethereum/go-ethereum/log" - "github.com/fractalplatform/fractal/crypto" - "github.com/fractalplatform/fractal/utils/console" -) - -// promptPassphrase prompts the user for a passphrase. Set confirmation to true -// to require the user to confirm the passphrase. -func promptPassphrase(confirmation bool) string { - passphrase, err := console.Stdin.PromptPassword("Passphrase: ") - if err != nil { - log.Crit("Failed to read passphrase: %v", err) - } - - if confirmation { - confirm, err := console.Stdin.PromptPassword("Repeat passphrase: ") - if err != nil { - log.Crit("Failed to read passphrase confirmation: %v", err) - } - if passphrase != confirm { - log.Crit("Passphrases do not match") - } - } - - return passphrase -} - -// getPassphrase obtains a passphrase given by the user. It first checks the -// --passfile command line flag and ultimately prompts the user for a -// passphrase. -func getPassphrase() string { - // Look for the --passwordfile flag. - if passphraseFlag != "" { - content, err := ioutil.ReadFile(passphraseFlag) - if err != nil { - log.Crit("Failed to read passphrase file '%s': %v", - passphraseFlag, err) - } - return strings.TrimRight(string(content), "\r\n") - } - - // Otherwise prompt the user for the passphrase. - return promptPassphrase(false) -} - -// signHash is a helper function that calculates a hash for the given message -// that can be safely used to calculate a signature from. -// -// The hash is calulcated as -// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}). -// -// This gives context to the signed message and prevents signing of transactions. -func signHash(data []byte) []byte { - msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data) - return crypto.Keccak256([]byte(msg)) -} - -// mustPrintJSON prints the JSON encoding of the given object and -// exits the program with an error message when the marshaling fails. -func mustPrintJSON(jsonObject interface{}) { - str, err := json.MarshalIndent(jsonObject, "", " ") - if err != nil { - log.Crit("Failed to marshal JSON object: %v", err) - } - fmt.Println(string(str)) -} diff --git a/cmd/utils/logconfig.go b/cmd/utils/logconfig.go new file mode 100644 index 00000000..65815230 --- /dev/null +++ b/cmd/utils/logconfig.go @@ -0,0 +1,63 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package utils + +import ( + "io" + "os" + + "github.com/ethereum/go-ethereum/log" + colorable "github.com/mattn/go-colorable" + "github.com/mattn/go-isatty" +) + +var glogger *log.GlogHandler + +// LogConfig represents a log config +type LogConfig struct { + PrintOrigins bool `mapstructure:"printorigins"` + Level int `mapstructure:"level"` + Vmodule string `mapstructure:"vmodule"` + BacktraceAt string `mapstructure:"backtraceat"` +} + +// DefaultLogConfig returns a default config +func DefaultLogConfig() *LogConfig { + return &LogConfig{ + PrintOrigins: false, + Level: 3, + } +} + +func init() { + usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" + output := io.Writer(os.Stderr) + if usecolor { + output = colorable.NewColorableStderr() + } + glogger = log.NewGlogHandler(log.StreamHandler(output, log.TerminalFormat(usecolor))) +} + +//Setup initializes logging based on the LogConfig +func (lc *LogConfig) Setup() { + // logging + log.PrintOrigins(lc.PrintOrigins) + glogger.Verbosity(log.Lvl(lc.Level)) + glogger.Vmodule(lc.Vmodule) + glogger.BacktraceAt(lc.BacktraceAt) + log.Root().SetHandler(glogger) +} diff --git a/common/address.go b/common/address.go index ce24620d..0f970b3f 100644 --- a/common/address.go +++ b/common/address.go @@ -17,6 +17,7 @@ package common import ( + "bytes" "encoding/hex" "encoding/json" "fmt" @@ -117,6 +118,10 @@ func (a Address) MarshalText() ([]byte, error) { return hexutil.Bytes(a[:]).MarshalText() } +func (a Address) Compare(x Address) int { + return bytes.Compare(a.Bytes(), x.Bytes()) +} + // UnmarshalText parses a hash in hex syntax. func (a *Address) UnmarshalText(input []byte) error { return hexutil.UnmarshalFixedText("Address", input, a[:]) diff --git a/common/author.go b/common/author.go new file mode 100644 index 00000000..b8439db0 --- /dev/null +++ b/common/author.go @@ -0,0 +1,126 @@ +package common + +import ( + "errors" + "io" + + "github.com/fractalplatform/fractal/utils/rlp" +) + +type AuthorType uint8 + +const ( + AccountNameType AuthorType = iota + PubKeyType + AddressType +) + +type ( + Author struct { + Owner + Weight uint64 `json:"weight"` + } + Owner interface { + String() string + } +) + +type AccountAuthor struct { + Account Name +} +type StorageAuthor struct { + Type AuthorType + DataRaw rlp.RawValue + Weight uint64 +} + +func NewAuthor(owner Owner, weight uint64) *Author { + return &Author{Owner: owner, Weight: weight} +} + +func (a *Author) GetWeight() uint64 { + return a.Weight +} + +func (a *Author) EncodeRLP(w io.Writer) error { + storageAuthor, err := a.encode() + if err != nil { + return err + } + return rlp.Encode(w, storageAuthor) +} + +func (a *Author) encode() (*StorageAuthor, error) { + switch aTy := a.Owner.(type) { + case Name: + value, err := rlp.EncodeToBytes(&aTy) + if err != nil { + return nil, err + } + return &StorageAuthor{ + Type: AccountNameType, + DataRaw: value, + Weight: a.Weight, + }, nil + case PubKey: + value, err := rlp.EncodeToBytes(&aTy) + if err != nil { + return nil, err + } + return &StorageAuthor{ + Type: PubKeyType, + DataRaw: value, + Weight: a.Weight, + }, nil + case Address: + value, err := rlp.EncodeToBytes(&aTy) + if err != nil { + return nil, err + } + return &StorageAuthor{ + Type: AddressType, + DataRaw: value, + Weight: a.Weight, + }, nil + } + return nil, errors.New("Author encode failed") +} + +func (a *Author) DecodeRLP(s *rlp.Stream) error { + storageAuthor := new(StorageAuthor) + err := s.Decode(storageAuthor) + if err != nil { + return err + } + return a.decode(storageAuthor) +} + +func (a *Author) decode(sa *StorageAuthor) error { + switch sa.Type { + case AccountNameType: + var name Name + if err := rlp.DecodeBytes(sa.DataRaw, &name); err != nil { + return err + } + a.Owner = name + a.Weight = sa.Weight + return nil + case PubKeyType: + var pubKey PubKey + if err := rlp.DecodeBytes(sa.DataRaw, &pubKey); err != nil { + return err + } + a.Owner = pubKey + a.Weight = sa.Weight + return nil + case AddressType: + var address Address + if err := rlp.DecodeBytes(sa.DataRaw, &address); err != nil { + return err + } + a.Owner = address + a.Weight = sa.Weight + return nil + } + return errors.New("Author decode failed") +} diff --git a/common/name.go b/common/name.go index 761a918a..52996078 100644 --- a/common/name.go +++ b/common/name.go @@ -23,15 +23,65 @@ import ( "regexp" "strings" + "github.com/ethereum/go-ethereum/log" "github.com/fractalplatform/fractal/utils/rlp" ) // Name represents the account name type Name string -// IsValidName verifies whether a string can represent a valid name or not. -func IsValidName(s string) bool { - return regexp.MustCompile("^[a-z0-9]{8,16}$").MatchString(s) +var accountNameCheck *regexp.Regexp +var assetNameCheck *regexp.Regexp + +var ( + AccountNameLevel uint64 = 1 + AccountNameLen uint64 = 16 + SubAccountNameLen uint64 = 8 + AssetNameLevel uint64 = 1 + AssetNameLen uint64 = 16 + SubAssetNameLen uint64 = 8 +) + +func init() { + SetAccountNameCheckRule(AccountNameLevel, AccountNameLen, SubAccountNameLen) + SetAssetNameCheckRule(AssetNameLevel, AssetNameLen, SubAssetNameLen) +} + +func SetAccountNameCheckRule(nameLevel, nameLen, subNameLen uint64) { + var nameCheck string + if nameLevel == 0 { + nameCheck = fmt.Sprintf("^[a-z0-9]{7,%d}$", nameLen) + } else { + nameCheck = fmt.Sprintf("^[a-z0-9]{7,%d}(\\.[a-z0-9]{1,%d}){0,%d}$", nameLen, subNameLen, nameLevel) + } + log.Info("Account name level", "level", nameLevel, "name length", nameLen, "sub name length", subNameLen) + accountNameCheck = regexp.MustCompile(nameCheck) +} + +func SetAssetNameCheckRule(nameLevel, nameLen, subNameLen uint64) { + var nameCheck string + if nameLevel == 0 { + nameCheck = fmt.Sprintf("^[a-z0-9]{2,%d}$", nameLen) + } else { + nameCheck = fmt.Sprintf("^[a-z0-9]{2,%d}(\\.[a-z0-9]{1,%d}){0,%d}$", nameLen, subNameLen, nameLevel) + } + log.Info("Asset name level", "level", nameLevel, "name length", nameLen, "sub name length", subNameLen) + assetNameCheck = regexp.MustCompile(nameCheck) +} + +// IsValidAccountName verifies whether a string can represent a valid name or not. +func IsValidAccountName(s string) bool { + if accountNameCheck == nil { + return false + } + return accountNameCheck.MatchString(s) +} + +func IsValidAssetName(s string) bool { + if assetNameCheck == nil { + return false + } + return assetNameCheck.MatchString(s) } // StrToName returns Name with string of s. @@ -43,6 +93,15 @@ func StrToName(s string) Name { return n } +// StrToName returns Name with string of s. +func StringToName(s string) (Name, error) { + var n Name + if !n.SetString(s) { + return n, fmt.Errorf("invalid name %v", s) + } + return n, nil +} + func parseName(s string) (Name, error) { var n Name if !n.SetString(s) { @@ -61,7 +120,7 @@ func BigToName(b *big.Int) (Name, error) { return BytesToName(b.Bytes()) } // SetString sets the name to the value of b.. func (n *Name) SetString(s string) bool { - if !IsValidName(s) { + if !IsValidAccountName(s) { return false } *n = Name(s) @@ -90,7 +149,7 @@ func (n *Name) UnmarshalJSON(data []byte) error { } // EncodeRLP implements rlp.Encoder -func (n *Name) EncodeRLP(w io.Writer) error { +func (n Name) EncodeRLP(w io.Writer) error { str := n.String() if len(str) != 0 { if _, err := parseName(str); err != nil { @@ -124,3 +183,37 @@ func (n Name) String() string { // Big converts a name to a big integer. func (n Name) Big() *big.Int { return new(big.Int).SetBytes([]byte(n.String())) } + +func (n Name) IsValidCreator(name string) bool { + + creator := n.String() + current := name + trimName := strings.TrimPrefix(current, creator) + + if strings.Index(trimName, ".") != 0 { + return false + } + + return true +} + +func (n Name) AccountNameLevel() int { + return len(strings.Split(n.String(), ".")) +} + +func SplitString(s string) []string { + return strings.Split(s, ".") +} + +func IsValidCreator(name string, subName string) bool { + + creator := name + current := subName + trimName := strings.TrimPrefix(current, creator) + + if strings.Index(trimName, ".") != 0 { + return false + } + + return true +} diff --git a/common/name_test.go b/common/name_test.go index 0d38dd30..9321e517 100644 --- a/common/name_test.go +++ b/common/name_test.go @@ -42,8 +42,8 @@ func TestValidateName(t *testing.T) { } for _, test := range tests { - if result := IsValidName(test.str); result != test.exp { - t.Errorf("IsValidName(%s) == %v; expected %v, len:%v", + if result := IsValidAccountName(test.str); result != test.exp { + t.Errorf("IsValidAccountName(%s) == %v; expected %v, len:%v", test.str, result, test.exp, len(test.str)) } } @@ -65,6 +65,7 @@ func TestNameUnmarshalJSON(t *testing.T) { {"short", true}, {"longnamelongnamelongnamelongname", true}, } + for i, test := range tests { bytes, err := json.Marshal(test.Input) diff --git a/common/pubkey_test.go b/common/pubkey_test.go index 905dc196..12402bec 100644 --- a/common/pubkey_test.go +++ b/common/pubkey_test.go @@ -18,7 +18,6 @@ package common import ( "encoding/json" - "fmt" "math/big" "testing" ) @@ -48,9 +47,6 @@ func TestIsHexPubkey(t *testing.T) { p1 := HexToPubKey("0x0462ad0e026eb91232feec4f333108b7f658ea34bc2715e0f4d575930398853942c9a09e638d19f9a18d47085e7b8ae59e257bd97e85acf9107947b5acbd8f969e") p.SetBytes(p1.Bytes()) - - fmt.Println("====>", p.String()) - } func TestPubKeyUnmarshalJSON(t *testing.T) { diff --git a/consensus/consensus.go b/consensus/consensus.go index 7c8c3f05..6b44fb23 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -56,7 +56,7 @@ type IChainReader interface { StateAt(hash common.Hash) (*state.StateDB, error) // WriteBlockWithState writes the block and all associated state to the database. - WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) error + WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (bool, error) // CalcGasLimit computes the gas limit of the next block after parent. CalcGasLimit(parent *types.Block) uint64 @@ -98,7 +98,9 @@ type IEngine interface { Engine() IEngine - ProcessAction(chainCfg *params.ChainConfig, state *state.StateDB, action *types.Action) error + ProcessAction(chainCfg *params.ChainConfig, state *state.StateDB, action *types.Action) ([]*types.InternalAction, error) + + GetDelegatedByTime(name string, timestamp uint64, state *state.StateDB) (*big.Int, *big.Int, uint64, error) IAPI } diff --git a/consensus/dpos/README.md b/consensus/dpos/README.md deleted file mode 100644 index 161aad7d..00000000 --- a/consensus/dpos/README.md +++ /dev/null @@ -1,55 +0,0 @@ -## dpos生成者注册及投票者投票 - -### 参数说明 -- `UnitStake` 一票所代表的权益 -- `MaxURLLen` 生产者URL最大长度 -- `ProducerMinQuantity` 生成者需要抵押的最低票数 -- `VoterMinQuantity` 投票者需要抵押的最低票数 -- `ActivatedMinQuantity` dpos启动需要的最低票数 -- `BlockInterval` 每轮生产者出块间隔 -- `BlockFrequency` 每轮生产者连续出块个数 -- `ProducerScheduleSize`: 每轮生成者最大个数 - -### dpos启动 -- 网络投票总数 >= `ActivatedMinQuantity` && 已注册的生成者个数 >= `ProducerScheduleSize` * 2 / 3 + 1 -- 启动后, 启动条件必须一直满足 - -### 注册生产者/更新生产者 -> RegProducer(producer string, address string, url string, stake *big.Int) error -- 账户名必须存在 -- 账户名与提供的公钥匹配 -- 提供的URL长度 <= `MaxURLLen` -- 抵押stake 需满足`UnitStake`的整数倍, 且商 >= `ProducerMinQuantity` -- 未投票 - -### 注销生成者 -> UnregProducer(producer string) error -- 有效生产者 -- 注销后,已注册生产者个数 >= `ProducerScheduleSize` * 2 / 3 + 1 -- 注销后,网络投票总数 >= `ActivatedMinQuantity` -- 未存在投票者投票 - -### 投票者投票 -> VoteProducer(voter string, producer string, stake *big.Int) error -- 账户名必须存在 -- 抵押stake 需满足`UnitStake`的整数倍, 且商 >= `VoterMinQuantity` -- 未注册生产者 -- 未投票 -- 有效的生产者 - -### 投票者改票 -> ChangeProducer(voter string, producer string) error -- 已投票 -- 不是相同的生产者 -- 有效的生产者 - -### 投票者取消投票 -> UnvoteProducer(voter string) error -- 已投票 -- 取消后,网络投票总数 >= `ActivatedMinQuantity` - -### 生产者取消投票者的投票 -> UnvoteVoter(producer string, voter string) error -- 投票者已投票 -- 投票的生产者确实是该生产者 -- 取消后,网络投票总数 >= `ActivatedMinQuantity` diff --git a/consensus/dpos/api.go b/consensus/dpos/api.go index 62c683ae..49e4bf68 100644 --- a/consensus/dpos/api.go +++ b/consensus/dpos/api.go @@ -32,9 +32,9 @@ type API struct { } type Irreversible_Ret struct { - ProposedIrreversible uint64 - LastIrreversible uint64 - BftIrreversible uint64 + ProposedIrreversible uint64 `json:"proposedIrreversible"` + BftIrreversible uint64 `json:"bftIrreversible"` + Reversible uint64 `json:"reversible"` } func (api *API) Info() (interface{}, error) { @@ -43,10 +43,9 @@ func (api *API) Info() (interface{}, error) { func (api *API) Irreversible() (interface{}, error) { ret := &Irreversible_Ret{} - - ret.ProposedIrreversible = api.dpos.proposedIrreversibleNum - ret.LastIrreversible = api.dpos.calcLastIrreversible() - ret.BftIrreversible = api.dpos.bftIrreversibleNum + ret.Reversible = api.chain.CurrentHeader().Number.Uint64() + ret.ProposedIrreversible = api.dpos.CalcProposedIrreversible(api.chain, nil, false) + ret.BftIrreversible = api.dpos.CalcBFTIrreversible() return ret, nil } @@ -63,7 +62,7 @@ func (api *API) Account(name string) (interface{}, error) { return vote, err } - if prod, err := sys.GetProducer(name); err != nil { + if prod, err := sys.GetCadidate(name); err != nil { return nil, err } else if prod != nil { return prod, err @@ -72,20 +71,20 @@ func (api *API) Account(name string) (interface{}, error) { return nil, nil } -func (api *API) Producers() ([]map[string]interface{}, error) { +func (api *API) Cadidates() ([]map[string]interface{}, error) { pfileds := []map[string]interface{}{} sys, err := api.system() if err != nil { return nil, err } - producers, err := sys.Producers() - if err != nil || len(producers) == 0 { + cadidates, err := sys.Cadidates() + if err != nil || len(cadidates) == 0 { return pfileds, err } - prods := producerInfoArray{} - for _, prod := range producers { + prods := cadidateInfoArray{} + for _, prod := range cadidates { prods = append(prods, prod) } sort.Sort(prods) @@ -123,22 +122,15 @@ func (api *API) LatestEpcho() (interface{}, error) { } func (api *API) ValidateEpcho() (interface{}, error) { - - cur_header := api.chain.CurrentHeader() - height := cur_header.Number.Uint64() - 1 - - target_ts := big.NewInt(cur_header.Time.Int64() - int64(api.dpos.config.DelayEcho*api.dpos.config.epochInterval())) - - for height > 0 { - pheader := api.chain.GetHeaderByNumber(height) - if pheader.Time.Cmp(target_ts) != 1 { + curHeader := api.chain.CurrentHeader() + targetTS := big.NewInt(curHeader.Time.Int64() - int64(api.dpos.config.DelayEcho*api.dpos.config.epochInterval())) + for curHeader.Number.Uint64() > 0 { + if curHeader.Time.Cmp(targetTS) == -1 { break - } else { - height -= 1 } + curHeader = api.chain.GetHeaderByHash(curHeader.ParentHash) } - - return api.Epcho(height) + return api.Epcho(curHeader.Number.Uint64() + 1) } func (api *API) system() (*System, error) { diff --git a/consensus/dpos/config.go b/consensus/dpos/config.go index aca8a4f5..53a6f5b5 100644 --- a/consensus/dpos/config.go +++ b/consensus/dpos/config.go @@ -30,12 +30,12 @@ import ( var DefaultConfig = &Config{ MaxURLLen: 512, UnitStake: big.NewInt(1000), - ProducerMinQuantity: big.NewInt(10), + CadidateMinQuantity: big.NewInt(10), VoterMinQuantity: big.NewInt(1), ActivatedMinQuantity: big.NewInt(100), BlockInterval: 3000, BlockFrequency: 6, - ProducerScheduleSize: 3, + CadidateScheduleSize: 3, DelayEcho: 2, AccountName: "ftsystemdpos", SystemName: "ftsystemio", @@ -48,21 +48,21 @@ var DefaultConfig = &Config{ // Config dpos configures type Config struct { // consensus fileds - MaxURLLen uint64 // url length - UnitStake *big.Int // state unit - ProducerMinQuantity *big.Int // min quantity - VoterMinQuantity *big.Int // min quantity - ActivatedMinQuantity *big.Int // min active quantity - BlockInterval uint64 - BlockFrequency uint64 - ProducerScheduleSize uint64 - DelayEcho uint64 - AccountName string - SystemName string - SystemURL string - ExtraBlockReward *big.Int - BlockReward *big.Int - Decimals uint64 + MaxURLLen uint64 `json:"maxURLLen"` // url length + UnitStake *big.Int `json:"unitStake"` // state unit + CadidateMinQuantity *big.Int `json:"cadidateMinQuantity"` // min quantity + VoterMinQuantity *big.Int `json:"voterMinQuantity"` // min quantity + ActivatedMinQuantity *big.Int `json:"activatedMinQuantity"` // min active quantity + BlockInterval uint64 `json:"blockInterval"` + BlockFrequency uint64 `json:"blockFrequency"` + CadidateScheduleSize uint64 `json:"cadidateScheduleSize"` + DelayEcho uint64 `json:"delayEcho"` + AccountName string `json:"accountName"` + SystemName string `json:"systemName"` + SystemURL string `json:"systemURL"` + ExtraBlockReward *big.Int `json:"extraBlockReward"` + BlockReward *big.Int `json:"blockReward"` + Decimals uint64 `json:"decimals"` // cache files decimal atomic.Value @@ -116,7 +116,7 @@ func (cfg *Config) epochInterval() uint64 { if epochInter := cfg.epochInter.Load(); epochInter != nil { return epochInter.(uint64) } - epochInter := cfg.blockInterval() * cfg.BlockFrequency * cfg.ProducerScheduleSize + epochInter := cfg.blockInterval() * cfg.BlockFrequency * cfg.CadidateScheduleSize cfg.epochInter.Store(epochInter) return epochInter } @@ -126,7 +126,7 @@ func (cfg *Config) consensusSize() uint64 { return safeSize.(uint64) } - safeSize := cfg.ProducerScheduleSize*2/3 + 1 + safeSize := cfg.CadidateScheduleSize*2/3 + 1 cfg.safeSize.Store(safeSize) return safeSize } diff --git a/consensus/dpos/config_test.go b/consensus/dpos/config_test.go index 55db5ddb..19f36263 100644 --- a/consensus/dpos/config_test.go +++ b/consensus/dpos/config_test.go @@ -51,7 +51,7 @@ func TestRLP(t *testing.T) { DefaultConfig.BlockInterval = 500 DefaultConfig.blockInter.Store(DefaultConfig.BlockInterval * uint64(time.Millisecond)) - DefaultConfig.epochInter.Store(DefaultConfig.blockInterval() * DefaultConfig.BlockFrequency * DefaultConfig.ProducerScheduleSize) + DefaultConfig.epochInter.Store(DefaultConfig.blockInterval() * DefaultConfig.BlockFrequency * DefaultConfig.CadidateScheduleSize) now = uint64(time.Now().UnixNano()) slot = DefaultConfig.slot(now) nslot = DefaultConfig.nextslot(now) diff --git a/consensus/dpos/database.go b/consensus/dpos/database.go index becd8ed2..f51e821a 100644 --- a/consensus/dpos/database.go +++ b/consensus/dpos/database.go @@ -29,11 +29,11 @@ var ( // IDB dpos database type IDB interface { - SetProducer(*producerInfo) error - DelProducer(string) error - GetProducer(string) (*producerInfo, error) - Producers() ([]*producerInfo, error) - ProducersSize() (uint64, error) + SetCadidate(*cadidateInfo) error + DelCadidate(string) error + GetCadidate(string) (*cadidateInfo, error) + Cadidates() ([]*cadidateInfo, error) + CadidatesSize() (uint64, error) SetVoter(*voterInfo) error DelVoter(string, string) error @@ -47,37 +47,42 @@ type IDB interface { Delegate(string, *big.Int) error Undelegate(string, *big.Int) error IncAsset2Acct(string, string, *big.Int) error + + GetDelegatedByTime(string, uint64) (*big.Int, *big.Int, uint64, error) } -type producerInfo struct { - Name string // producer name - URL string // producer url - Quantity *big.Int // producer stake quantity - TotalQuantity *big.Int // producer total stake quantity - Height uint64 // timestamp +type cadidateInfo struct { + Name string `json:"name"` // cadidate name + URL string `json:"url"` // cadidate url + Quantity *big.Int `json:"quantity"` // cadidate stake quantity + TotalQuantity *big.Int `json:"totalQuantity"` // cadidate total stake quantity + Height uint64 `json:"height"` // timestamp + Counter uint64 `json:"counter"` + InBlackList bool `json:"inBlackList"` } type voterInfo struct { - Name string // voter name - Producer string // producer approved by this voter - Quantity *big.Int // stake approved by this voter - Height uint64 // timestamp + Name string `json:"name"` // voter name + Cadidate string `json:"cadidate"` // cadidate approved by this voter + Quantity *big.Int `json:"quantity"` // stake approved by this voter + Height uint64 `json:"height"` // timestamp } type globalState struct { - Height uint64 // block height - ActivatedProducerScheduleUpdate uint64 // update time - ActivatedProducerSchedule []string // producers - ActivatedTotalQuantity *big.Int // the sum of activate producer votes - TotalQuantity *big.Int // the sum of all producer votes + Height uint64 `json:"height"` // block height + ActivatedCadidateScheduleUpdate uint64 `json:"activatedCadidateScheduleUpdate"` // update time + ActivatedCadidateSchedule []string `json:"activatedCadidateSchedule"` // cadidates + ActivatedTotalQuantity *big.Int `json:"activatedTotalQuantity"` // the sum of activate cadidate votes + TotalQuantity *big.Int `json:"totalQuantity"` // the sum of all cadidate votes + TakeOver bool `json:"takeOver"` // systemio take over dpos } -type producerInfoArray []*producerInfo +type cadidateInfoArray []*cadidateInfo -func (prods producerInfoArray) Len() int { +func (prods cadidateInfoArray) Len() int { return len(prods) } -func (prods producerInfoArray) Less(i, j int) bool { +func (prods cadidateInfoArray) Less(i, j int) bool { val := prods[i].TotalQuantity.Cmp(prods[j].TotalQuantity) if val == 0 { if prods[i].Height == prods[j].Height { @@ -87,6 +92,6 @@ func (prods producerInfoArray) Less(i, j int) bool { } return val > 0 } -func (prods producerInfoArray) Swap(i, j int) { +func (prods cadidateInfoArray) Swap(i, j int) { prods[i], prods[j] = prods[j], prods[i] } diff --git a/consensus/dpos/database_test.go b/consensus/dpos/database_test.go index 9865408c..21fedc89 100644 --- a/consensus/dpos/database_test.go +++ b/consensus/dpos/database_test.go @@ -31,8 +31,8 @@ func TestDatabase(t *testing.T) { // gstate gstate := &globalState{ Height: 10, - ActivatedProducerScheduleUpdate: uint64(time.Now().UnixNano()), - ActivatedProducerSchedule: []string{}, + ActivatedCadidateScheduleUpdate: uint64(time.Now().UnixNano()), + ActivatedCadidateSchedule: []string{}, ActivatedTotalQuantity: big.NewInt(1000), TotalQuantity: big.NewInt(100000), } @@ -51,7 +51,7 @@ func TestDatabase(t *testing.T) { // voter vote := &voterInfo{ Name: "fos", - Producer: "fos", + Cadidate: "fos", Quantity: big.NewInt(1000), } @@ -68,8 +68,8 @@ func TestDatabase(t *testing.T) { vjson, _ := json.Marshal(nvote) t.Log("voter ", string(vjson)) - // producer - prod := &producerInfo{ + // cadidate + prod := &cadidateInfo{ Name: "fos", URL: "www.fractalproject.com", Quantity: big.NewInt(1000), @@ -81,7 +81,7 @@ func TestDatabase(t *testing.T) { panic(fmt.Sprintf("prod EncodeToBytes--%v", err)) } - nprod := &producerInfo{} + nprod := &cadidateInfo{} if err := rlp.DecodeBytes(pval, nprod); err != nil { panic(fmt.Sprintf("prod DecodeBytes--%v", err)) } @@ -89,21 +89,21 @@ func TestDatabase(t *testing.T) { pjson, _ := json.Marshal(nprod) t.Log("prod ", string(pjson)) - prod1 := &producerInfo{ + prod1 := &cadidateInfo{ Name: "fos1", URL: "www.fractalproject.com", Quantity: big.NewInt(1000), TotalQuantity: big.NewInt(2000), } - prod2 := &producerInfo{ + prod2 := &cadidateInfo{ Name: "fos2", URL: "www.fractalproject.com", Quantity: big.NewInt(1000), TotalQuantity: big.NewInt(1000), } - prods := producerInfoArray{} + prods := cadidateInfoArray{} prods = append(prods, prod) prods = append(prods, prod1) prods = append(prods, prod2) diff --git a/consensus/dpos/dpos.go b/consensus/dpos/dpos.go index d34c4a0f..20f8dff1 100644 --- a/consensus/dpos/dpos.go +++ b/consensus/dpos/dpos.go @@ -30,18 +30,19 @@ import ( "github.com/fractalplatform/fractal/crypto" "github.com/fractalplatform/fractal/state" "github.com/fractalplatform/fractal/types" - "github.com/fractalplatform/fractal/utils/rlp" - "golang.org/x/crypto/sha3" + lru "github.com/hashicorp/golang-lru" ) var ( errMissingSignature = errors.New("extra-data 65 byte suffix signature missing") - errMismatchSignerAndValidator = errors.New("mismatch block signer and producer") + errMismatchSignerAndValidator = errors.New("mismatch block signer and cadidate") errInvalidMintBlockTime = errors.New("invalid time to mint the block") - errInvalidBlockProducer = errors.New("invalid block producer") + errInvalidBlockCadidate = errors.New("invalid block cadidate") errInvalidTimestamp = errors.New("invalid timestamp") - ErrIllegalProducerName = errors.New("illegal producer name") - ErrIllegalProducerPubKey = errors.New("illegal producer pubkey") + ErrIllegalCadidateName = errors.New("illegal cadidate name") + ErrIllegalCadidatePubKey = errors.New("illegal cadidate pubkey") + ErrTooMuchRreversible = errors.New("too much rreversible blocks") + ErrSystemTakeOver = errors.New("system account take over") errUnknownBlock = errors.New("unknown block") extraSeal = 65 timeOfGenesisBlock int64 @@ -53,6 +54,10 @@ type stateDB struct { state *state.StateDB } +func (s *stateDB) GetSnapshot(key string, timestamp uint64) ([]byte, error) { + return s.state.GetSnapshot(s.name, key, timestamp) +} + func (s *stateDB) Get(key string) ([]byte, error) { return s.state.Get(s.name, key) } @@ -65,11 +70,12 @@ func (s *stateDB) Delete(key string) error { return nil } func (s *stateDB) Delegate(from string, amount *big.Int) error { - accountDB, err := accountmanager.NewAccountManager(s.state) - if err != nil { - return err - } - return accountDB.TransferAsset(common.StrToName(from), common.StrToName(s.name), s.assetid, amount) + return nil + // accountDB, err := accountmanager.NewAccountManager(s.state) + // if err != nil { + // return err + // } + // return accountDB.TransferAsset(common.StrToName(from), common.StrToName(s.name), s.assetid, amount) } func (s *stateDB) Undelegate(to string, amount *big.Int) error { accountDB, err := accountmanager.NewAccountManager(s.state) @@ -90,9 +96,10 @@ func (s *stateDB) IsValidSign(name string, pubkey []byte) bool { if err != nil { return false } - return accountDB.IsValidSign(common.StrToName(name), types.ActionType(0), common.BytesToPubKey(pubkey)) == nil + return accountDB.IsValidSign(common.StrToName(name), common.BytesToPubKey(pubkey)) == nil } +// Genesis dpos genesis store func Genesis(cfg *Config, state *state.StateDB, height uint64) error { db := &LDB{ IDatabase: &stateDB{ @@ -100,7 +107,7 @@ func Genesis(cfg *Config, state *state.StateDB, height uint64) error { state: state, }, } - if err := db.SetProducer(&producerInfo{ + if err := db.SetCadidate(&cadidateInfo{ Name: cfg.SystemName, URL: cfg.SystemURL, Quantity: big.NewInt(0), @@ -110,21 +117,21 @@ func Genesis(cfg *Config, state *state.StateDB, height uint64) error { return err } - activatedProducerSchedule := []string{} - for i := uint64(0); i < cfg.ProducerScheduleSize; i++ { - activatedProducerSchedule = append(activatedProducerSchedule, cfg.SystemName) + activatedCadidateSchedule := []string{} + for i := uint64(0); i < cfg.CadidateScheduleSize; i++ { + activatedCadidateSchedule = append(activatedCadidateSchedule, cfg.SystemName) } if err := db.SetState(&globalState{ Height: height, ActivatedTotalQuantity: big.NewInt(0), - ActivatedProducerSchedule: activatedProducerSchedule, + ActivatedCadidateSchedule: activatedCadidateSchedule, }); err != nil { return err } if err := db.SetState(&globalState{ Height: height + 1, ActivatedTotalQuantity: big.NewInt(0), - ActivatedProducerSchedule: activatedProducerSchedule, + ActivatedCadidateSchedule: activatedCadidateSchedule, }); err != nil { return err } @@ -143,11 +150,7 @@ type Dpos struct { config *Config // cache - proposedIrreversibleNum uint64 - bftIrreversibleNum uint64 - producerIrreversibleNum map[string]uint64 - - firtEpcho uint64 + bftIrreversibles *lru.Cache } // New creates a DPOS consensus engine @@ -155,21 +158,10 @@ func New(config *Config, chain consensus.IChainReader) *Dpos { dpos := &Dpos{ config: config, } - if chain != nil { - dpos.init(chain) - } + dpos.bftIrreversibles, _ = lru.New(int(config.CadidateScheduleSize)) return dpos } -func (dpos *Dpos) init(chain consensus.IChainReader) { - fheader := chain.GetHeaderByNumber(1) - if fheader == nil { - return - } - dpos.firtEpcho = dpos.config.epoch(fheader.Time.Uint64()) - dpos.calcProposedIrreversible(chain) -} - // SetConfig set dpos config func (dpos *Dpos) SetConfig(config *Config) { dpos.rw.Lock() @@ -221,27 +213,40 @@ func (dpos *Dpos) Finalize(chain consensus.IChainReader, header *types.Header, t }, } - if dpos.firtEpcho == 0 { - if fheader := chain.GetHeaderByNumber(1); fheader != nil { - dpos.firtEpcho = dpos.config.epoch(fheader.Time.Uint64()) - } - } - counter := int64(0) - if dpos.IsFirst(header.Time.Uint64()) { - timestamp := header.Time.Uint64() - dpos.config.blockInterval()*dpos.config.BlockFrequency + parentEpoch := dpos.config.epoch(parent.Time.Uint64()) + currentEpoch := dpos.config.epoch(header.Time.Uint64()) + if parentEpoch != currentEpoch { tparent := parent - for tparent.Number.Uint64() > 0 && tparent.Time.Uint64() >= timestamp { + for tparent.Number.Uint64() > 1 && dpos.config.epoch(tparent.Number.Uint64()) == dpos.config.epoch(parent.Time.Uint64()) { counter++ tparent = chain.GetHeaderByHash(tparent.ParentHash) } + // next epoch + sys.updateElectedCadidates(header.Time.Uint64()) + } + + if parent.Number.Uint64() > 0 && (dpos.CalcProposedIrreversible(chain, parent, true) == 0 || header.Time.Uint64()-parent.Time.Uint64() > 2*dpos.config.epochInterval()) { + if systemio := strings.Compare(header.Coinbase.String(), dpos.config.SystemName) == 0; systemio { + latest, err := sys.GetState(header.Number.Uint64()) + if err != nil { + return nil, err + } + latest.TakeOver = true + if err := sys.SetState(latest); err != nil { + return nil, err + } + } } - parent_epoch := dpos.config.epoch(parent.Time.Uint64()) - current_epoch := dpos.config.epoch(header.Time.Uint64()) - if parent_epoch != current_epoch { - // next epoch - sys.updateElectedProducers(header.Time.Uint64()) + cadidate, err := sys.GetCadidate(header.Coinbase.String()) + if err != nil { + return nil, err + } else if cadidate != nil { + cadidate.Counter++ + if err := sys.SetCadidate(cadidate); err != nil { + return nil, err + } } extraReward := new(big.Int).Mul(dpos.config.extraBlockReward(), big.NewInt(counter)) @@ -260,7 +265,10 @@ func (dpos *Dpos) Finalize(chain consensus.IChainReader, header *types.Header, t // update state root at the end blk.Head.Root = state.IntermediateRoot() - + if strings.Compare(header.Coinbase.String(), dpos.config.SystemName) == 0 { + dpos.bftIrreversibles.Purge() + } + dpos.bftIrreversibles.Add(header.Coinbase, header.ProposedIrreversible) return blk, nil } @@ -281,8 +289,8 @@ func (dpos *Dpos) Seal(chain consensus.IChainReader, block *types.Block, stop <- if err != nil { return nil, err } + copy(header.Extra[len(header.Extra)-extraSeal:], sighash) - dpos.calcProposedIrreversible(chain) return block.WithSeal(header), nil } @@ -307,11 +315,11 @@ func (dpos *Dpos) VerifySeal(chain consensus.IChainReader, header *types.Header) return err } - if err := dpos.IsValidateProducer(chain, header.Number.Uint64()-1, header.Time.Uint64(), proudcer, [][]byte{pubkey}, state); err != nil { + if err := dpos.IsValidateCadidate(chain, parent, header.Time.Uint64(), proudcer, [][]byte{pubkey}, state, true); err != nil { return err } - return dpos.calcProposedIrreversible(chain) + return nil } // CalcDifficulty is the difficulty adjustment algorithm. @@ -326,8 +334,8 @@ func (dpos *Dpos) CalcDifficulty(chain consensus.IChainReader, time uint64, pare return big.NewInt((int64(time)-timeOfGenesisBlock)/int64(dpos.config.blockInterval()) + 1) } -//IsValidateProducer current producer -func (dpos *Dpos) IsValidateProducer(chain consensus.IChainReader, height uint64, timestamp uint64, producer string, pubkeys [][]byte, state *state.StateDB) error { +//IsValidateCadidate current cadidate +func (dpos *Dpos) IsValidateCadidate(chain consensus.IChainReader, parent *types.Header, timestamp uint64, cadidate string, pubkeys [][]byte, state *state.StateDB, force bool) error { if timestamp%dpos.BlockInterval() != 0 { return errInvalidMintBlockTime } @@ -337,38 +345,20 @@ func (dpos *Dpos) IsValidateProducer(chain consensus.IChainReader, height uint64 state: state, } - if !common.IsValidName(producer) { - return ErrIllegalProducerName + if !common.IsValidAccountName(cadidate) { + return ErrIllegalCadidateName } has := false for _, pubkey := range pubkeys { - if db.IsValidSign(producer, pubkey) { + if db.IsValidSign(cadidate, pubkey) { has = true } } if !has { - return ErrIllegalProducerPubKey + return ErrIllegalCadidatePubKey } - target_ts := big.NewInt(int64(timestamp - dpos.config.DelayEcho*dpos.config.epochInterval())) - // find target block - var pheader *types.Header - for height > 0 { - pheader = chain.GetHeaderByNumber(height) - if pheader.Time.Cmp(target_ts) != 1 { - break - } else { - height -= 1 - } - } - - // if height > dpos.config.DelayEcho { - // height = height - dpos.config.DelayEcho - // } else { - // height = uint64(0) - // } - sys := &System{ config: dpos.config, IDB: &LDB{ @@ -376,13 +366,41 @@ func (dpos *Dpos) IsValidateProducer(chain consensus.IChainReader, height uint64 }, } - gstate, err := sys.GetState(height) + latest, err := sys.GetState(parent.Number.Uint64()) + if err != nil { + return err + } + + systemio := strings.Compare(cadidate, dpos.config.SystemName) == 0 + if latest.TakeOver { + if force && systemio { + return nil + } + return ErrSystemTakeOver + } else if parent.Number.Uint64() > 0 && (dpos.CalcProposedIrreversible(chain, parent, true) == 0 || timestamp-parent.Time.Uint64() > 2*dpos.config.epochInterval()) { + if force && systemio { + // first take over + return nil + } + return ErrTooMuchRreversible + } + + targetTime := big.NewInt(int64(timestamp - dpos.config.DelayEcho*dpos.config.epochInterval())) + // find target block + for parent.Number.Uint64() > 0 { + if parent.Time.Cmp(targetTime) == -1 { + break + } + parent = chain.GetHeaderByHash(parent.ParentHash) + } + + gstate, err := sys.GetState(parent.Number.Uint64() + 1) if err != nil { return err } offset := dpos.config.getoffset(timestamp) - if gstate == nil || offset >= uint64(len(gstate.ActivatedProducerSchedule)) || strings.Compare(gstate.ActivatedProducerSchedule[offset], producer) != 0 { - return fmt.Errorf("%v %v, except %v index %v (%v) ", errInvalidBlockProducer, producer, gstate.ActivatedProducerSchedule, offset, timestamp/dpos.config.epochInterval()) + if gstate == nil || offset >= uint64(len(gstate.ActivatedCadidateSchedule)) || strings.Compare(gstate.ActivatedCadidateSchedule[offset], cadidate) != 0 { + return fmt.Errorf("%v %v, except %v index %v (%v) ", errInvalidBlockCadidate, cadidate, gstate.ActivatedCadidateSchedule, offset, timestamp/dpos.config.epochInterval()) } return nil } @@ -392,23 +410,41 @@ func (dpos *Dpos) BlockInterval() uint64 { return dpos.config.blockInterval() } +// Slot func (dpos *Dpos) Slot(timestamp uint64) uint64 { return dpos.config.slot(timestamp) } +// IsFirst the first of cadidate func (dpos *Dpos) IsFirst(timestamp uint64) bool { return timestamp%dpos.config.epochInterval()%(dpos.config.blockInterval()*dpos.config.BlockFrequency) == 0 } +func (dpos *Dpos) GetDelegatedByTime(name string, timestamp uint64, state *state.StateDB) (*big.Int, *big.Int, uint64, error) { + sys := &System{ + config: dpos.config, + IDB: &LDB{ + IDatabase: &stateDB{ + name: dpos.config.AccountName, + state: state, + }, + }, + } + return sys.GetDelegatedByTime(name, timestamp) +} + // Engine an engine func (dpos *Dpos) Engine() consensus.IEngine { return dpos } -func (dpos *Dpos) calcLastIrreversible() uint64 { +func (dpos *Dpos) CalcBFTIrreversible() uint64 { irreversibles := UInt64Slice{} - for _, irreversible := range dpos.producerIrreversibleNum { - irreversibles = append(irreversibles, irreversible) + keys := dpos.bftIrreversibles.Keys() + for _, key := range keys { + if irreversible, ok := dpos.bftIrreversibles.Get(key); ok { + irreversibles = append(irreversibles, irreversible.(uint64)) + } } if len(irreversibles) == 0 { @@ -421,30 +457,27 @@ func (dpos *Dpos) calcLastIrreversible() uint64 { return irreversibles[(len(irreversibles)-1)/3] } -func (dpos *Dpos) calcProposedIrreversible(chain consensus.IChainReader) error { +func (dpos *Dpos) CalcProposedIrreversible(chain consensus.IChainReader, parent *types.Header, strict bool) uint64 { curHeader := chain.CurrentHeader() - - producerMap := make(map[string]uint64) - for curHeader.Number.Uint64() > dpos.proposedIrreversibleNum { - if curHeader.Number.Uint64()-dpos.proposedIrreversibleNum+uint64(len(producerMap)) < dpos.config.consensusSize() { - return nil + if parent != nil { + curHeader = chain.GetHeaderByHash(parent.Hash()) + } + cadidateMap := make(map[string]uint64) + timestamp := curHeader.Time.Uint64() + for curHeader.Number.Uint64() > 0 { + if strings.Compare(curHeader.Coinbase.String(), dpos.config.SystemName) == 0 { + return curHeader.Number.Uint64() } - epoch := dpos.config.epoch(curHeader.Time.Uint64()) - if e, ok := producerMap[curHeader.Coinbase.String()]; e != epoch && ok { - return nil + if strict && timestamp-curHeader.Time.Uint64() >= 2*dpos.config.epochInterval() { + break } - producerMap[curHeader.Coinbase.String()] = epoch - - if uint64(len(producerMap)) >= dpos.config.consensusSize() { - dpos.proposedIrreversibleNum = curHeader.Number.Uint64() - return nil + cadidateMap[curHeader.Coinbase.String()]++ + if uint64(len(cadidateMap)) >= dpos.config.consensusSize() { + return curHeader.Number.Uint64() } curHeader = chain.GetHeaderByHash(curHeader.ParentHash) - if curHeader == nil { - return nil - } } - return nil + return 0 } func ecrecover(header *types.Header, extra []byte) ([]byte, error) { @@ -461,24 +494,9 @@ func ecrecover(header *types.Header, extra []byte) ([]byte, error) { } func signHash(header *types.Header, extra []byte) (hash common.Hash) { - hasher := sha3.NewLegacyKeccak256() - rlp.Encode(hasher, []interface{}{ - header.ParentHash, - header.Coinbase, - header.Root, - header.TxsRoot, - header.ReceiptsRoot, - header.Bloom, - header.Difficulty, - header.Number, - header.GasLimit, - header.GasUsed, - header.Time, - header.Extra[:len(header.Extra)-extraSeal], - extra, - }) - hasher.Sum(hash[:0]) - return hash + theader := types.CopyHeader(header) + theader.Extra = theader.Extra[:len(theader.Extra)-extraSeal] + return theader.Hash() } // UInt64Slice attaches the methods of sort.Interface to []uint64, sorting in increasing order. diff --git a/consensus/dpos/ldb.go b/consensus/dpos/ldb.go index 933f736f..79e03bab 100644 --- a/consensus/dpos/ldb.go +++ b/consensus/dpos/ldb.go @@ -35,26 +35,28 @@ type IDatabase interface { Delegate(string, *big.Int) error Undelegate(string, *big.Int) error IncAsset2Acct(string, string, *big.Int) error + + GetSnapshot(key string, timestamp uint64) ([]byte, error) } var ( - // ProducerKeyPrefix producer name --> producerInfo - ProducerKeyPrefix = "prod" + // CadidateKeyPrefix cadidate name --> cadidateInfo + CadidateKeyPrefix = "prod" // VoterKeyPrefix voter name ---> voterInfo VoterKeyPrefix = "vote" - // // DelegatorKeyPrfix producer name ----> voter names + // // DelegatorKeyPrfix cadidate name ----> voter names // DelegatorKeyPrfix = "dele" - // ProducersKeyPrefix produces ----> producer names - ProducersKeyPrefix = "prods" + // CadidatesKeyPrefix produces ----> cadidate names + CadidatesKeyPrefix = "prods" // StateKeyPrefix height --> globalState StateKeyPrefix = "state" // Separator Split characters Separator = "_" - // ProducersKey producers - ProducersKey = "prods" - // ProducersSizeKey producers size - ProducersSizeKey = "prodsize" + // CadidatesKey cadidates + CadidatesKey = "prods" + // CadidatesSizeKey cadidates size + CadidatesSizeKey = "prodsize" // LastestStateKey lastest LastestStateKey = "lastest" ) @@ -73,17 +75,17 @@ func NewLDB(db IDatabase) (*LDB, error) { return ldb, nil } -func (db *LDB) GetProducer(name string) (*producerInfo, error) { - key := strings.Join([]string{ProducerKeyPrefix, name}, Separator) - producerInfo := &producerInfo{} +func (db *LDB) GetCadidate(name string) (*cadidateInfo, error) { + key := strings.Join([]string{CadidateKeyPrefix, name}, Separator) + cadidateInfo := &cadidateInfo{} if val, err := db.Get(key); err != nil { return nil, err } else if val == nil { return nil, nil - } else if err := rlp.DecodeBytes(val, producerInfo); err != nil { + } else if err := rlp.DecodeBytes(val, cadidateInfo); err != nil { return nil, err } - return producerInfo, nil + return cadidateInfo, nil } func (db *LDB) GetVoter(name string) (*voterInfo, error) { @@ -99,8 +101,8 @@ func (db *LDB) GetVoter(name string) (*voterInfo, error) { return voterInfo, nil } -// func (db *LDB) GetDelegators(producer string) ([]string, error) { -// key := strings.Join([]string{DelegatorKeyPrfix, producer}, Separator) +// func (db *LDB) GetDelegators(cadidate string) ([]string, error) { +// key := strings.Join([]string{DelegatorKeyPrfix, cadidate}, Separator) // delegators := []string{} // if val, err := db.Get(key); err != nil { // return nil, err @@ -112,33 +114,33 @@ func (db *LDB) GetVoter(name string) (*voterInfo, error) { // return delegators, nil // } -func (db *LDB) SetProducer(producer *producerInfo) error { - key := strings.Join([]string{ProducerKeyPrefix, producer.Name}, Separator) - if val, err := rlp.EncodeToBytes(producer); err != nil { +func (db *LDB) SetCadidate(cadidate *cadidateInfo) error { + key := strings.Join([]string{CadidateKeyPrefix, cadidate.Name}, Separator) + if val, err := rlp.EncodeToBytes(cadidate); err != nil { return err } else if err := db.Put(key, val); err != nil { return err } - // producers - producers := []string{} - pkey := strings.Join([]string{ProducersKeyPrefix, ProducersKey}, Separator) + // cadidates + cadidates := []string{} + pkey := strings.Join([]string{CadidatesKeyPrefix, CadidatesKey}, Separator) if pval, err := db.Get(pkey); err != nil { return err } else if pval == nil { - } else if err := rlp.DecodeBytes(pval, &producers); err != nil { + } else if err := rlp.DecodeBytes(pval, &cadidates); err != nil { return err } - for _, name := range producers { - if strings.Compare(name, producer.Name) == 0 { + for _, name := range cadidates { + if strings.Compare(name, cadidate.Name) == 0 { return nil } } - producers = append(producers, producer.Name) - npval, err := rlp.EncodeToBytes(producers) + cadidates = append(cadidates, cadidate.Name) + npval, err := rlp.EncodeToBytes(cadidates) if err != nil { return err } @@ -146,8 +148,8 @@ func (db *LDB) SetProducer(producer *producerInfo) error { return err } - skey := strings.Join([]string{ProducersKeyPrefix, ProducersSizeKey}, Separator) - return db.Put(skey, uint64tobytes(uint64(len(producers)))) + skey := strings.Join([]string{CadidatesKeyPrefix, CadidatesSizeKey}, Separator) + return db.Put(skey, uint64tobytes(uint64(len(cadidates)))) } func (db *LDB) SetVoter(voter *voterInfo) error { @@ -160,7 +162,7 @@ func (db *LDB) SetVoter(voter *voterInfo) error { // // delegators // delegators := []string{} - // dkey := strings.Join([]string{DelegatorKeyPrfix, voter.Producer}, Separator) + // dkey := strings.Join([]string{DelegatorKeyPrfix, voter.Cadidate}, Separator) // if dval, err := db.Get(dkey); err != nil { // return err @@ -182,45 +184,45 @@ func (db *LDB) SetVoter(voter *voterInfo) error { return nil } -func (db *LDB) DelProducer(name string) error { - key := strings.Join([]string{ProducerKeyPrefix, name}, Separator) +func (db *LDB) DelCadidate(name string) error { + key := strings.Join([]string{CadidateKeyPrefix, name}, Separator) if err := db.Delete(key); err != nil { return err } - // producers - producers := []string{} - pkey := strings.Join([]string{ProducersKeyPrefix, ProducersKey}, Separator) + // cadidates + cadidates := []string{} + pkey := strings.Join([]string{CadidatesKeyPrefix, CadidatesKey}, Separator) if pval, err := db.Get(pkey); err != nil { return err } else if pval == nil { - } else if err := rlp.DecodeBytes(pval, &producers); err != nil { + } else if err := rlp.DecodeBytes(pval, &cadidates); err != nil { return err } - for index, prod := range producers { + for index, prod := range cadidates { if strings.Compare(prod, name) == 0 { - producers = append(producers[:index], producers[index+1:]...) + cadidates = append(cadidates[:index], cadidates[index+1:]...) break } } - skey := strings.Join([]string{ProducersKeyPrefix, ProducersSizeKey}, Separator) - if err := db.Put(skey, uint64tobytes(uint64(len(producers)))); err != nil { + skey := strings.Join([]string{CadidatesKeyPrefix, CadidatesSizeKey}, Separator) + if err := db.Put(skey, uint64tobytes(uint64(len(cadidates)))); err != nil { return err } - if len(producers) == 0 { + if len(cadidates) == 0 { return db.Delete(pkey) } - npval, err := rlp.EncodeToBytes(producers) + npval, err := rlp.EncodeToBytes(cadidates) if err != nil { return err } return db.Put(pkey, npval) } -func (db *LDB) DelVoter(name string, producer string) error { +func (db *LDB) DelVoter(name string, cadidate string) error { key := strings.Join([]string{VoterKeyPrefix, name}, Separator) if err := db.Delete(key); err != nil { return err @@ -228,7 +230,7 @@ func (db *LDB) DelVoter(name string, producer string) error { // delegators // delegators := []string{} - // dkey := strings.Join([]string{DelegatorKeyPrfix, producer}, Separator) + // dkey := strings.Join([]string{DelegatorKeyPrfix, cadidate}, Separator) // if dval, err := db.Get(dkey); err != nil { // return err // } else if dval == nil { @@ -253,21 +255,21 @@ func (db *LDB) DelVoter(name string, producer string) error { return nil } -func (db *LDB) Producers() ([]*producerInfo, error) { - // producers - pkey := strings.Join([]string{ProducersKeyPrefix, ProducersKey}, Separator) - producers := []string{} +func (db *LDB) Cadidates() ([]*cadidateInfo, error) { + // cadidates + pkey := strings.Join([]string{CadidatesKeyPrefix, CadidatesKey}, Separator) + cadidates := []string{} if pval, err := db.Get(pkey); err != nil { return nil, err } else if pval == nil { return nil, nil - } else if err := rlp.DecodeBytes(pval, &producers); err != nil { + } else if err := rlp.DecodeBytes(pval, &cadidates); err != nil { return nil, err } - prods := producerInfoArray{} - for _, producer := range producers { - prod, err := db.GetProducer(producer) + prods := cadidateInfoArray{} + for _, cadidate := range cadidates { + prod, err := db.GetCadidate(cadidate) if err != nil { return nil, err } @@ -277,9 +279,9 @@ func (db *LDB) Producers() ([]*producerInfo, error) { return prods, nil } -func (db *LDB) ProducersSize() (uint64, error) { +func (db *LDB) CadidatesSize() (uint64, error) { size := uint64(0) - skey := strings.Join([]string{ProducersKeyPrefix, ProducersSizeKey}, Separator) + skey := strings.Join([]string{CadidatesKeyPrefix, CadidatesSizeKey}, Separator) if sval, err := db.Get(skey); err != nil { return 0, err } else if sval != nil { @@ -340,6 +342,35 @@ func (db *LDB) DelState(height uint64) error { return db.Delete(key) } +func (db *LDB) GetDelegatedByTime(name string, timestamp uint64) (*big.Int, *big.Int, uint64, error) { + key := strings.Join([]string{CadidateKeyPrefix, name}, Separator) + val, err := db.GetSnapshot(key, timestamp) + if err != nil { + return big.NewInt(0), big.NewInt(0), 0, err + } + if val != nil { + cadidateInfo := &cadidateInfo{} + if err := rlp.DecodeBytes(val, cadidateInfo); err != nil { + return big.NewInt(0), big.NewInt(0), 0, err + } + return cadidateInfo.Quantity, cadidateInfo.TotalQuantity, cadidateInfo.Counter, nil + } + + key = strings.Join([]string{VoterKeyPrefix, name}, Separator) + val, err = db.GetSnapshot(key, timestamp) + if err != nil { + return big.NewInt(0), big.NewInt(0), 0, err + } + if val != nil { + voterInfo := &voterInfo{} + if err := rlp.DecodeBytes(val, voterInfo); err != nil { + return big.NewInt(0), big.NewInt(0), 0, err + } + return voterInfo.Quantity, big.NewInt(0), 0, nil + } + return big.NewInt(0), big.NewInt(0), 0, nil +} + func (db *LDB) lastestHeight() (uint64, error) { lkey := strings.Join([]string{StateKeyPrefix, LastestStateKey}, Separator) if val, err := db.Get(lkey); err != nil { diff --git a/consensus/dpos/ldb_test.go b/consensus/dpos/ldb_test.go index 5e7bd082..9a7a1604 100644 --- a/consensus/dpos/ldb_test.go +++ b/consensus/dpos/ldb_test.go @@ -63,7 +63,9 @@ func (ldb *levelDB) Undelegate(string, *big.Int) error { func (ldb *levelDB) IncAsset2Acct(string, string, *big.Int) error { return nil } - +func (ldb *levelDB) GetSnapshot(string, uint64) ([]byte, error) { + return nil, nil +} func newTestLDB() (*levelDB, func()) { dirname, err := ioutil.TempDir(os.TempDir(), "dpos_test_") if err != nil { @@ -89,7 +91,7 @@ func TestLDBVoter(t *testing.T) { defer function() voter := &voterInfo{ Name: "vos", - Producer: "fos", + Cadidate: "fos", Quantity: big.NewInt(1000), Height: uint64(time.Now().UnixNano()), } @@ -104,17 +106,17 @@ func TestLDBVoter(t *testing.T) { panic(fmt.Errorf("getvoter --- not nil")) } - // if delegators, err := db.GetDelegators(nvoter.Producer); err != nil { + // if delegators, err := db.GetDelegators(nvoter.Cadidate); err != nil { // panic(fmt.Errorf("getdelegators --- %v", err)) // } else if len(delegators) != 1 { // panic(fmt.Errorf("getdelegators --- not mismatch")) // } - if err := db.DelVoter(nvoter.Name, nvoter.Producer); err != nil { + if err := db.DelVoter(nvoter.Name, nvoter.Cadidate); err != nil { panic(fmt.Errorf("delvoter --- %v", err)) } - // if delegators, err := db.GetDelegators(nvoter.Producer); err != nil { + // if delegators, err := db.GetDelegators(nvoter.Cadidate); err != nil { // panic(fmt.Errorf("getdelegators after del --- %v", err)) // } else if len(delegators) != 0 { // t.Log(len(delegators)) @@ -128,50 +130,50 @@ func TestLDBVoter(t *testing.T) { } } -func TestLDBProducer(t *testing.T) { - // SetProducer(*producerInfo) error - // DelProducer(string) error - // GetProducer(string) (*producerInfo, error) - // Producers() ([]*producerInfo, error) - // ProducersSize() (uint64, error) +func TestLDBCadidate(t *testing.T) { + // SetCadidate(*cadidateInfo) error + // DelCadidate(string) error + // GetCadidate(string) (*cadidateInfo, error) + // Cadidates() ([]*cadidateInfo, error) + // CadidatesSize() (uint64, error) ldb, function := newTestLDB() db, _ := NewLDB(ldb) defer function() - prod := &producerInfo{ + prod := &cadidateInfo{ Name: "fos", URL: "www.fractalproject.com", Quantity: big.NewInt(1000), TotalQuantity: big.NewInt(1000), Height: uint64(time.Now().UnixNano()), } - if err := db.SetProducer(prod); err != nil { + if err := db.SetCadidate(prod); err != nil { panic(fmt.Errorf("setprod --- %v", err)) } - nprod, err := db.GetProducer(prod.Name) + nprod, err := db.GetCadidate(prod.Name) if err != nil { panic(fmt.Errorf("getprod --- %v", err)) } else if nprod == nil { panic(fmt.Errorf("getprod --- not nil")) } - if size, err := db.ProducersSize(); err != nil { + if size, err := db.CadidatesSize(); err != nil { panic(fmt.Errorf("prodsize --- %v", err)) } else if size != 1 { panic(fmt.Errorf("prodsize --- mismatch")) } - if err := db.DelProducer(nprod.Name); err != nil { + if err := db.DelCadidate(nprod.Name); err != nil { panic(fmt.Errorf("delprod --- %v", err)) } - if size, err := db.ProducersSize(); err != nil { + if size, err := db.CadidatesSize(); err != nil { panic(fmt.Errorf("prodsize --- %v", err)) } else if size != 0 { panic(fmt.Errorf("prodsize --- mismatch")) } - if nprod, err := db.GetProducer(nprod.Name); err != nil { + if nprod, err := db.GetCadidate(nprod.Name); err != nil { panic(fmt.Errorf("getprod --- %v", err)) } else if nprod != nil { panic(fmt.Errorf("getprod --- should nil")) @@ -187,8 +189,8 @@ func TestLDBState(t *testing.T) { defer function() gstate := &globalState{ Height: 10, - ActivatedProducerScheduleUpdate: uint64(time.Now().UnixNano()), - ActivatedProducerSchedule: []string{}, + ActivatedCadidateScheduleUpdate: uint64(time.Now().UnixNano()), + ActivatedCadidateSchedule: []string{}, ActivatedTotalQuantity: big.NewInt(1000), TotalQuantity: big.NewInt(100000), } diff --git a/consensus/dpos/processor.go b/consensus/dpos/processor.go index 70ef2566..8c1da0ff 100644 --- a/consensus/dpos/processor.go +++ b/consensus/dpos/processor.go @@ -17,9 +17,12 @@ package dpos import ( + "fmt" "math/big" + "strings" "github.com/fractalplatform/fractal/accountmanager" + "github.com/fractalplatform/fractal/common" "github.com/fractalplatform/fractal/utils/rlp" "github.com/fractalplatform/fractal/params" @@ -27,39 +30,41 @@ import ( "github.com/fractalplatform/fractal/types" ) -type RegisterProducer struct { - Url string - Stake *big.Int +type RegisterCadidate struct { + Url string } -type UpdateProducer struct { +type UpdateCadidate struct { Url string Stake *big.Int } -type VoteProducer struct { - Producer string - Stake *big.Int +type VoteCadidate struct { + Cadidate string } -type ChangeProducer struct { - Producer string +type ChangeCadidate struct { + Cadidate string } type RemoveVoter struct { - Voter string + Voters []string +} + +type KickedCadidate struct { + Cadidates []string } -func (dpos *Dpos) ProcessAction(chainCfg *params.ChainConfig, state *state.StateDB, action *types.Action) error { +func (dpos *Dpos) ProcessAction(chainCfg *params.ChainConfig, state *state.StateDB, action *types.Action) ([]*types.InternalAction, error) { snap := state.Snapshot() - err := dpos.processAction(chainCfg, state, action) + internalLogs, err := dpos.processAction(chainCfg, state, action) if err != nil { state.RevertToSnapshot(snap) } - return err + return internalLogs, err } -func (dpos *Dpos) processAction(chainCfg *params.ChainConfig, state *state.StateDB, action *types.Action) error { +func (dpos *Dpos) processAction(chainCfg *params.ChainConfig, state *state.StateDB, action *types.Action) ([]*types.InternalAction, error) { sys := &System{ config: dpos.config, IDB: &LDB{ @@ -70,64 +75,120 @@ func (dpos *Dpos) processAction(chainCfg *params.ChainConfig, state *state.State }, }, } + + var internalActions []*types.InternalAction + + if !action.CheckValue() { + return nil, accountmanager.ErrAmountValueInvalid + } + + if action.AssetID() != chainCfg.SysTokenID { + return nil, accountmanager.ErrAssetIDInvalid + } + + if strings.Compare(action.Recipient().String(), dpos.config.AccountName) != 0 { + return nil, accountmanager.ErrInvalidReceiptAsset + } + + if action.Value().Cmp(big.NewInt(0)) > 0 { + accountDB, err := accountmanager.NewAccountManager(state) + if err != nil { + return nil, err + } + if err := accountDB.TransferAsset(action.Sender(), action.Recipient(), action.AssetID(), action.Value()); err != nil { + return nil, err + } + } + switch action.Type() { - case types.RegProducer: - arg := &RegisterProducer{} + case types.RegCadidate: + arg := &RegisterCadidate{} if err := rlp.DecodeBytes(action.Data(), &arg); err != nil { - return err + return nil, err } - if err := sys.RegProducer(action.Sender().String(), arg.Url, arg.Stake); err != nil { - return err + if err := sys.RegCadidate(action.Sender().String(), arg.Url, action.Value()); err != nil { + return nil, err } - case types.UpdateProducer: - arg := &UpdateProducer{} + case types.UpdateCadidate: + arg := &UpdateCadidate{} if err := rlp.DecodeBytes(action.Data(), &arg); err != nil { - return err + return nil, err + } + if arg.Stake.Sign() == 1 { + return nil, fmt.Errorf("stake cannot be greater zero") } - if err := sys.UpdateProducer(action.Sender().String(), arg.Url, arg.Stake); err != nil { - return err + if action.Value().Sign() == 1 && arg.Stake.Sign() == -1 { + return nil, fmt.Errorf("value & stake cannot allowed at the same time") } - case types.UnregProducer: - if err := sys.UnregProducer(action.Sender().String()); err != nil { - return err + if err := sys.UpdateCadidate(action.Sender().String(), arg.Url, new(big.Int).Add(action.Value(), arg.Stake)); err != nil { + return nil, err } + case types.UnregCadidate: + stake, err := sys.UnregCadidate(action.Sender().String()) + if err != nil { + return nil, err + } + actionX := types.NewAction(action.Type(), action.Recipient(), action.Sender(), 0, 0, action.AssetID(), stake, nil) + internalAction := &types.InternalAction{Action: actionX.NewRPCAction(0), ActionType: "", GasUsed: 0, GasLimit: 0, Depth: 0, Error: ""} + internalActions = append(internalActions, internalAction) case types.RemoveVoter: arg := &RemoveVoter{} if err := rlp.DecodeBytes(action.Data(), &arg); err != nil { - return err + return nil, err + } + for _, voter := range arg.Voters { + stake, err := sys.UnvoteVoter(action.Sender().String(), voter) + if err != nil { + return nil, err + } + actionX := types.NewAction(action.Type(), action.Recipient(), common.Name(voter), 0, 0, action.AssetID(), stake, nil) + internalAction := &types.InternalAction{Action: actionX.NewRPCAction(0), ActionType: "", GasUsed: 0, GasLimit: 0, Depth: 0, Error: ""} + internalActions = append(internalActions, internalAction) + } + case types.VoteCadidate: + arg := &VoteCadidate{} + if err := rlp.DecodeBytes(action.Data(), &arg); err != nil { + return nil, err } - if err := sys.UnvoteVoter(action.Sender().String(), arg.Voter); err != nil { - return err + if err := sys.VoteCadidate(action.Sender().String(), arg.Cadidate, action.Value()); err != nil { + return nil, err } - case types.VoteProducer: - arg := &VoteProducer{} + case types.ChangeCadidate: + arg := &ChangeCadidate{} if err := rlp.DecodeBytes(action.Data(), &arg); err != nil { - return err + return nil, err + } + if err := sys.ChangeCadidate(action.Sender().String(), arg.Cadidate); err != nil { + return nil, err } - if err := sys.VoteProducer(action.Sender().String(), arg.Producer, arg.Stake); err != nil { - return err + case types.UnvoteCadidate: + stake, err := sys.UnvoteCadidate(action.Sender().String()) + if err != nil { + return nil, err } - case types.ChangeProducer: - arg := &ChangeProducer{} + actionX := types.NewAction(action.Type(), action.Recipient(), action.Sender(), 0, 0, action.AssetID(), stake, nil) + internalAction := &types.InternalAction{Action: actionX.NewRPCAction(0), ActionType: "", GasUsed: 0, GasLimit: 0, Depth: 0, Error: ""} + internalActions = append(internalActions, internalAction) + case types.KickedCadidate: + if strings.Compare(action.Sender().String(), dpos.config.SystemName) != 0 { + return nil, fmt.Errorf("no permission for kicking cadidates") + } + arg := &KickedCadidate{} if err := rlp.DecodeBytes(action.Data(), &arg); err != nil { - return err + return nil, err } - if err := sys.ChangeProducer(action.Sender().String(), arg.Producer); err != nil { - return err + for _, cadicate := range arg.Cadidates { + if err := sys.KickedCadidate(cadicate); err != nil { + return nil, err + } } - case types.UnvoteProducer: - if err := sys.UnvoteProducer(action.Sender().String()); err != nil { - return err + case types.ExitTakeOver: + if strings.Compare(action.Sender().String(), dpos.config.SystemName) != 0 { + return nil, fmt.Errorf("no permission for exit take over") } + sys.ExitTakeOver() default: - return accountmanager.ErrUnkownTxType - } - accountDB, err := accountmanager.NewAccountManager(state) - if err != nil { - return err - } - if action.Value().Cmp(big.NewInt(0)) > 0 { - accountDB.TransferAsset(action.Sender(), action.Recipient(), action.AssetID(), action.Value()) + return nil, accountmanager.ErrUnkownTxType } - return nil + return internalActions, nil } diff --git a/consensus/dpos/vote.go b/consensus/dpos/vote.go index eac7a4fd..9af1858d 100644 --- a/consensus/dpos/vote.go +++ b/consensus/dpos/vote.go @@ -21,6 +21,8 @@ import ( "math/big" "math/rand" "strings" + + "github.com/fractalplatform/fractal/state" ) type System struct { @@ -28,8 +30,20 @@ type System struct { IDB } -// RegProducer register a producer -func (sys *System) RegProducer(producer string, url string, stake *big.Int) error { +func NewSystem(state *state.StateDB, config *Config) *System { + return &System{ + config: config, + IDB: &LDB{ + IDatabase: &stateDB{ + name: config.AccountName, + state: state, + }, + }, + } +} + +// RegCadidate register a cadidate +func (sys *System) RegCadidate(cadidate string, url string, stake *big.Int) error { // parameter validity if uint64(len(url)) > sys.config.MaxURLLen { return fmt.Errorf("invalid url %v(too long, max %v)", url, sys.config.MaxURLLen) @@ -39,24 +53,24 @@ func (sys *System) RegProducer(producer string, url string, stake *big.Int) erro if m.Sign() != 0 { return fmt.Errorf("invalid stake %v(non divisibility, unit %v)", stake, sys.config.unitStake()) } - if q.Cmp(sys.config.ProducerMinQuantity) < 0 { - return fmt.Errorf("invalid stake %v(insufficient, producer min %v)", stake, new(big.Int).Mul(sys.config.ProducerMinQuantity, sys.config.unitStake())) + if q.Cmp(sys.config.CadidateMinQuantity) < 0 { + return fmt.Errorf("invalid stake %v(insufficient, cadidate min %v)", stake, new(big.Int).Mul(sys.config.CadidateMinQuantity, sys.config.unitStake())) } - if voter, err := sys.GetVoter(producer); err != nil { + if voter, err := sys.GetVoter(cadidate); err != nil { return err } else if voter != nil { - return fmt.Errorf("invalid producer %v(alreay vote to %v)", producer, voter.Producer) + return fmt.Errorf("invalid cadidate %v(alreay vote to %v)", cadidate, voter.Cadidate) } - prod, err := sys.GetProducer(producer) + prod, err := sys.GetCadidate(cadidate) if err != nil { return err } if prod != nil { - return fmt.Errorf("invalid producer %v(already exist)", producer) + return fmt.Errorf("invalid cadidate %v(already exist)", cadidate) } - prod = &producerInfo{ - Name: producer, + prod = &cadidateInfo{ + Name: cadidate, Quantity: big.NewInt(0), TotalQuantity: big.NewInt(0), } @@ -66,7 +80,7 @@ func (sys *System) RegProducer(producer string, url string, stake *big.Int) erro } gstate.TotalQuantity = new(big.Int).Add(gstate.TotalQuantity, q) - if err := sys.Delegate(producer, stake); err != nil { + if err := sys.Delegate(cadidate, stake); err != nil { return fmt.Errorf("delegate (%v) failed(%v)", stake, err) } @@ -74,7 +88,7 @@ func (sys *System) RegProducer(producer string, url string, stake *big.Int) erro prod.Quantity = new(big.Int).Add(prod.Quantity, q) prod.TotalQuantity = new(big.Int).Add(prod.TotalQuantity, q) prod.Height = gstate.Height - if err := sys.SetProducer(prod); err != nil { + if err := sys.SetCadidate(prod); err != nil { return err } if err := sys.SetState(gstate); err != nil { @@ -83,8 +97,8 @@ func (sys *System) RegProducer(producer string, url string, stake *big.Int) erro return nil } -// UpdateProducer update a producer -func (sys *System) UpdateProducer(producer string, url string, stake *big.Int) error { +// UpdateCadidate update a cadidate +func (sys *System) UpdateCadidate(cadidate string, url string, stake *big.Int) error { // parameter validity if uint64(len(url)) > sys.config.MaxURLLen { return fmt.Errorf("invalid url %v(too long, max %v)", url, sys.config.MaxURLLen) @@ -94,16 +108,16 @@ func (sys *System) UpdateProducer(producer string, url string, stake *big.Int) e if m.Sign() != 0 { return fmt.Errorf("invalid stake %v(non divisibility, unit %v)", stake, sys.config.unitStake()) } - if q.Cmp(sys.config.ProducerMinQuantity) < 0 { - return fmt.Errorf("invalid stake %v(insufficient, producer min %v)", stake, new(big.Int).Mul(sys.config.ProducerMinQuantity, sys.config.unitStake())) + if q.Cmp(sys.config.CadidateMinQuantity) < 0 { + return fmt.Errorf("invalid stake %v(insufficient, cadidate min %v)", stake, new(big.Int).Mul(sys.config.CadidateMinQuantity, sys.config.unitStake())) } - prod, err := sys.GetProducer(producer) + prod, err := sys.GetCadidate(cadidate) if err != nil { return err } if prod == nil { - return fmt.Errorf("invalid producer %v(not exist)", producer) + return fmt.Errorf("invalid cadidate %v(not exist)", cadidate) } q = new(big.Int).Sub(q, prod.Quantity) @@ -112,7 +126,7 @@ func (sys *System) UpdateProducer(producer string, url string, stake *big.Int) e if err != nil { return err } - if sys.isdpos() && new(big.Int).Add(gstate.TotalQuantity, q).Cmp(sys.config.ActivatedMinQuantity) < 0 { + if sys.isdpos(gstate) && new(big.Int).Add(gstate.TotalQuantity, q).Cmp(sys.config.ActivatedMinQuantity) < 0 { return fmt.Errorf("insufficient actived stake") } gstate.TotalQuantity = new(big.Int).Add(gstate.TotalQuantity, q) @@ -120,11 +134,11 @@ func (sys *System) UpdateProducer(producer string, url string, stake *big.Int) e tstake := new(big.Int).Mul(q, sys.config.unitStake()) if q.Sign() < 0 { tstake = new(big.Int).Abs(tstake) - if err := sys.Undelegate(producer, tstake); err != nil { + if err := sys.Undelegate(cadidate, tstake); err != nil { return fmt.Errorf("undelegate %v failed(%v)", q, err) } } else { - if err := sys.Delegate(producer, tstake); err != nil { + if err := sys.Delegate(cadidate, tstake); err != nil { return fmt.Errorf("delegate (%v) failed(%v)", q, err) } } @@ -134,7 +148,7 @@ func (sys *System) UpdateProducer(producer string, url string, stake *big.Int) e } prod.Quantity = new(big.Int).Add(prod.Quantity, q) prod.TotalQuantity = new(big.Int).Add(prod.TotalQuantity, q) - if err := sys.SetProducer(prod); err != nil { + if err := sys.SetCadidate(prod); err != nil { return err } if err := sys.SetState(gstate); err != nil { @@ -143,64 +157,72 @@ func (sys *System) UpdateProducer(producer string, url string, stake *big.Int) e return nil } -// UnregProducer unregister a producer -func (sys *System) UnregProducer(producer string) error { +// UnregCadidate unregister a cadidate +func (sys *System) UnregCadidate(cadidate string) (*big.Int, error) { // parameter validity // modify or update - prod, err := sys.GetProducer(producer) + var stake *big.Int + prod, err := sys.GetCadidate(cadidate) if err != nil { - return err + return nil, err } if prod != nil { gstate, err := sys.GetState(LastBlockHeight) if err != nil { - return err + return nil, err } - if sys.isdpos() { - if cnt, err := sys.ProducersSize(); err != nil { - return err + if sys.isdpos(gstate) { + if cnt, err := sys.CadidatesSize(); err != nil { + return nil, err } else if uint64(cnt) <= sys.config.consensusSize() { - return fmt.Errorf("insufficient actived producers") + return nil, fmt.Errorf("insufficient actived cadidates") } if new(big.Int).Sub(gstate.TotalQuantity, prod.TotalQuantity).Cmp(sys.config.ActivatedMinQuantity) < 0 { - return fmt.Errorf("insufficient actived stake") + return nil, fmt.Errorf("insufficient actived stake") } } - // voters, err := sys.GetDelegators(producer) + // voters, err := sys.GetDelegators(cadidate) // if err != nil { // return err // } // for _, voter := range voters { - // if err := sys.unvoteProducer(voter); err != nil { + // if err := sys.unvoteCadidate(voter); err != nil { // return err // } // } if prod.TotalQuantity.Cmp(prod.Quantity) > 0 { - return fmt.Errorf("already has voter") + return nil, fmt.Errorf("already has voter") } - stake := new(big.Int).Mul(prod.Quantity, sys.config.unitStake()) - if err := sys.Undelegate(producer, stake); err != nil { - return fmt.Errorf("undelegate %v failed(%v)", stake, err) + stake = new(big.Int).Mul(prod.Quantity, sys.config.unitStake()) + if err := sys.Undelegate(cadidate, stake); err != nil { + return nil, fmt.Errorf("undelegate %v failed(%v)", stake, err) } - if err := sys.DelProducer(prod.Name); err != nil { - return err + if prod.InBlackList { + if err := sys.SetCadidate(prod); err != nil { + return nil, err + } + } else { + if err := sys.DelCadidate(prod.Name); err != nil { + return nil, err + } } + gstate.TotalQuantity = new(big.Int).Sub(gstate.TotalQuantity, prod.Quantity) if err := sys.SetState(gstate); err != nil { - return err + return nil, err } } else { - return fmt.Errorf("invalide producer %v", producer) + return nil, fmt.Errorf("invalide cadidate %v", cadidate) } - return nil + return stake, nil } -// VoteProducer vote a producer -func (sys *System) VoteProducer(voter string, producer string, stake *big.Int) error { +// VoteCadidate vote a cadidate +func (sys *System) VoteCadidate(voter string, cadidate string, stake *big.Int) error { // parameter validity m := big.NewInt(0) q, _ := new(big.Int).DivMod(stake, sys.config.unitStake(), m) @@ -211,22 +233,22 @@ func (sys *System) VoteProducer(voter string, producer string, stake *big.Int) e return fmt.Errorf("invalid stake %v(insufficient, voter min %v)", stake, new(big.Int).Mul(sys.config.VoterMinQuantity, sys.config.unitStake())) } - if prod, err := sys.GetProducer(voter); err != nil { + if prod, err := sys.GetCadidate(voter); err != nil { return err } else if prod != nil { - return fmt.Errorf("invalid vote(alreay is producer)") + return fmt.Errorf("invalid vote(alreay is cadidate)") } if vote, err := sys.GetVoter(voter); err != nil { return err } else if vote != nil { - return fmt.Errorf("invalid vote(already voted to producer %v)", vote.Producer) + return fmt.Errorf("invalid vote(already voted to cadidate %v)", vote.Cadidate) } - prod, err := sys.GetProducer(producer) + prod, err := sys.GetCadidate(cadidate) if err != nil { return err } if prod == nil { - return fmt.Errorf("invalid vote(invalid producers %v)", producer) + return fmt.Errorf("invalid vote(invalid cadidates %v)", cadidate) } // modify or update @@ -242,14 +264,14 @@ func (sys *System) VoteProducer(voter string, producer string, stake *big.Int) e prod.TotalQuantity = new(big.Int).Add(prod.TotalQuantity, q) vote := &voterInfo{ Name: voter, - Producer: producer, + Cadidate: cadidate, Quantity: q, Height: gstate.Height, } if err := sys.SetVoter(vote); err != nil { return err } - if err := sys.SetProducer(prod); err != nil { + if err := sys.SetCadidate(prod); err != nil { return err } if err := sys.SetState(gstate); err != nil { @@ -258,8 +280,8 @@ func (sys *System) VoteProducer(voter string, producer string, stake *big.Int) e return nil } -// ChangeProducer change a producer -func (sys *System) ChangeProducer(voter string, producer string) error { +// ChangeCadidate change a cadidate +func (sys *System) ChangeCadidate(voter string, cadidate string) error { // parameter validity vote, err := sys.GetVoter(voter) if err != nil { @@ -268,99 +290,139 @@ func (sys *System) ChangeProducer(voter string, producer string) error { if vote == nil { return fmt.Errorf("invalid voter %v", voter) } - if strings.Compare(vote.Producer, producer) == 0 { - return fmt.Errorf("same producer") + if strings.Compare(vote.Cadidate, cadidate) == 0 { + return fmt.Errorf("same cadidate") } - prod, err := sys.GetProducer(producer) + prod, err := sys.GetCadidate(cadidate) if err != nil { return err } if prod == nil { - return fmt.Errorf("invalid producer %v", producer) + return fmt.Errorf("invalid cadidate %v", cadidate) } // modify or update - oprod, err := sys.GetProducer(vote.Producer) + oprod, err := sys.GetCadidate(vote.Cadidate) if err != nil { return err } oprod.TotalQuantity = new(big.Int).Sub(oprod.TotalQuantity, vote.Quantity) - if err := sys.SetProducer(oprod); err != nil { + if err := sys.SetCadidate(oprod); err != nil { return err } - if err := sys.DelVoter(vote.Name, vote.Producer); err != nil { + if err := sys.DelVoter(vote.Name, vote.Cadidate); err != nil { return err } - vote.Producer = prod.Name + vote.Cadidate = prod.Name prod.TotalQuantity = new(big.Int).Add(prod.TotalQuantity, vote.Quantity) if err := sys.SetVoter(vote); err != nil { return err } - if err := sys.SetProducer(prod); err != nil { + if err := sys.SetCadidate(prod); err != nil { return err } return nil } -// UnvoteProducer cancel vote -func (sys *System) UnvoteProducer(voter string) error { +// UnvoteCadidate cancel vote +func (sys *System) UnvoteCadidate(voter string) (*big.Int, error) { // parameter validity - return sys.unvoteProducer(voter) + return sys.unvoteCadidate(voter) } // UnvoteVoter cancel voter -func (sys *System) UnvoteVoter(producer string, voter string) error { +func (sys *System) UnvoteVoter(cadidate string, voter string) (*big.Int, error) { // parameter validity vote, err := sys.GetVoter(voter) if err != nil { - return err + return nil, err } if vote == nil { - return fmt.Errorf("invalid voter %v", voter) + return nil, fmt.Errorf("invalid voter %v", voter) + } + if strings.Compare(cadidate, vote.Cadidate) != 0 { + return nil, fmt.Errorf("invalid cadidate %v", cadidate) + } + return sys.unvoteCadidate(voter) +} + +func (sys *System) GetDelegatedByTime(name string, timestamp uint64) (*big.Int, *big.Int, uint64, error) { + q, tq, c, err := sys.IDB.GetDelegatedByTime(name, timestamp) + if err != nil { + return big.NewInt(0), big.NewInt(0), 0, err + } + return new(big.Int).Mul(q, sys.config.unitStake()), new(big.Int).Mul(tq, sys.config.unitStake()), c, nil +} + +func (sys *System) KickedCadidate(cadidate string) error { + prod, err := sys.GetCadidate(cadidate) + if prod != nil { + if err := sys.Undelegate(sys.config.SystemName, new(big.Int).Mul(prod.Quantity, sys.config.unitStake())); err != nil { + return err + } + state, err := sys.GetState(LastBlockHeight) + if err != nil { + return err + } + state.TotalQuantity = new(big.Int).Sub(state.TotalQuantity, prod.Quantity) + prod.TotalQuantity = new(big.Int).Sub(prod.TotalQuantity, prod.Quantity) + prod.Quantity = big.NewInt(0) + prod.InBlackList = true + if err := sys.SetState(state); err != nil { + return err + } + return sys.SetCadidate(prod) } - if strings.Compare(producer, vote.Producer) != 0 { - return fmt.Errorf("invalid producer %v", producer) + return err +} + +func (sys *System) ExitTakeOver() error { + latest, err := sys.GetState(LastBlockHeight) + if latest != nil { + latest.TakeOver = false + return sys.SetState(latest) } - return sys.unvoteProducer(voter) + return err } -func (sys *System) unvoteProducer(voter string) error { +func (sys *System) unvoteCadidate(voter string) (*big.Int, error) { // modify or update + var stake *big.Int vote, err := sys.GetVoter(voter) if err != nil { - return err + return nil, err } if vote == nil { - return fmt.Errorf("invalid voter %v", voter) + return nil, fmt.Errorf("invalid voter %v", voter) } gstate, err := sys.GetState(LastBlockHeight) if err != nil { - return err + return nil, err } - if sys.isdpos() && new(big.Int).Sub(gstate.TotalQuantity, vote.Quantity).Cmp(sys.config.ActivatedMinQuantity) < 0 { - return fmt.Errorf("insufficient actived stake") + if sys.isdpos(gstate) && new(big.Int).Sub(gstate.TotalQuantity, vote.Quantity).Cmp(sys.config.ActivatedMinQuantity) < 0 { + return nil, fmt.Errorf("insufficient actived stake") } - stake := new(big.Int).Mul(vote.Quantity, sys.config.unitStake()) + stake = new(big.Int).Mul(vote.Quantity, sys.config.unitStake()) if err := sys.Undelegate(voter, stake); err != nil { - return fmt.Errorf("undelegate %v failed(%v)", stake, err) + return nil, fmt.Errorf("undelegate %v failed(%v)", stake, err) } gstate.TotalQuantity = new(big.Int).Sub(gstate.TotalQuantity, vote.Quantity) - prod, err := sys.GetProducer(vote.Producer) + prod, err := sys.GetCadidate(vote.Cadidate) if err != nil { - return err + return nil, err } prod.TotalQuantity = new(big.Int).Sub(prod.TotalQuantity, vote.Quantity) - if err := sys.SetProducer(prod); err != nil { - return err + if err := sys.SetCadidate(prod); err != nil { + return nil, err } - if err := sys.DelVoter(vote.Name, vote.Producer); err != nil { - return err + if err := sys.DelVoter(vote.Name, vote.Cadidate); err != nil { + return nil, err } if err := sys.SetState(gstate); err != nil { - return err + return nil, err } - return nil + return stake, nil } func (sys *System) onblock(height uint64) error { @@ -370,70 +432,72 @@ func (sys *System) onblock(height uint64) error { } ngstate := &globalState{ Height: height + 1, - ActivatedProducerSchedule: gstate.ActivatedProducerSchedule, - ActivatedProducerScheduleUpdate: gstate.ActivatedProducerScheduleUpdate, + ActivatedCadidateSchedule: gstate.ActivatedCadidateSchedule, + ActivatedCadidateScheduleUpdate: gstate.ActivatedCadidateScheduleUpdate, ActivatedTotalQuantity: gstate.ActivatedTotalQuantity, TotalQuantity: new(big.Int).SetBytes(gstate.TotalQuantity.Bytes()), + TakeOver: gstate.TakeOver, } sys.SetState(ngstate) return nil } -func (sys *System) updateElectedProducers(timestamp uint64) error { +func (sys *System) updateElectedCadidates(timestamp uint64) error { gstate, err := sys.GetState(LastBlockHeight) if err != nil { return err } - size, _ := sys.ProducersSize() + size, _ := sys.CadidatesSize() if gstate.TotalQuantity.Cmp(sys.config.ActivatedMinQuantity) < 0 || uint64(size) < sys.config.consensusSize() { - activatedProducerSchedule := []string{} + activatedCadidateSchedule := []string{} activeTotalQuantity := big.NewInt(0) - producer, _ := sys.GetProducer(sys.config.SystemName) - for i := uint64(0); i < sys.config.ProducerScheduleSize; i++ { - activatedProducerSchedule = append(activatedProducerSchedule, sys.config.SystemName) - activeTotalQuantity = new(big.Int).Add(activeTotalQuantity, producer.TotalQuantity) + cadidate, _ := sys.GetCadidate(sys.config.SystemName) + for i := uint64(0); i < sys.config.CadidateScheduleSize; i++ { + activatedCadidateSchedule = append(activatedCadidateSchedule, sys.config.SystemName) + activeTotalQuantity = new(big.Int).Add(activeTotalQuantity, cadidate.TotalQuantity) } - gstate.ActivatedProducerSchedule = activatedProducerSchedule - gstate.ActivatedProducerScheduleUpdate = timestamp + gstate.ActivatedCadidateSchedule = activatedCadidateSchedule + gstate.ActivatedCadidateScheduleUpdate = timestamp gstate.ActivatedTotalQuantity = activeTotalQuantity return sys.SetState(gstate) } - producers, err := sys.Producers() + cadidates, err := sys.Cadidates() if err != nil { return err } - activatedProducerSchedule := []string{} + activatedCadidateSchedule := []string{} activeTotalQuantity := big.NewInt(0) - for index, producer := range producers { - if uint64(index) >= sys.config.ProducerScheduleSize { + for _, cadidate := range cadidates { + if cadidate.InBlackList || strings.Compare(cadidate.Name, sys.config.SystemName) == 0 { + continue + } + activatedCadidateSchedule = append(activatedCadidateSchedule, cadidate.Name) + activeTotalQuantity = new(big.Int).Add(activeTotalQuantity, cadidate.TotalQuantity) + if uint64(len(activatedCadidateSchedule)) == sys.config.CadidateScheduleSize { break } - activatedProducerSchedule = append(activatedProducerSchedule, producer.Name) - activeTotalQuantity = new(big.Int).Add(activeTotalQuantity, producer.TotalQuantity) } seed := int64(timestamp) r := rand.New(rand.NewSource(seed)) - for i := len(activatedProducerSchedule) - 1; i > 0; i-- { + for i := len(activatedCadidateSchedule) - 1; i > 0; i-- { j := int(r.Int31n(int32(i + 1))) - activatedProducerSchedule[i], activatedProducerSchedule[j] = activatedProducerSchedule[j], activatedProducerSchedule[i] + activatedCadidateSchedule[i], activatedCadidateSchedule[j] = activatedCadidateSchedule[j], activatedCadidateSchedule[i] } - gstate.ActivatedProducerSchedule = activatedProducerSchedule - gstate.ActivatedProducerScheduleUpdate = timestamp + gstate.ActivatedCadidateSchedule = activatedCadidateSchedule + gstate.ActivatedCadidateScheduleUpdate = timestamp gstate.ActivatedTotalQuantity = activeTotalQuantity return sys.SetState(gstate) } -func (sys *System) isdpos() bool { - if size, _ := sys.ProducersSize(); uint64(size) < sys.config.consensusSize() { - return false - } - gstate, _ := sys.GetState(LastBlockHeight) - if gstate.TotalQuantity.Cmp(sys.config.ActivatedMinQuantity) < 0 { - return false +func (sys *System) isdpos(gstate *globalState) bool { + for _, cadidate := range gstate.ActivatedCadidateSchedule { + if strings.Compare(cadidate, sys.config.SystemName) != 0 { + return true + } } - return true + return false } diff --git a/consensus/dpos/vote_test.go b/consensus/dpos/vote_test.go index 4312eb8d..cad3bd78 100644 --- a/consensus/dpos/vote_test.go +++ b/consensus/dpos/vote_test.go @@ -22,7 +22,7 @@ import ( ) var ( - minproducerstake = big.NewInt(0).Mul(DefaultConfig.unitStake(), DefaultConfig.ProducerMinQuantity) + mincadidatestake = big.NewInt(0).Mul(DefaultConfig.unitStake(), DefaultConfig.CadidateMinQuantity) minvoterstake = big.NewInt(0).Mul(DefaultConfig.unitStake(), DefaultConfig.VoterMinQuantity) big1 = big.NewInt(1) ) @@ -43,157 +43,157 @@ func TestVote(t *testing.T) { ActivatedTotalQuantity: big.NewInt(0), }) - // RegProducer - producer := "testproducer" + // RegCadidate + cadidate := "testcadidate" url := "testurl" - stake := big.NewInt(0).Sub(minproducerstake, big1) + stake := big.NewInt(0).Sub(mincadidatestake, big1) - if err := dpos.UnregProducer(producer); err == nil { - t.Errorf("UnregProducer should failed --- %v", err) + if _, err := dpos.UnregCadidate(cadidate); err == nil { + t.Errorf("UnregCadidate should failed --- %v", err) } - err = dpos.RegProducer(producer, url, stake) + err = dpos.RegCadidate(cadidate, url, stake) if err == nil { - t.Errorf("RegProducer should failed --- %v", err) + t.Errorf("RegCadidate should failed --- %v", err) } - if err := dpos.UnregProducer(producer); err == nil { - t.Errorf("UnregProducer should failed --- %v", err) + if _, err := dpos.UnregCadidate(cadidate); err == nil { + t.Errorf("UnregCadidate should failed --- %v", err) } - err = dpos.RegProducer(producer, url, minproducerstake) + err = dpos.RegCadidate(cadidate, url, mincadidatestake) if nil != err { - t.Errorf("RegProducer failed --- %v", err) + t.Errorf("RegCadidate failed --- %v", err) } - if gstate, err := dpos.GetState(LastBlockHeight); err != nil || gstate.TotalQuantity.Cmp(DefaultConfig.ProducerMinQuantity) != 0 { - t.Errorf("gstate totalQuantity mismatch --- %v(%v, %v)", err, gstate.TotalQuantity, DefaultConfig.ProducerMinQuantity) + if gstate, err := dpos.GetState(LastBlockHeight); err != nil || gstate.TotalQuantity.Cmp(DefaultConfig.CadidateMinQuantity) != 0 { + t.Errorf("gstate totalQuantity mismatch --- %v(%v, %v)", err, gstate.TotalQuantity, DefaultConfig.CadidateMinQuantity) } - // GetProducer - if prod, err := dpos.GetProducer(producer); err != nil { - t.Errorf("GetProducer failed --- %v", err) - } else if prod.Name != producer || prod.URL != url || prod.Quantity.Cmp(DefaultConfig.ProducerMinQuantity) != 0 || prod.Quantity.Cmp(prod.TotalQuantity) != 0 { - t.Errorf("producer info not match") + // GetCadidate + if prod, err := dpos.GetCadidate(cadidate); err != nil { + t.Errorf("GetCadidate failed --- %v", err) + } else if prod.Name != cadidate || prod.URL != url || prod.Quantity.Cmp(DefaultConfig.CadidateMinQuantity) != 0 || prod.Quantity.Cmp(prod.TotalQuantity) != 0 { + t.Errorf("cadidate info not match") } - // Producers - if prods, err := dpos.Producers(); err != nil || len(prods) != 1 { - t.Errorf("producers mismatch") + // Cadidates + if prods, err := dpos.Cadidates(); err != nil || len(prods) != 1 { + t.Errorf("cadidates mismatch") } - // ProducersSize - if size, err := dpos.ProducersSize(); err != nil || size != 1 { - t.Errorf("producers mismatch") + // CadidatesSize + if size, err := dpos.CadidatesSize(); err != nil || size != 1 { + t.Errorf("cadidates mismatch") } - // VoteProducer + // VoteCadidate voter := "testvoter" vstake := big.NewInt(0).Sub(minvoterstake, big1) - err = dpos.VoteProducer(voter, producer, vstake) + err = dpos.VoteCadidate(voter, cadidate, vstake) if err == nil { - t.Errorf("VoteProducer should failed --- %v", err) + t.Errorf("VoteCadidate should failed --- %v", err) } - err = dpos.VoteProducer(voter, producer, minvoterstake) + err = dpos.VoteCadidate(voter, cadidate, minvoterstake) if nil != err { - t.Errorf("VoterProducer failed --- %v", err) + t.Errorf("VoterCadidate failed --- %v", err) } - prod, _ := dpos.GetProducer(producer) + prod, _ := dpos.GetCadidate(cadidate) gstate, _ := dpos.GetState(LastBlockHeight) if prod.TotalQuantity.Cmp(gstate.TotalQuantity) != 0 { t.Errorf("gstate totalQuantity mismatch --- %v(%v, %v)", err, gstate.TotalQuantity, prod.TotalQuantity) } else if new(big.Int).Sub(prod.TotalQuantity, prod.Quantity).Cmp(DefaultConfig.VoterMinQuantity) != 0 { - t.Errorf("producer totalQuantity mismatch --- %v(%v, %v)", err, prod.TotalQuantity, prod.Quantity) + t.Errorf("cadidate totalQuantity mismatch --- %v(%v, %v)", err, prod.TotalQuantity, prod.Quantity) } // GetVoter if vote, err := dpos.GetVoter(voter); err != nil { t.Errorf("GetVoter failed --- %v", err) - } else if vote.Name != voter || vote.Producer != producer || vote.Quantity.Cmp(DefaultConfig.VoterMinQuantity) != 0 { + } else if vote.Name != voter || vote.Cadidate != cadidate || vote.Quantity.Cmp(DefaultConfig.VoterMinQuantity) != 0 { t.Errorf("voter info not match --- %v", err) } - // voter cant reg producer - err = dpos.RegProducer(voter, url, minproducerstake) - if err.Error() != "invalid producer testvoter(alreay vote to testproducer)" { + // voter cant reg cadidate + err = dpos.RegCadidate(voter, url, mincadidatestake) + if err.Error() != "invalid cadidate testvoter(alreay vote to testcadidate)" { t.Errorf("wrong err type --- %v", err) } // test change - producer2 := "testproducer2" + cadidate2 := "testcadidate2" url2 := "testurl2" - dpos.RegProducer(producer2, url2, minproducerstake) - // Producers - if prods, err := dpos.Producers(); err != nil || len(prods) != 2 { - t.Errorf("producers mismatch") + dpos.RegCadidate(cadidate2, url2, mincadidatestake) + // Cadidates + if prods, err := dpos.Cadidates(); err != nil || len(prods) != 2 { + t.Errorf("cadidates mismatch") } - if err := dpos.ChangeProducer(voter, producer2); err != nil { - t.Errorf("ChangeProducer failed --- %v", err) + if err := dpos.ChangeCadidate(voter, cadidate2); err != nil { + t.Errorf("ChangeCadidate failed --- %v", err) } vote, _ := dpos.GetVoter(voter) - prod, _ = dpos.GetProducer(producer) - prod2, _ := dpos.GetProducer(producer2) + prod, _ = dpos.GetCadidate(cadidate) + prod2, _ := dpos.GetCadidate(cadidate2) gstate, _ = dpos.GetState(LastBlockHeight) - if vote.Producer != prod2.Name || vote.Quantity.Cmp(DefaultConfig.VoterMinQuantity) != 0 || + if vote.Cadidate != prod2.Name || vote.Quantity.Cmp(DefaultConfig.VoterMinQuantity) != 0 || prod.Quantity.Cmp(prod.TotalQuantity) != 0 || new(big.Int).Add(prod.TotalQuantity, prod2.TotalQuantity).Cmp(gstate.TotalQuantity) != 0 { t.Log(prod2.TotalQuantity, gstate.TotalQuantity) t.Error("Change stake not work") } - if err := dpos.UnvoteProducer(voter); err != nil { - t.Errorf("UnvoteProducer failed --- %v", err) + if _, err := dpos.UnvoteCadidate(voter); err != nil { + t.Errorf("UnvoteCadidate failed --- %v", err) } else if vote, err := dpos.GetVoter(voter); err != nil || vote != nil { - t.Errorf("UnvoteProducer failed --- %v", err) + t.Errorf("UnvoteCadidate failed --- %v", err) } - prod2, _ = dpos.GetProducer(producer2) + prod2, _ = dpos.GetCadidate(cadidate2) gstate, _ = dpos.GetState(LastBlockHeight) if prod.Quantity.Cmp(prod.TotalQuantity) != 0 || prod2.Quantity.Cmp(prod2.TotalQuantity) != 0 || new(big.Int).Add(prod.TotalQuantity, prod2.TotalQuantity).Cmp(gstate.TotalQuantity) != 0 { - t.Errorf("UnvoteProducer failed") + t.Errorf("UnvoteCadidate failed") } - if err := dpos.UnregProducer(producer); err != nil { - t.Errorf("UnregProducer failed --- %v", err) - } else if prod, err := dpos.GetProducer(producer); err != nil || prod != nil { - t.Errorf("UnregProducer failed --- %v", err) + if _, err := dpos.UnregCadidate(cadidate); err != nil { + t.Errorf("UnregCadidate failed --- %v", err) + } else if prod, err := dpos.GetCadidate(cadidate); err != nil || prod != nil { + t.Errorf("UnregCadidate failed --- %v", err) } else if gstate, _ = dpos.GetState(LastBlockHeight); prod2.TotalQuantity.Cmp(gstate.TotalQuantity) != 0 { - t.Errorf("UnvoteProducer failed mismatch %v %v", prod2.TotalQuantity, gstate.TotalQuantity) + t.Errorf("UnvoteCadidate failed mismatch %v %v", prod2.TotalQuantity, gstate.TotalQuantity) } // activate dpos state DefaultConfig.safeSize.Store(uint64(2)) - pmq2 := big.NewInt(0).Mul(DefaultConfig.ProducerMinQuantity, big.NewInt(2)) + pmq2 := big.NewInt(0).Mul(DefaultConfig.CadidateMinQuantity, big.NewInt(2)) DefaultConfig.ActivatedMinQuantity = big.NewInt(0).Add(pmq2, big1) - err = dpos.RegProducer(producer, url, minproducerstake) + err = dpos.RegCadidate(cadidate, url, mincadidatestake) if err != nil { - t.Errorf("RegProducer err %v", err) + t.Errorf("RegCadidate err %v", err) } // register again - err = dpos.RegProducer(producer, url, minproducerstake) - if err.Error() != "invalid producer testproducer(already exist)" { + err = dpos.RegCadidate(cadidate, url, mincadidatestake) + if err.Error() != "invalid cadidate testcadidate(already exist)" { t.Errorf("wrong err: %v", err) } - err = dpos.VoteProducer(voter, producer, minvoterstake) + err = dpos.VoteCadidate(voter, cadidate, minvoterstake) if nil != err { - t.Errorf("VoterProducer failed --- %v", err) + t.Errorf("VoterCadidate failed --- %v", err) } //t.Log(dpos.isdpos()) - err = dpos.UnregProducer(producer) - if err == nil || err.Error() != "insufficient actived producers" { + _, err = dpos.UnregCadidate(cadidate) + if err.Error() != "already has voter" { t.Errorf("wrong err: %v", err) } - err = dpos.UnvoteProducer(voter) - if err == nil || err.Error() != "insufficient actived stake" { + _, err = dpos.UnvoteCadidate(voter) + if err != nil { t.Errorf("wrong err: %v", err) } } diff --git a/consensus/miner/api.go b/consensus/miner/api.go index 98ae4831..59ce7fc6 100644 --- a/consensus/miner/api.go +++ b/consensus/miner/api.go @@ -28,13 +28,15 @@ type API struct { } func (api *API) Start() bool { - api.miner.Start() - return true + return api.miner.Start(false) +} + +func (api *API) Force() bool { + return api.miner.Start(true) } func (api *API) Stop() bool { - api.miner.Stop() - return true + return api.miner.Stop() } func (api *API) Mining() bool { diff --git a/consensus/miner/miner.go b/consensus/miner/miner.go index c819c1e5..a9f2c2a3 100644 --- a/consensus/miner/miner.go +++ b/consensus/miner/miner.go @@ -85,29 +85,31 @@ func (miner *Miner) update() { } // Start start worker -func (miner *Miner) Start() { +func (miner *Miner) Start(force bool) bool { atomic.StoreInt32(&miner.shouldStart, 1) if atomic.LoadInt32(&miner.canStart) == 0 { log.Error("Network syncing, will start miner afterwards") - return + return false } if !atomic.CompareAndSwapInt32(&miner.mining, 0, 1) { log.Error("miner already started") - return + return false } log.Info("Starting mining operation") - miner.worker.start() + miner.worker.start(force) + return true } // Stop stop worker -func (miner *Miner) Stop() { +func (miner *Miner) Stop() bool { if !atomic.CompareAndSwapInt32(&miner.mining, 1, 0) { log.Error("miner already stopped") - return + return false } log.Info("Stopping mining operation") atomic.StoreInt32(&miner.shouldStart, 0) miner.worker.stop() + return true } // Mining wroker is wroking @@ -135,7 +137,7 @@ func (miner *Miner) SetCoinbase(name string, privKeys []string) error { privs = append(privs, priv) } - if !common.IsValidName(name) { + if !common.IsValidAccountName(name) { return fmt.Errorf("invalid name %v", name) } @@ -146,7 +148,7 @@ func (miner *Miner) SetCoinbase(name string, privKeys []string) error { // SetExtra extra data func (miner *Miner) SetExtra(extra []byte) error { if uint64(len(extra)) > params.MaximumExtraDataSize-65 { - err := fmt.Errorf("Extra exceeds max length. %d > %v", len(extra), params.MaximumExtraDataSize) + err := fmt.Errorf("Extra exceeds max length. %d > %v", len(extra), params.MaximumExtraDataSize-65) log.Warn("SetExtra", "error", err) return err } diff --git a/consensus/miner/worker.go b/consensus/miner/worker.go index 4095712f..f08d65d8 100644 --- a/consensus/miner/worker.go +++ b/consensus/miner/worker.go @@ -62,10 +62,12 @@ type Worker struct { currentWork *Work + wg sync.WaitGroup mining int32 quitWork chan struct{} quitWorkRW sync.RWMutex quit chan struct{} + force bool } func newWorker(consensus consensus.IConsensus) *Worker { @@ -106,6 +108,7 @@ out: case ev := <-txsCh: // Apply transactions to the pending state if we're not mining. if atomic.LoadInt32(&worker.mining) == 0 { + worker.wg.Wait() txs := make(map[common.Name][]*types.Transaction) for _, tx := range ev.Data.([]*types.Transaction) { action := tx.GetActions()[0] @@ -123,15 +126,18 @@ out: } } -func (worker *Worker) start() { +func (worker *Worker) start(force bool) { if !atomic.CompareAndSwapInt32(&worker.mining, 0, 1) { log.Warn("worker already started") return } + worker.force = force go worker.mintLoop() } func (worker *Worker) mintLoop() { + worker.wg.Add(1) + defer worker.wg.Done() dpos, ok := worker.Engine().(*dpos.Dpos) if !ok { panic("only support dpos engine") @@ -142,7 +148,7 @@ func (worker *Worker) mintLoop() { return nil, err } for index, privKey := range worker.privKeys { - if err := accountDB.IsValidSign(common.StrToName(worker.coinbase), types.ActionType(0), common.BytesToPubKey(worker.pubKeys[index])); err == nil { + if err := accountDB.IsValidSign(common.StrToName(worker.coinbase), common.BytesToPubKey(worker.pubKeys[index])); err == nil { return crypto.Sign(content, privKey) } } @@ -187,17 +193,22 @@ func (worker *Worker) mintBlock(timestamp int64, quit chan struct{}) { log.Error("failed to mint block", "timestamp", timestamp, "err", err) return } - if err := cdpos.IsValidateProducer(worker, header.Number.Uint64(), uint64(timestamp), worker.coinbase, worker.pubKeys, state); err != nil { + if err := cdpos.IsValidateCadidate(worker, header, uint64(timestamp), worker.coinbase, worker.pubKeys, state, worker.force); err != nil { switch err { - case dpos.ErrIllegalProducerName: + case dpos.ErrSystemTakeOver: fallthrough - case dpos.ErrIllegalProducerPubKey: + case dpos.ErrTooMuchRreversible: + fallthrough + case dpos.ErrIllegalCadidateName: + fallthrough + case dpos.ErrIllegalCadidatePubKey: log.Error("failed to mint the block", "timestamp", timestamp, "err", err) default: log.Debug("failed to mint the block", "timestamp", timestamp, "err", err) } return } + bstart := time.Now() outer: @@ -209,7 +220,7 @@ outer: } block, err := worker.commitNewWork(timestamp, quit) if err == nil { - log.Info("Mined new block", "producer", block.Coinbase(), "number", block.Number(), "hash", block.Hash().String(), "time", block.Time().Int64(), "txs", len(block.Txs), "gas", block.GasUsed(), "diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(bstart))) + log.Info("Mined new block", "cadidate", block.Coinbase(), "number", block.Number(), "hash", block.Hash().String(), "time", block.Time().Int64(), "txs", len(block.Txs), "gas", block.GasUsed(), "diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(bstart))) break outer } if strings.Contains(err.Error(), "mint") { @@ -282,8 +293,9 @@ func (worker *Worker) commitNewWork(timestamp int64, quit chan struct{}) (*types Time: big.NewInt(timestamp), Difficulty: worker.CalcDifficulty(worker.IConsensus, uint64(timestamp), parent), } - if common.IsValidName(worker.coinbase) { + if common.IsValidAccountName(worker.coinbase) { header.Coinbase = common.StrToName(worker.coinbase) + header.ProposedIrreversible = dpos.CalcProposedIrreversible(worker, parent, false) } state, err := worker.StateAt(parent.Root) if err != nil { @@ -348,7 +360,7 @@ func (worker *Worker) commitNewWork(timestamp int64, quit chan struct{}) (*types if bytes.Compare(block.ParentHash().Bytes(), worker.CurrentHeader().Hash().Bytes()) != 0 { return nil, fmt.Errorf("old parent hash") } - if err := worker.WriteBlockWithState(block, work.currentReceipts, work.currentState); err != nil { + if _, err := worker.WriteBlockWithState(block, work.currentReceipts, work.currentState); err != nil { return nil, fmt.Errorf("writing block to chain, err: %v", err) } @@ -390,6 +402,16 @@ func (worker *Worker) commitTransactions(work *Work, txs *types.TransactionsByPr action := tx.GetActions()[0] + if strings.Compare(work.currentHeader.Coinbase.String(), worker.Config().SysName) != 0 { + switch action.Type() { + case types.KickedCadidate: + fallthrough + case types.ExitTakeOver: + continue + default: + } + } + from := action.Sender() // Start executing the transaction work.currentState.Prepare(tx.Hash(), common.Hash{}, work.currentCnt) @@ -432,7 +454,7 @@ func (worker *Worker) commitTransactions(work *Work, txs *types.TransactionsByPr func (worker *Worker) commitTransaction(work *Work, tx *types.Transaction) ([]*types.Log, error) { snap := work.currentState.Snapshot() var name *common.Name - if common.IsValidName(work.currentHeader.Coinbase.String()) { + if common.IsValidAccountName(work.currentHeader.Coinbase.String()) { name = new(common.Name) *name = common.StrToName(work.currentHeader.Coinbase.String()) } diff --git a/crypto/signature_nocgo.go b/crypto/signature_nocgo.go index f90485fd..ec9567c2 100644 --- a/crypto/signature_nocgo.go +++ b/crypto/signature_nocgo.go @@ -68,7 +68,6 @@ func Sign(hash []byte, prv *ecdsa.PrivateKey) ([]byte, error) { if err != nil { return nil, err } - // Convert to Ethereum signature format with 'recovery id' v at the end. v := sig[0] - 27 copy(sig, sig[1:]) sig[64] = v diff --git a/ftservice/apibackend.go b/ftservice/apibackend.go index 1f10cef1..b22a8294 100644 --- a/ftservice/apibackend.go +++ b/ftservice/apibackend.go @@ -34,7 +34,6 @@ import ( "github.com/fractalplatform/fractal/state" "github.com/fractalplatform/fractal/types" "github.com/fractalplatform/fractal/utils/fdb" - "github.com/fractalplatform/fractal/wallet" ) // APIBackend implements ftserviceapi.Backend for full nodes @@ -114,6 +113,98 @@ func (b *APIBackend) GetReceipts(ctx context.Context, hash common.Hash) ([]*type return nil, nil } +func (b *APIBackend) GetDetailTxsLog(ctx context.Context, hash common.Hash) ([]*types.DetailTx, error) { + if number := rawdb.ReadHeaderNumber(b.ftservice.chainDb, hash); number != nil { + return rawdb.ReadDetailTxs(b.ftservice.chainDb, hash, *number), nil + } + return nil, nil +} + +func (b *APIBackend) GetBlockDetailLog(ctx context.Context, blockNr rpc.BlockNumber) *types.BlockAndResult { + hash := rawdb.ReadCanonicalHash(b.ftservice.chainDb, uint64(blockNr)) + if hash == (common.Hash{}) { + return nil + } + receipts := rawdb.ReadReceipts(b.ftservice.chainDb, hash, uint64(blockNr)) + txDetails := rawdb.ReadDetailTxs(b.ftservice.chainDb, hash, uint64(blockNr)) + return &types.BlockAndResult{ + Receipts: receipts, + DetailTxs: txDetails, + } +} + +func (b *APIBackend) GetTxsByFilter(ctx context.Context, filterFn func(common.Name) bool, blockNr rpc.BlockNumber, lookbackNum uint64) []common.Hash { + lastnum := uint64(blockNr) - lookbackNum + + txHashs := make([]common.Hash, 0) + + for ublocknum := uint64(blockNr); ublocknum > lastnum; ublocknum-- { + + hash := rawdb.ReadCanonicalHash(b.ftservice.chainDb, ublocknum) + if hash == (common.Hash{}) { + continue + } + + blockBody := rawdb.ReadBody(b.ftservice.chainDb, hash, ublocknum) + if blockBody == nil { + continue + } + batch_txs := blockBody.Transactions + + for _, tx := range batch_txs { + for _, act := range tx.GetActions() { + if filterFn(act.Sender()) || filterFn(act.Recipient()) { + txHashs = append(txHashs, tx.Hash()) + break + } + } + } + } + + return txHashs +} + +func (b *APIBackend) GetDetailTxByFilter(ctx context.Context, filterFn func(common.Name) bool, blockNr rpc.BlockNumber, lookbackNum uint64) []*types.DetailTx { + lastnum := uint64(blockNr) - lookbackNum + + txdetails := make([]*types.DetailTx, 0) + + for ublocknum := uint64(blockNr); ublocknum > lastnum; ublocknum-- { + + hash := rawdb.ReadCanonicalHash(b.ftservice.chainDb, ublocknum) + if hash == (common.Hash{}) { + continue + } + + batch_txdetails := rawdb.ReadDetailTxs(b.ftservice.chainDb, hash, ublocknum) + for _, txd := range batch_txdetails { + + new_intxs := make([]*types.DetailAction, 0) + for _, intx := range txd.Actions { + new_inactions := make([]*types.InternalAction, 0) + for _, inlog := range intx.InternalActions { + if filterFn(inlog.Action.From) || filterFn(inlog.Action.To) { + new_inactions = append(new_inactions, inlog) + } + } + if len(new_inactions) > 0 { + new_intxs = append(new_intxs, &types.DetailAction{InternalActions: new_inactions}) + } + } + + if len(new_intxs) > 0 { + txdetails = append(txdetails, &types.DetailTx{TxHash: txd.TxHash, Actions: new_intxs}) + } + } + } + + return txdetails +} + +func (b *APIBackend) GetBadBlocks(ctx context.Context) ([]*types.Block, error) { + return b.ftservice.blockchain.BadBlocks(), nil +} + func (b *APIBackend) GetTd(blockHash common.Hash) *big.Int { return b.ftservice.blockchain.GetTdByHash(blockHash) } @@ -173,8 +264,7 @@ func (b *APIBackend) GetEVM(ctx context.Context, account *accountmanager.Account EgnineContext: b.ftservice.Engine(), } - fromPubkey := common.PubKey{} - context := processor.NewEVMContext(from, fromPubkey, assetID, gasPrice, header, evmcontext, nil) + context := processor.NewEVMContext(from, assetID, gasPrice, header, evmcontext, nil) return vm.NewEVM(context, account, state, b.ChainConfig(), vmCfg), vmError, nil } @@ -183,10 +273,6 @@ func (b *APIBackend) SetGasPrice(gasPrice *big.Int) bool { return true } -func (b *APIBackend) Wallet() *wallet.Wallet { - return b.ftservice.Wallet() -} - func (b *APIBackend) GetAccountManager() (*accountmanager.AccountManager, error) { sdb, err := b.ftservice.blockchain.State() if err != nil { diff --git a/ftservice/config.go b/ftservice/config.go index 2bc958c8..f35b23c8 100644 --- a/ftservice/config.go +++ b/ftservice/config.go @@ -18,7 +18,6 @@ package ftservice import ( "github.com/fractalplatform/fractal/blockchain" - "github.com/fractalplatform/fractal/common" "github.com/fractalplatform/fractal/ftservice/gasprice" "github.com/fractalplatform/fractal/metrics" "github.com/fractalplatform/fractal/txpool" @@ -31,30 +30,29 @@ type Config struct { Genesis *blockchain.Genesis `toml:",omitempty"` // Database options - SkipBcVersionCheck bool `mapstructure:"ftservice-skipvcversioncheck"` - DatabaseHandles int `mapstructure:"ftservice-databasehandles"` - DatabaseCache int `mapstructure:"ftservice-databasecache"` + DatabaseHandles int + DatabaseCache int `mapstructure:"databasecache"` // Transaction pool options - TxPool *txpool.Config + TxPool *txpool.Config `mapstructure:"txpool"` // Gas Price Oracle options - GasPrice gasprice.Config + GasPrice gasprice.Config `mapstructure:"gpo"` // miner - Miner *MinerConfig + Miner *MinerConfig `mapstructure:"miner"` - CoinBase common.Address - MetricsConf *metrics.Config + MetricsConf *metrics.Config `mapstructure:"metrics"` // snapshot - Snapshot bool + Snapshot bool + ContractLogFlag bool `mapstructure:"contractlog"` } // MinerConfig miner config type MinerConfig struct { - Start bool `mapstructure:"miner-start"` - Name string `mapstructure:"miner-name"` - PrivateKeys []string `mapstructure:"miner-private"` - ExtraData string `mapstructure:"miner-extra"` + Start bool `mapstructure:"start"` + Name string `mapstructure:"name"` + PrivateKeys []string `mapstructure:"private"` + ExtraData string `mapstructure:"extra"` } diff --git a/ftservice/ftservice.go b/ftservice/ftservice.go index 43aecdd6..fee280d0 100644 --- a/ftservice/ftservice.go +++ b/ftservice/ftservice.go @@ -17,30 +17,26 @@ package ftservice import ( - "fmt" "math/big" "sync" "github.com/ethereum/go-ethereum/log" - am "github.com/fractalplatform/fractal/accountmanager" "github.com/fractalplatform/fractal/blockchain" "github.com/fractalplatform/fractal/consensus" "github.com/fractalplatform/fractal/consensus/dpos" "github.com/fractalplatform/fractal/consensus/miner" "github.com/fractalplatform/fractal/ftservice/gasprice" - "github.com/fractalplatform/fractal/internal/api" "github.com/fractalplatform/fractal/node" "github.com/fractalplatform/fractal/p2p" adaptor "github.com/fractalplatform/fractal/p2p/protoadaptor" "github.com/fractalplatform/fractal/params" "github.com/fractalplatform/fractal/processor" "github.com/fractalplatform/fractal/processor/vm" - "github.com/fractalplatform/fractal/rawdb" "github.com/fractalplatform/fractal/rpc" + "github.com/fractalplatform/fractal/rpcapi" "github.com/fractalplatform/fractal/state" "github.com/fractalplatform/fractal/txpool" "github.com/fractalplatform/fractal/utils/fdb" - "github.com/fractalplatform/fractal/wallet" ) // FtService implements the fractal service. @@ -51,13 +47,13 @@ type FtService struct { blockchain *blockchain.BlockChain txPool *txpool.TxPool chainDb fdb.Database // Block chain database - wallet *wallet.Wallet engine consensus.IEngine miner *miner.Miner p2pServer *adaptor.ProtoAdaptor gasPrice *big.Int lock sync.RWMutex // Protects the variadic fields (e.g. gas price) APIBackend *APIBackend + snapshot *state.SnapshotSt } // New creates a new ftservice object (including the initialisation of the common ftservice object) @@ -78,47 +74,23 @@ func New(ctx *node.ServiceContext, config *Config) (*FtService, error) { config: config, chainDb: chainDb, chainConfig: chainCfg, - wallet: ctx.Wallet, p2pServer: ctx.P2P, shutdownChan: make(chan bool), } - if !config.SkipBcVersionCheck { - bcVersion := rawdb.ReadDatabaseVersion(chainDb) - if bcVersion != blockchain.BlockChainVersion && bcVersion != 0 { - return nil, fmt.Errorf("Blockchain DB version mismatch (%d / %d)", bcVersion, blockchain.BlockChainVersion) - } - rawdb.WriteDatabaseVersion(chainDb, blockchain.BlockChainVersion) - } - //blockchain - ftservice.blockchain, err = blockchain.NewBlockChain(chainDb, vm.Config{}, ftservice.chainConfig, txpool.SenderCacher) - if err != nil { - return nil, err + vmconfig := vm.Config{ + ContractLogFlag: config.ContractLogFlag, } - ftservice.wallet.SetBlockChain(ftservice.blockchain) - if config.Snapshot { - go state.SnapShotblk(chainDb, 300, 3600) - } - - statedb, err := ftservice.blockchain.State() + ftservice.blockchain, err = blockchain.NewBlockChain(chainDb, vmconfig, ftservice.chainConfig, txpool.SenderCacher) if err != nil { - panic(fmt.Sprintf("state db err %v", err)) - } - accountManager, err := am.NewAccountManager(statedb) - if err != nil { - panic(fmt.Sprintf("genesis accountManager new err: %v", err)) - } - if ok, err := accountManager.AccountIsExist(chainCfg.SysName); !ok { - panic(fmt.Sprintf("system account is not exist %v", err)) + return nil, err } - assetInfo, err := accountManager.GetAssetInfoByName(chainCfg.SysToken) - if err != nil { - panic(fmt.Sprintf("genesis system asset err %v", err)) + ftservice.snapshot = state.NewSnapshot(chainDb, 3600) + if config.Snapshot { + ftservice.snapshot.Start() } - chainCfg.SysTokenID = assetInfo.AssetId - chainCfg.SysTokenDecimals = assetInfo.Decimals // txpool if config.TxPool.Journal != "" { @@ -155,7 +127,7 @@ func New(ctx *node.ServiceContext, config *Config) (*FtService, error) { ftservice.miner.SetCoinbase(config.Miner.Name, config.Miner.PrivateKeys) ftservice.miner.SetExtra([]byte(config.Miner.ExtraData)) if config.Miner.Start { - ftservice.miner.Start() + ftservice.miner.Start(false) } ftservice.APIBackend = &APIBackend{ftservice: ftservice} @@ -167,8 +139,7 @@ func New(ctx *node.ServiceContext, config *Config) (*FtService, error) { // APIs return the collection of RPC services the ftservice package offers. func (fs *FtService) APIs() []rpc.API { - apis := api.GetAPIs(fs.APIBackend) - return apis + return rpcapi.GetAPIs(fs.APIBackend) } // Start implements node.Service, starting all internal goroutines. @@ -179,6 +150,7 @@ func (fs *FtService) Start() error { // Stop implements node.Service, terminating all internal goroutine func (fs *FtService) Stop() error { + fs.snapshot.Stop() fs.blockchain.Stop() fs.txPool.Stop() fs.chainDb.Close() @@ -212,5 +184,4 @@ func (s *FtService) BlockChain() *blockchain.BlockChain { return s.blockchain } func (s *FtService) TxPool() *txpool.TxPool { return s.txPool } func (s *FtService) Engine() consensus.IEngine { return s.engine } func (s *FtService) ChainDb() fdb.Database { return s.chainDb } -func (s *FtService) Wallet() *wallet.Wallet { return s.wallet } func (s *FtService) Protocols() []p2p.Protocol { return nil } diff --git a/ftservice/gasprice/gasprice.go b/ftservice/gasprice/gasprice.go index 0c2d5927..35946842 100644 --- a/ftservice/gasprice/gasprice.go +++ b/ftservice/gasprice/gasprice.go @@ -23,23 +23,23 @@ import ( "sync" "github.com/fractalplatform/fractal/common" - "github.com/fractalplatform/fractal/internal/api" "github.com/fractalplatform/fractal/rpc" + "github.com/fractalplatform/fractal/rpcapi" "github.com/fractalplatform/fractal/types" ) var maxPrice = big.NewInt(500 * 1e9) type Config struct { - Blocks int `mapstructure:"gpo-blocks"` - Percentile int `mapstructure:"gpo-percentile"` + Blocks int `mapstructure:"blocks"` + Percentile int `mapstructure:"percentile"` Default *big.Int } // Oracle recommends gas prices based on the content of recent // blocks. Suitable for both light and full clients. type Oracle struct { - backend api.Backend + backend rpcapi.Backend lastHead common.Hash lastPrice *big.Int cacheLock sync.RWMutex @@ -50,7 +50,7 @@ type Oracle struct { } // NewOracle returns a new oracle. -func NewOracle(backend api.Backend, params Config) *Oracle { +func NewOracle(backend rpcapi.Backend, params Config) *Oracle { blocks := params.Blocks if blocks < 1 { blocks = 1 diff --git a/internal/api/keystore.go b/internal/api/keystore.go deleted file mode 100644 index e5f97d42..00000000 --- a/internal/api/keystore.go +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package api - -import ( - "context" - - "github.com/ethereum/go-ethereum/common/hexutil" - am "github.com/fractalplatform/fractal/accountmanager" - "github.com/fractalplatform/fractal/common" - "github.com/fractalplatform/fractal/crypto" - "github.com/fractalplatform/fractal/types" - "github.com/fractalplatform/fractal/utils/rlp" -) - -type PrivateKeyStoreAPI struct { - b Backend -} - -func NewPrivateKeyStoreAPI(b Backend) *PrivateKeyStoreAPI { - return &PrivateKeyStoreAPI{b} -} - -// NewAccount generates a new key and stores it into the key directory. -func (api *PrivateKeyStoreAPI) NewAccount(ctx context.Context, passphrase string) (map[string]interface{}, error) { - a, err := api.b.Wallet().NewAccount(passphrase) - if err != nil { - return nil, err - } - - key, err := api.b.Wallet().GetPrivateKey(a, passphrase) - if err != nil { - return nil, err - } - - return map[string]interface{}{ - "address": a.Addr, - "path": a.Path, - "publicKey": hexutil.Bytes(crypto.FromECDSAPub(&key.PrivateKey.PublicKey)).String(), - }, nil -} - -// Delete deletes a account by passsphrase. -func (api *PrivateKeyStoreAPI) Delete(ctx context.Context, addr common.Address, passphrase string) error { - a, err := api.b.Wallet().Find(addr) - if err != nil { - return err - } - return api.b.Wallet().Delete(a, passphrase) -} - -// Update changes the passphrase of an existing account. -func (api *PrivateKeyStoreAPI) Update(ctx context.Context, addr common.Address, passphrase, newPassphrase string) error { - a, err := api.b.Wallet().Find(addr) - if err != nil { - return err - } - return api.b.Wallet().Update(a, passphrase, newPassphrase) -} - -// ImportRawKey stores the given key into the key directory, encrypting it with the passphrase. -func (api *PrivateKeyStoreAPI) ImportRawKey(ctx context.Context, privkey string, passphrase string) (map[string]interface{}, error) { - key, err := crypto.HexToECDSA(privkey) - if err != nil { - return nil, err - } - a, err := api.b.Wallet().ImportECDSA(key, passphrase) - if err != nil { - return nil, err - } - return map[string]interface{}{ - "address": a.Addr, - "path": a.Path, - "publicKey": hexutil.Bytes(crypto.FromECDSAPub(&key.PublicKey)), - }, nil -} - -// ExportRawKey export account private key . -func (api *PrivateKeyStoreAPI) ExportRawKey(ctx context.Context, addr common.Address, passphrase string) (hexutil.Bytes, error) { - a, err := api.b.Wallet().Find(addr) - if err != nil { - return nil, err - } - key, err := api.b.Wallet().GetPrivateKey(a, passphrase) - if err != nil { - return nil, err - } - return hexutil.Bytes(crypto.FromECDSA(key.PrivateKey)), nil -} - -// ListAccount returns all key files -func (api *PrivateKeyStoreAPI) ListAccount(ctx context.Context) ([]map[string]interface{}, error) { - accounts := api.b.Wallet().Accounts() - ret := make([]map[string]interface{}, 0) - for _, account := range accounts { - tmpa := map[string]interface{}{ - "address": account.Addr, - "path": account.Path, - "publicKey": account.PublicKey, - } - ret = append(ret, tmpa) - } - return ret, nil -} - -// SignTransaction sign transaction and return raw hex . -func (api *PrivateKeyStoreAPI) SignTransaction(ctx context.Context, addr common.Address, passphrase string, tx *types.Transaction) (hexutil.Bytes, error) { - a, err := api.b.Wallet().Find(addr) - if err != nil { - return nil, err - } - signed, err := api.b.Wallet().SignTxWithPassphrase(a, passphrase, tx, tx.GetActions()[0], api.b.ChainConfig().ChainID) - if err != nil { - return nil, err - } - rawtx, err := rlp.EncodeToBytes(signed) - if err != nil { - return nil, err - } - return hexutil.Bytes(rawtx), nil -} - -// SignData sign data and return raw hex -func (api *PrivateKeyStoreAPI) SignData(ctx context.Context, addr common.Address, passphrase string, data hexutil.Bytes) (hexutil.Bytes, error) { - a, err := api.b.Wallet().NewAccount(passphrase) - if err != nil { - return nil, err - } - key, err := api.b.Wallet().GetPrivateKey(a, passphrase) - if err != nil { - return nil, err - } - - sig, err := crypto.Sign(data[:], key.PrivateKey) - if err != nil { - return nil, err - } - - return hexutil.Bytes(sig), nil -} - -func (api *PrivateKeyStoreAPI) BindAccountAndPublicKey(ctx context.Context, accountName string) error { - return api.b.Wallet().BindAccountAndPublicKey(accountName) -} - -func (api *PrivateKeyStoreAPI) DeleteBound(ctx context.Context, accountName string) error { - return api.b.Wallet().DeleteBound(accountName) -} - -func (api *PrivateKeyStoreAPI) UpdateBindingInfo(ctx context.Context, accountName string) error { - return api.b.Wallet().BindAccountAndPublicKey(accountName) -} - -func (api *PrivateKeyStoreAPI) GetAccountsByPublicKeys(ctx context.Context) ([]am.Account, error) { - accounts, err := api.b.Wallet().GetAllAccounts() - if err != nil { - return nil, err - } - return accounts, nil -} diff --git a/metrics/config.go b/metrics/config.go index 3cb09c9f..ca3237bb 100644 --- a/metrics/config.go +++ b/metrics/config.go @@ -1,11 +1,11 @@ package metrics type Config struct { - MetricsFlag bool `mapstructure:"test-metricsflag"` - InfluxDBFlag bool `mapstructure:"test-influxdbflag"` - Url string `mapstructure:"test-influxdburl"` - DataBase string `mapstructure:"test-influxdbname"` - UserName string `mapstructure:"test-influxdbuser"` - PassWd string `mapstructure:"test-influxdbpasswd"` - NameSpace string `mapstructure:"test-influxdbnamespace"` + MetricsFlag bool `mapstructure:"metrics"` + InfluxDBFlag bool `mapstructure:"influxdb"` + URL string `mapstructure:"influxdburl"` + DataBase string `mapstructure:"influxdbname"` + UserName string `mapstructure:"influxdbuser"` + PassWd string `mapstructure:"influxdbpasswd"` + NameSpace string `mapstructure:"influxdbnamespace"` } diff --git a/node/config.go b/node/config.go index 8dd996dd..7a0516ba 100644 --- a/node/config.go +++ b/node/config.go @@ -30,45 +30,40 @@ import ( "github.com/fractalplatform/fractal/crypto" "github.com/fractalplatform/fractal/p2p" "github.com/fractalplatform/fractal/p2p/enode" - "github.com/fractalplatform/fractal/wallet/keystore" ) const ( - datadirPrivateKey = "nodekey" // Path within the datadir to the node's private key - datadirDefaultKeyStore = "keystore" // Path within the datadir to the keystore - datadirBootNodes = "bootnodes" // Path within the datadir to the boot node list - datadirStaticNodes = "staticnodes" // Path within the datadir to the static node list - datadirTrustedNodes = "trustednodes" // Path within the datadir to the trusted node list + datadirPrivateKey = "nodekey" // Path within the datadir to the node's private key + datadirBootNodes = "bootnodes" // Path within the datadir to the boot node list + datadirStaticNodes = "staticnodes" // Path within the datadir to the static node list + datadirTrustedNodes = "trustednodes" // Path within the datadir to the trusted node list ) // Config represents a small collection of configuration values to fine tune the // P2P network layer of a protocol stack. type Config struct { - Name string `mapstructure:"node-name"` - DataDir string `mapstructure:"node-datadir"` - - KeyStoreDir string `mapstructure:"node-keystore"` - UseLightweightKDF bool `mapstructure:"node-lightkdf"` - - IPCPath string `mapstructure:"node-ipcpath"` - - HTTPHost string `mapstructure:"node-httphost"` - HTTPPort int `mapstructure:"node-httpport"` - HTTPModules []string `mapstructure:"node-httpmodules"` - HTTPCors []string `mapstructure:"node-httpcors"` - HTTPVirtualHosts []string `mapstructure:"node-httpvirtualhosts"` - - WSHost string `mapstructure:"node-wshost"` - WSPort int `mapstructure:"node-wsport"` - WSModules []string `mapstructure:"node-wsmodules"` - WSOrigins []string `mapstructure:"node-wsorigins"` - WSExposeAll bool `mapstructure:"node-wsexposall"` + Name string + DataDir string `mapstructure:"datadir"` + UseLightweightKDF bool `mapstructure:"lightkdf"` + IPCPath string `mapstructure:"ipcpath"` + + HTTPHost string `mapstructure:"httphost"` + HTTPPort int `mapstructure:"httpport"` + HTTPModules []string `mapstructure:"httpmodules"` + HTTPCors []string `mapstructure:"httpcors"` + HTTPVirtualHosts []string `mapstructure:"httpvirtualhosts"` + + WSHost string `mapstructure:"wshost"` + WSPort int `mapstructure:"wsport"` + WSModules []string `mapstructure:"wsmodules"` + WSOrigins []string `mapstructure:"wsorigins"` + WSExposeAll bool `mapstructure:"wsexposall"` // p2p - P2PBootNodes string - P2PStaticNodes string - P2PTrustNodes string - P2PConfig *p2p.Config + P2PBootNodes string `mapstructure:"bootnodes"` + P2PStaticNodes string `mapstructure:"staticnodes"` + P2PTrustNodes string `mapstructure:"trustnodes"` + P2PConfig *p2p.Config `mapstructure:"p2p"` // Logger is a custom logger to use with the p2p.Server. Logger log.Logger `toml:",omitempty"` @@ -134,18 +129,6 @@ func (c *Config) resolvePath(path string) string { return filepath.Join(filepath.Join(c.DataDir, c.Name), path) } -// walletConfig determines the settings for scrypt and keydirectory -func (c *Config) walletConfig() (int, int, string) { - scryptN := keystore.StandardScryptN - scryptP := keystore.StandardScryptP - if c.UseLightweightKDF { - scryptN = keystore.LightScryptN - scryptP = keystore.LightScryptP - } - - return scryptN, scryptP, filepath.Join(c.DataDir, datadirDefaultKeyStore) -} - func (c *Config) NodeKey() *ecdsa.PrivateKey { // Use any specifically configured key. if c.P2PConfig.PrivateKey != nil { diff --git a/node/node.go b/node/node.go index df059050..74282fe8 100644 --- a/node/node.go +++ b/node/node.go @@ -18,7 +18,6 @@ package node import ( "fmt" - "io/ioutil" "os" "path/filepath" "reflect" @@ -31,13 +30,11 @@ import ( adaptor "github.com/fractalplatform/fractal/p2p/protoadaptor" "github.com/fractalplatform/fractal/rpc" "github.com/fractalplatform/fractal/utils/filelock" - "github.com/fractalplatform/fractal/wallet" ) // Node is a container on which services can be registered. type Node struct { config *Config - wallet *wallet.Wallet running bool instanceDirLock filelock.Releaser // prevents concurrent use of instance directory serviceFuncs []ServiceConstructor // Service constructors (in dependency order) @@ -70,14 +67,9 @@ func New(conf *Config) (*Node, error) { if conf.Logger == nil { conf.Logger = log.New() } - w, err := makeWallet(conf) - if err != nil { - return nil, err - } return &Node{ config: conf, - wallet: w, running: false, serviceFuncs: []ServiceConstructor{}, services: make(map[reflect.Type]Service), @@ -88,23 +80,6 @@ func New(conf *Config) (*Node, error) { }, nil } -func makeWallet(conf *Config) (*wallet.Wallet, error) { - n, p, dir := conf.walletConfig() - if dir == "" { - var err error - // There is no datadir. - dir, err = ioutil.TempDir("", "tmpkeystore") - if err != nil { - return nil, err - } - } - - if err := os.MkdirAll(dir, 0700); err != nil { - return nil, err - } - return wallet.NewWallet(dir, n, p), nil -} - // Register injects a new service into the node's stack. The service created by // the passed constructor must be unique in its type with regard to sibling ones. func (n *Node) Register(constructor ServiceConstructor) error { @@ -147,7 +122,6 @@ func (n *Node) Start() error { ctx := &ServiceContext{ config: n.config, services: make(map[reflect.Type]Service), - Wallet: n.wallet, P2P: n.p2pServer, } for kind, s := range services { // copy needed for threaded access @@ -430,3 +404,12 @@ func (n *Node) stopWS() { n.wsHandler = nil } } + +func (n *Node) GetNodeConfig() *ServiceContext { + ctx := &ServiceContext{ + config: n.config, + services: make(map[reflect.Type]Service), + P2P: n.p2pServer, + } + return ctx +} diff --git a/node/service.go b/node/service.go index fab37acf..a0789b01 100644 --- a/node/service.go +++ b/node/service.go @@ -26,7 +26,6 @@ import ( "github.com/fractalplatform/fractal/utils/fdb" ldb "github.com/fractalplatform/fractal/utils/fdb/leveldb" mdb "github.com/fractalplatform/fractal/utils/fdb/memdb" - "github.com/fractalplatform/fractal/wallet" ) // ServiceContext is a collection of service independent options inherited from @@ -35,7 +34,6 @@ import ( type ServiceContext struct { config *Config services map[reflect.Type]Service // Index of the already constructed services - Wallet *wallet.Wallet P2P *adaptor.ProtoAdaptor } diff --git a/p2p/discover/udp_test.go b/p2p/discover/udp_test.go index baf3c0c5..8b637f85 100644 --- a/p2p/discover/udp_test.go +++ b/p2p/discover/udp_test.go @@ -301,10 +301,10 @@ func TestUDP_findnodeMultiReply(t *testing.T) { // send the reply as two packets. list := []*node{ - wrapNode(enode.MustParseV4("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303?discport=30304")), - wrapNode(enode.MustParseV4("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303")), - wrapNode(enode.MustParseV4("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301?discport=17")), - wrapNode(enode.MustParseV4("enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303")), + wrapNode(enode.MustParseV4("fnode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303?discport=30304")), + wrapNode(enode.MustParseV4("fnode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303")), + wrapNode(enode.MustParseV4("fnode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301?discport=17")), + wrapNode(enode.MustParseV4("fnode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303")), } rpclist := make([]rpcNode, len(list)) for i := range list { diff --git a/p2p/enode/node.go b/p2p/enode/node.go index ff24c1b7..a9505f42 100644 --- a/p2p/enode/node.go +++ b/p2p/enode/node.go @@ -155,7 +155,7 @@ func (n ID) String() string { // The Go syntax representation of a ID is a call to HexID. func (n ID) GoString() string { - return fmt.Sprintf("enode.HexID(\"%x\")", n[:]) + return fmt.Sprintf("fnode.HexID(\"%x\")", n[:]) } // TerminalString returns a shortened hex string for terminal logging. diff --git a/p2p/enode/nodedb.go b/p2p/enode/nodedb.go index da1db133..442b72bc 100644 --- a/p2p/enode/nodedb.go +++ b/p2p/enode/nodedb.go @@ -175,7 +175,7 @@ func (db *DB) Node(id ID) *Node { func mustDecodeNode(id, data []byte) *Node { node := new(Node) if err := rlp.DecodeBytes(data, &node.r); err != nil { - panic(fmt.Errorf("p2p/enode: can't decode node %x in DB: %v", id, err)) + panic(fmt.Errorf("p2p/fnode: can't decode node %x in DB: %v", id, err)) } // Restore node id cache. copy(node.id[:], id) diff --git a/p2p/enode/urlv4.go b/p2p/enode/urlv4.go index d56ad772..8898e589 100644 --- a/p2p/enode/urlv4.go +++ b/p2p/enode/urlv4.go @@ -31,7 +31,7 @@ import ( "github.com/fractalplatform/fractal/p2p/enr" ) -var incompleteNodeURL = regexp.MustCompile("(?i)^(?:enode://)?([0-9a-f]+)$") +var incompleteNodeURL = regexp.MustCompile("(?i)^(?:fnode://)?([0-9a-f]+)$") // MustParseV4 parses a node URL. It panics if the URL is not valid. func MustParseV4(rawurl string) *Node { @@ -108,8 +108,8 @@ func parseComplete(rawurl string) (*Node, error) { if err != nil { return nil, err } - if u.Scheme != "enode" { - return nil, errors.New("invalid URL scheme, want \"enode\"") + if u.Scheme != "fnode" { + return nil, errors.New("invalid URL scheme, want \"fnode\"") } // Parse the Node ID from the user portion. if u.User == nil { @@ -171,7 +171,7 @@ func (n *Node) v4URL() string { default: nodeid = fmt.Sprintf("%s.%x", scheme, n.id[:]) } - u := url.URL{Scheme: "enode"} + u := url.URL{Scheme: "fnode"} if n.Incomplete() { u.Host = nodeid } else { diff --git a/p2p/enode/urlv4_test.go b/p2p/enode/urlv4_test.go index 3680ab6b..5ee27dea 100644 --- a/p2p/enode/urlv4_test.go +++ b/p2p/enode/urlv4_test.go @@ -34,27 +34,27 @@ var parseNodeTests = []struct { }{ { rawurl: "http://foobar", - wantError: `invalid URL scheme, want "enode"`, + wantError: `invalid URL scheme, want "fnode"`, }, { - rawurl: "enode://01010101@123.124.125.126:3", + rawurl: "fnode://01010101@123.124.125.126:3", wantError: `invalid node ID (wrong length, want 128 hex chars)`, }, // Complete nodes with IP address. { - rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@hostname:3", + rawurl: "fnode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@hostname:3", wantError: `invalid IP address`, }, { - rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:foo", + rawurl: "fnode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:foo", wantError: `invalid port`, }, { - rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:3?discport=foo", + rawurl: "fnode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:3?discport=foo", wantError: `invalid discport in query`, }, { - rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150", + rawurl: "fnode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150", wantResult: NewV4( hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), net.IP{0x7f, 0x0, 0x0, 0x1}, @@ -63,7 +63,7 @@ var parseNodeTests = []struct { ), }, { - rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[::]:52150", + rawurl: "fnode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[::]:52150", wantResult: NewV4( hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), net.ParseIP("::"), @@ -72,7 +72,7 @@ var parseNodeTests = []struct { ), }, { - rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:52150", + rawurl: "fnode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:52150", wantResult: NewV4( hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), net.ParseIP("2001:db8:3c4d:15::abcd:ef12"), @@ -81,7 +81,7 @@ var parseNodeTests = []struct { ), }, { - rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150?discport=22334", + rawurl: "fnode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150?discport=22334", wantResult: NewV4( hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), net.IP{0x7f, 0x0, 0x0, 0x1}, @@ -98,7 +98,7 @@ var parseNodeTests = []struct { ), }, { - rawurl: "enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439", + rawurl: "fnode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439", wantResult: NewV4( hexPubkey("1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439"), nil, 0, 0, @@ -110,7 +110,7 @@ var parseNodeTests = []struct { wantError: `invalid node ID (wrong length, want 128 hex chars)`, }, { - rawurl: "enode://01010101", + rawurl: "fnode://01010101", wantError: `invalid node ID (wrong length, want 128 hex chars)`, }, { @@ -153,7 +153,7 @@ func TestParseNode(t *testing.T) { func TestNodeString(t *testing.T) { for i, test := range parseNodeTests { - if test.wantError == "" && strings.HasPrefix(test.rawurl, "enode://") { + if test.wantError == "" && strings.HasPrefix(test.rawurl, "fnode://") { str := test.wantResult.String() if str != test.rawurl { t.Errorf("test %d: Node.String() mismatch:\ngot: %s\nwant: %s", i, str, test.rawurl) diff --git a/p2p/server.go b/p2p/server.go index 400c9e67..f904a758 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -62,29 +62,28 @@ type Config struct { PrivateKey *ecdsa.PrivateKey // NetworkID is ID of network - NetworkID uint `mapstructure:"p2p-networkdID"` + NetworkID uint `mapstructure:"networkid"` // MaxPeers is the maximum number of peers that can be // connected. It must be greater than zero. - MaxPeers int `mapstructure:"p2p-maxpeers"` + MaxPeers int `mapstructure:"maxpeers"` // MaxPendingPeers is the maximum number of peers that can be pending in the // handshake phase, counted separately for inbound and outbound connections. // Zero defaults to preset values. - MaxPendingPeers int `mapstructure:"p2p-maxpendpeers"` + MaxPendingPeers int `mapstructure:"maxpendpeers"` // DialRatio controls the ratio of inbound to dialed connections. // Example: a DialRatio of 2 allows 1/2 of connections to be dialed. // Setting DialRatio to zero defaults it to 3. - DialRatio int `mapstructure:"p2p-dialratio"` + DialRatio int `mapstructure:"dialratio"` // NoDiscovery can be used to disable the peer discovery mechanism. // Disabling is useful for protocol debugging (manual topology). - NoDiscovery bool `mapstructure:"p2p-nodiscover"` + NoDiscovery bool `mapstructure:"nodiscover"` // Name sets the node name of this server. - // Use common.MakeName to create a name that follows existing conventions. - Name string `mapstructure:"p2p-nodename"` + Name string `mapstructure:"name"` // BootstrapNodes are used to establish connectivity // with the rest of the network. @@ -101,11 +100,11 @@ type Config struct { // Connectivity can be restricted to certain IP networks. // If this option is set to a non-nil value, only hosts which match one of the // IP networks contained in the list are considered. - NetRestrict *netutil.Netlist `mapstructure:"p2p-badIP"` + NetRestrict *netutil.Netlist // NodeDatabase is the path to the database containing the previously seen // live nodes in the network. - NodeDatabase string `mapstructure:"p2p-nodedb"` + NodeDatabase string `mapstructure:"nodedb"` // Protocols should contain the protocols supported // by the server. Matching protocols are launched for @@ -118,7 +117,7 @@ type Config struct { // If the port is zero, the operating system will pick a port. The // ListenAddr field will be updated with the actual address when // the server is started. - ListenAddr string `mapstructure:"p2p-listenaddr"` + ListenAddr string `mapstructure:"listenaddr"` // If set to a non-nil value, the given NAT port mapper // is used to make the listening port available to the @@ -130,7 +129,7 @@ type Config struct { Dialer NodeDialer // If NoDial is true, the server will not dial any peers. - NoDial bool `mapstructure:"p2p-nodial"` + NoDial bool `mapstructure:"nodial"` // If EnableMsgEvents is set then the server will emit PeerEvents // whenever a message is sent to or received from a peer diff --git a/params/chainconfig.go b/params/chainconfig.go index 492f4f71..f84cfda7 100644 --- a/params/chainconfig.go +++ b/params/chainconfig.go @@ -17,38 +17,109 @@ package params import ( + "encoding/json" "math/big" - - "github.com/fractalplatform/fractal/common" ) -const DefaultPubkeyHex = "047db227d7094ce215c3a0f57e1bcc732551fe351f94249471934567e0f5dc1bf795962b8cccb87a2eb56b29fbe37d614e2f4c3c45b789ae4f1f51f4cb21972ffd" - // ChainConfig is the core config which determines the blockchain settings. -// ChainConfig is stored in the database on a per block basis. type ChainConfig struct { - ChainID *big.Int `json:"chainId"` // chainId identifies the current chain and is used for replay protection - BootNodes []string `json:"bootnodes"` // enode URLs of the P2P bootstrap nodes - SysName common.Name `json:"sysName"` // system name - SysToken string `json:"sysToken"` // system token - AssetChargeRatio uint64 `json:"assetChargeRatio"` - ContractChargeRatio uint64 `json:"contractChargeRatio"` - SysTokenID uint64 `json:"-"` - SysTokenDecimals uint64 `json:"-"` - UpperLimit *big.Int `json:"upperlimit"` + BootNodes []string `json:"bootnodes,omitempty"` // enode URLs of the P2P bootstrap nodes + ChainID *big.Int `json:"chainId,omitempty"` // chainId identifies the current chain and is used for replay protection + ChainName string `json:"chainName,omitempty"` // chain name + ChainURL string `json:"chainUrl,omitempty"` // chain url + AccountNameCfg *NameConfig `json:"accountParams,omitempty"` + AssetNameCfg *NameConfig `json:"assetParams,omitempty"` + ChargeCfg *ChargeConfig `json:"chargeParams,omitempty"` + ForkedCfg *FrokedConfig `json:"upgradeParams,omitempty"` + DposCfg *DposConfig `json:"dposParams,omitempty"` + SysName string `json:"systemName,omitempty"` // system name + AccountName string `json:"accountName,omitempty"` // system name + DposName string `json:"dposName,omitempty"` // system name + SysToken string `json:"systemToken,omitempty"` // system token + SysTokenID uint64 `json:"sysTokenID,omitempty"` + SysTokenDecimals uint64 `json:"sysTokenDecimal,omitempty"` +} + +type ChargeConfig struct { + AssetRatio uint64 `json:"assetRatio,omitempty"` + ContractRatio uint64 `json:"contractRatio,omitempty"` +} + +type NameConfig struct { + Level uint64 `json:"level,omitempty"` + Length uint64 `json:"length,omitempty"` + SubLength uint64 `json:"subLength,omitempty"` +} + +type FrokedConfig struct { + ForkBlockNum uint64 `json:"blockCnt,omitempty"` + Forkpercentage uint64 `json:"upgradeRatio,omitempty"` +} + +type DposConfig struct { + MaxURLLen uint64 `json:"maxURLLen,omitempty"` // url length + UnitStake *big.Int `json:"unitStake,omitempty"` // state unit + CadidateMinQuantity *big.Int `json:"cadidateMinQuantity,omitempty"` // min quantity + VoterMinQuantity *big.Int `json:"voterMinQuantity,omitempty"` // min quantity + ActivatedMinQuantity *big.Int `json:"activatedMinQuantity,omitempty"` // min active quantity + BlockInterval uint64 `json:"blockInterval,omitempty"` + BlockFrequency uint64 `json:"blockFrequency,omitempty"` + CadidateScheduleSize uint64 `json:"cadidateScheduleSize,omitempty"` + DelayEcho uint64 `json:"delayEcho,omitempty"` + ExtraBlockReward *big.Int `json:"extraBlockReward,omitempty"` + BlockReward *big.Int `json:"blockReward,omitempty"` } var DefaultChainconfig = &ChainConfig{ - ChainID: big.NewInt(1), - SysName: "ftsystemio", - SysToken: "ftoken", - AssetChargeRatio: 80, - ContractChargeRatio: 80, + BootNodes: []string{}, + ChainID: big.NewInt(1), + ChainName: "fractal", + ChainURL: "https://fractalproject.com", + AccountNameCfg: &NameConfig{ + Level: 1, + Length: 16, + SubLength: 8, + }, + AssetNameCfg: &NameConfig{ + Level: 1, + Length: 16, + SubLength: 8, + }, + ChargeCfg: &ChargeConfig{ + AssetRatio: 80, + ContractRatio: 80, + }, + ForkedCfg: &FrokedConfig{ + ForkBlockNum: 10000, + Forkpercentage: 80, + }, + DposCfg: &DposConfig{ + MaxURLLen: 512, + UnitStake: big.NewInt(1000), + CadidateMinQuantity: big.NewInt(10), + VoterMinQuantity: big.NewInt(1), + ActivatedMinQuantity: big.NewInt(100), + BlockInterval: 3000, + BlockFrequency: 6, + CadidateScheduleSize: 3, + DelayEcho: 2, + ExtraBlockReward: big.NewInt(1), + BlockReward: big.NewInt(5), + }, + SysName: "fractal.admin", + AccountName: "fractal.account", + DposName: "fractal.dpos", + SysToken: "ftoken", +} + +func (cfg *ChainConfig) Copy() *ChainConfig { + bts, _ := json.Marshal(cfg) + c := &ChainConfig{} + json.Unmarshal(bts, c) + return c } const ( - // TheForkNum this hard forking for add fork controller function. - TheForkNum uint64 = 422500 // NextForkID is the id of next fork NextForkID uint64 = 0 ) diff --git a/params/gas_table.go b/params/gas_table.go index d1ecfb83..21af3453 100644 --- a/params/gas_table.go +++ b/params/gas_table.go @@ -20,7 +20,7 @@ import ( "github.com/fractalplatform/fractal/types" ) -// GasTable organizes gas prices for different ethereum phases. +// GasTable organizes gas prices for different phases. type GasTable struct { ExtcodeSize uint64 ExtcodeCopy uint64 @@ -38,14 +38,21 @@ type GasTable struct { // not charged. CreateBySuicide uint64 SetOwner uint64 + GetAccountTime uint64 GetSnapshotTime uint64 GetAssetAmount uint64 SnapBalance uint64 + IssueAsset uint64 + DestroyAsset uint64 + AddAsset uint64 + GetAccountID uint64 + GetDelegate uint64 + CryptoCalc uint64 + CryptoByte uint64 } -// Variables containing gas prices for different ethereum phases. +// Variables containing gas prices for different phases. var ( - // GasTable contain the gas re-prices GasTableInstanse = GasTable{ ExtcodeSize: 700, @@ -58,9 +65,17 @@ var ( CreateBySuicide: 25000, SetOwner: 200, + GetAccountTime: 200, GetSnapshotTime: 200, GetAssetAmount: 200, SnapBalance: 200, + IssueAsset: 200, + DestroyAsset: 200, + AddAsset: 200, + GetAccountID: 200, + GetDelegate: 200, + CryptoCalc: 20000, + CryptoByte: 1000, } ) diff --git a/params/params.go b/params/params.go index 956081c4..de885972 100644 --- a/params/params.go +++ b/params/params.go @@ -38,34 +38,25 @@ const ( //VM const - ExpByteGas uint64 = 10 // Times ceil(log256(exponent)) for the EXP instruction. - SloadGas uint64 = 50 // Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added. CallValueTransferGas uint64 = 9000 // Paid for CALL when the value transfer is non-zero. - CallNewAccountGas uint64 = 25000 // Paid for CALL when the destination address didn't exist prior. QuadCoeffDiv uint64 = 512 // Divisor for the quadratic particle of the memory cost equation. SstoreSetGas uint64 = 20000 // Once per SLOAD operation. LogDataGas uint64 = 8 // Per byte in a LOG* operation's data. CallStipend uint64 = 2300 // Free gas given at beginning of call. - Sha3Gas uint64 = 30 // Once per SHA3 operation. - Sha3WordGas uint64 = 6 // Once per word of the SHA3 operation's data. - SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero. - SstoreClearGas uint64 = 5000 // Once per SSTORE operation if the zeroness doesn't change. - SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero. - JumpdestGas uint64 = 1 // Refunded gas, once per SSTORE operation if the zeroness changes to zero. - EpochDuration uint64 = 30000 // Duration between proof-of-work epochs. - CallGas uint64 = 40 // Once per CALL operation & message call transaction. - CreateDataGas uint64 = 200 // - CallCreateDepth uint64 = 1024 // Maximum depth of call/create stack. - ExpGas uint64 = 10 // Once per EXP instruction - LogGas uint64 = 375 // Per LOG* operation. - CopyGas uint64 = 3 // - StackLimit uint64 = 1024 // Maximum size of VM stack allowed. - TierStepGas uint64 = 0 // Once per operation, for a selection of them. - LogTopicGas uint64 = 375 // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas. - CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction. - SuicideRefundGas uint64 = 24000 // Refunded following a suicide operation. - MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. + Sha3Gas uint64 = 30 // Once per SHA3 operation. + Sha3WordGas uint64 = 6 // Once per word of the SHA3 operation's data. + SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero. + JumpdestGas uint64 = 1 // Refunded gas, once per SSTORE operation if the zeroness changes to zero. + EpochDuration uint64 = 30000 // Duration between proof-of-work epochs. + CreateDataGas uint64 = 200 // + CallCreateDepth uint64 = 1024 // Maximum depth of call/create stack. + LogGas uint64 = 375 // Per LOG* operation. + CopyGas uint64 = 3 // + StackLimit uint64 = 1024 // Maximum size of VM stack allowed. + LogTopicGas uint64 = 375 // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas. + CreateGas uint64 = 32000 // Once per CREATE operation & contract-creation transaction. + MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. MaxCodeSize = 24576 // Maximum bytecode to permit for a contract @@ -83,10 +74,6 @@ const ( Bn256ScalarMulGas uint64 = 40000 // Gas needed for an elliptic curve scalar multiplication Bn256PairingBaseGas uint64 = 100000 // Base price for an elliptic curve pairing check Bn256PairingPerPointGas uint64 = 80000 // Per-point price for an elliptic curve pairing check - - Wei = 1 - GWei = 1e9 - Ether = 1e18 ) var ( @@ -95,3 +82,8 @@ var ( MinimumDifficulty = big.NewInt(131072) // The minimum that the difficulty may ever be. DurationLimit = big.NewInt(13) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not. ) + +var ( + MaxSignDepth = uint64(10) + MaxSignLength = uint64(50) +) diff --git a/processor/error.go b/processor/error.go index ee544df5..d73a1e87 100644 --- a/processor/error.go +++ b/processor/error.go @@ -52,6 +52,10 @@ var ( ErrNonceTooLow = errors.New("nonce too low") errZeroBlockTime = errors.New("timestamp equals parent's") + + errParentBlock = errors.New("parent block not exist") + // + ErrActionInvalidValue = errors.New("action value invalid") ) // GenesisMismatchError is raised when trying to overwrite an existing diff --git a/processor/evm.go b/processor/evm.go index 65f2ad39..ddba6d8a 100644 --- a/processor/evm.go +++ b/processor/evm.go @@ -60,7 +60,7 @@ type ChainContext interface { StateAt(hash common.Hash) (*state.StateDB, error) // WriteBlockWithState writes the block and all associated state to the database. - WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) error + WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (bool, error) // CheckForkID checks the validity of forkID CheckForkID(header *types.Header) error @@ -73,12 +73,14 @@ type ChainContext interface { } type EgnineContext interface { - // Author retrieves the Ethereum address of the account that minted the given + // Author retrieves the address of the account that minted the given // block, which may be different from the header's coinbase if a consensus // engine is based on signatures. Author(header *types.Header) (common.Name, error) - ProcessAction(chainCfg *params.ChainConfig, state *state.StateDB, action *types.Action) error + ProcessAction(chainCfg *params.ChainConfig, state *state.StateDB, action *types.Action) ([]*types.InternalAction, error) + + GetDelegatedByTime(name string, timestamp uint64, state *state.StateDB) (*big.Int, *big.Int, uint64, error) } type EvmContext struct { @@ -87,7 +89,7 @@ type EvmContext struct { } // NewEVMContext creates a new context for use in the EVM. -func NewEVMContext(sender common.Name, fromPubkey common.PubKey, assetID uint64, gasPrice *big.Int, header *types.Header, chain *EvmContext, author *common.Name) vm.Context { +func NewEVMContext(sender common.Name, assetID uint64, gasPrice *big.Int, header *types.Header, chain *EvmContext, author *common.Name) vm.Context { // If we don't have an explicit author (i.e. not mining), extract from the header var beneficiary common.Name if author == nil { @@ -96,16 +98,17 @@ func NewEVMContext(sender common.Name, fromPubkey common.PubKey, assetID uint64, beneficiary = *author } return vm.Context{ - GetHash: GetHashFn(header, chain), - Origin: sender, - FromPubkey: fromPubkey, - AssetID: assetID, - Coinbase: beneficiary, - BlockNumber: new(big.Int).Set(header.Number), - Time: new(big.Int).Set(header.Time), - Difficulty: new(big.Int).Set(header.Difficulty), - GasLimit: header.GasLimit, - GasPrice: new(big.Int).Set(gasPrice), + GetHash: GetHashFn(header, chain), + GetDelegatedByTime: chain.GetDelegatedByTime, + GetHeaderByNumber: chain.GetHeaderByNumber, + Origin: sender, + AssetID: assetID, + Coinbase: beneficiary, + BlockNumber: new(big.Int).Set(header.Number), + Time: new(big.Int).Set(header.Time), + Difficulty: new(big.Int).Set(header.Difficulty), + GasLimit: header.GasLimit, + GasPrice: new(big.Int).Set(gasPrice), } } diff --git a/processor/processor.go b/processor/processor.go index c0e78a9c..36bf2518 100644 --- a/processor/processor.go +++ b/processor/processor.go @@ -17,6 +17,7 @@ package processor import ( + "github.com/ethereum/go-ethereum/log" "github.com/fractalplatform/fractal/accountmanager" "github.com/fractalplatform/fractal/common" "github.com/fractalplatform/fractal/consensus" @@ -42,7 +43,7 @@ func NewStateProcessor(bc ChainContext, engine consensus.IEngine) *StateProcesso } } -// Process processes the state changes according to the Ethereum rules by running +// Process processes the state changes according to the rules by running // the transaction messages using the statedb and applying any rewards to both // the processor (coinbase) and any included uncles. // @@ -92,14 +93,13 @@ func (p *StateProcessor) ApplyTransaction(author *common.Name, gp *common.GasPoo var totalGas uint64 var ios []*types.ActionResult + detailTx := &types.DetailTx{} + var detailActions []*types.DetailAction for i, action := range tx.GetActions() { - fromPubkey, err := types.Recover(types.NewSigner(config.ChainID), action, tx) - if err != nil { - return nil, 0, err - } - - if err := accountDB.IsValidSign(action.Sender(), action.Type(), fromPubkey); err != nil { - return nil, 0, err + if needCheckSign(accountDB, action) { + if err := accountDB.RecoverTx(types.NewSigner(config.ChainID), tx); err != nil { + return nil, 0, err + } } nonce, err := accountDB.GetNonce(action.Sender()) @@ -116,7 +116,7 @@ func (p *StateProcessor) ApplyTransaction(author *common.Name, gp *common.GasPoo ChainContext: p.bc, EgnineContext: p.engine, } - context := NewEVMContext(action.Sender(), fromPubkey, assetID, tx.GasPrice(), header, evmcontext, author) + context := NewEVMContext(action.Sender(), assetID, tx.GasPrice(), header, evmcontext, author) vmenv := vm.NewEVM(context, accountDB, statedb, config, cfg) _, gas, failed, err, vmerr := ApplyMessage(accountDB, vmenv, action, gp, gasPrice, assetID, config, p.engine) @@ -137,9 +137,14 @@ func (p *StateProcessor) ApplyTransaction(author *common.Name, gp *common.GasPoo vmerrstr := "" if vmerr != nil { vmerrstr = vmerr.Error() + log.Debug("processer apply transaction ", "hash", tx.Hash(), "err", vmerrstr) } - ios = append(ios, &types.ActionResult{Status: status, Index: uint64(i), GasUsed: gas, Error: vmerrstr}) - + var gasAllot []*types.GasDistribution + for account, gas := range vmenv.FounderGasMap { + gasAllot = append(gasAllot, &types.GasDistribution{Account: account.String(), Gas: uint64(gas.Value), TypeID: gas.TypeID}) + } + ios = append(ios, &types.ActionResult{Status: status, Index: uint64(i), GasUsed: gas, GasAllot: gasAllot, Error: vmerrstr}) + detailActions = append(detailActions, &types.DetailAction{InternalActions: vmenv.InternalTxs}) } root := statedb.ReceiptRoot() receipt := types.NewReceipt(root[:], *usedGas, totalGas) @@ -149,5 +154,21 @@ func (p *StateProcessor) ApplyTransaction(author *common.Name, gp *common.GasPoo receipt.Logs = statedb.GetLogs(tx.Hash()) receipt.Bloom = types.CreateBloom([]*types.Receipt{receipt}) + detailTx.TxHash = receipt.TxHash + detailTx.Actions = detailActions + receipt.SetInternalTxsLog(detailTx) return receipt, totalGas, nil } + +func needCheckSign(accountDB *accountmanager.AccountManager, action *types.Action) bool { + authorVersion := types.GetAuthorCache(action) + if len(authorVersion) == 0 { + return true + } + for name, version := range authorVersion { + if tmpVersion, err := accountDB.GetAuthorVersion(name); err != nil || version != tmpVersion { + return true + } + } + return false +} diff --git a/processor/transition.go b/processor/transition.go index 359756ec..c637f57f 100644 --- a/processor/transition.go +++ b/processor/transition.go @@ -82,10 +82,10 @@ func (st *StateTransition) preCheck() error { func (st *StateTransition) buyGas() error { mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.action.Gas()), st.gasPrice) - balance, err := st.account.GetAccountBalanceByID(st.from, st.assetID) + balance, err := st.account.GetAccountBalanceByID(st.from, st.assetID, 0) //balance, err := st.account.GetAccountBalanceByID(st.from, st.assetID) if err != nil { - return errInsufficientBalanceForGas + return err } if balance.Cmp(mgval) < 0 { return errInsufficientBalanceForGas @@ -131,24 +131,35 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo switch { case actionType == types.CreateContract: ret, st.gas, vmerr = evm.Create(sender, st.action, st.gas) - case actionType == types.Transfer: + case actionType == types.CallContract: ret, st.gas, vmerr = evm.Call(sender, st.action, st.gas) - case actionType == types.RegProducer: + case actionType == types.RegCadidate: fallthrough - case actionType == types.UpdateProducer: + case actionType == types.UpdateCadidate: fallthrough - case actionType == types.UnregProducer: + case actionType == types.UnregCadidate: fallthrough case actionType == types.RemoveVoter: fallthrough - case actionType == types.VoteProducer: + case actionType == types.VoteCadidate: + fallthrough + case actionType == types.ChangeCadidate: fallthrough - case actionType == types.ChangeProducer: + case actionType == types.KickedCadidate: fallthrough - case actionType == types.UnvoteProducer: - vmerr = st.engine.ProcessAction(st.evm.ChainConfig(), st.evm.StateDB, st.action) + case actionType == types.ExitTakeOver: + fallthrough + case actionType == types.UnvoteCadidate: + internalLogs, err := st.engine.ProcessAction(st.evm.ChainConfig(), st.evm.StateDB, st.action) + vmerr = err + evm.InternalTxs = append(evm.InternalTxs, internalLogs...) default: - vmerr = st.account.Process(st.action) + internalLogs, err := st.account.Process(&types.AccountManagerContext{ + Action: st.action, + Number: st.evm.Context.BlockNumber.Uint64(), + }) + vmerr = err + evm.InternalTxs = append(evm.InternalTxs, internalLogs...) } if vmerr != nil { log.Debug("VM returned with error", "err", vmerr) @@ -170,13 +181,23 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo st.refundGas() if st.action.Value().Sign() != 0 { - assetFounder, _ := st.account.GetAssetFounder(st.action.AssetID()) - assetFounderRatio := st.chainConfig.AssetChargeRatio - if len(assetFounder.String()) > 0 { - if _, ok := evm.FounderGasMap[assetFounder]; !ok { - evm.FounderGasMap[assetFounder] = int64(params.ActionGas * assetFounderRatio / 100) + + assetInfo, _ := evm.AccountDB.GetAssetInfoByID(st.action.AssetID()) + assetName := common.Name(assetInfo.GetAssetName()) + + assetFounderRatio := st.chainConfig.ChargeCfg.AssetRatio + if len(assetName.String()) > 0 { + if _, ok := evm.FounderGasMap[assetName]; !ok { + dGas := vm.DistributeGas{ + Value: int64(params.ActionGas * assetFounderRatio / 100), + TypeID: vm.AssetGas} + evm.FounderGasMap[assetName] = dGas } else { - evm.FounderGasMap[assetFounder] += int64(params.ActionGas * assetFounderRatio / 100) + dGas := vm.DistributeGas{ + Value: int64(params.ActionGas * assetFounderRatio / 100), + TypeID: vm.AssetGas} + dGas.Value = evm.FounderGasMap[assetName].Value + dGas.Value + evm.FounderGasMap[assetName] = dGas } } } @@ -188,13 +209,36 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo func (st *StateTransition) distributeGas() error { var totalGas int64 - for founder, gas := range st.evm.FounderGasMap { - st.account.AddAccountBalanceByID(founder, st.assetID, new(big.Int).Mul(st.gasPrice, big.NewInt(gas))) - totalGas += gas + for name, gas := range st.evm.FounderGasMap { + var founder common.Name + if vm.AssetGas == gas.TypeID { + assetInfo, _ := st.account.GetAssetInfoByName(name.String()) + if assetInfo != nil { + founder = assetInfo.GetAssetFounder() + } + } else if vm.ContractGas == gas.TypeID { + founder, _ = st.account.GetFounder(name) + } else if vm.CoinbaseGas == gas.TypeID { + founder = name + } + + st.account.AddAccountBalanceByID(founder, st.assetID, new(big.Int).Mul(st.gasPrice, big.NewInt(gas.Value))) + totalGas += gas.Value } if totalGas > int64(st.gasUsed()) { return fmt.Errorf("calc wrong gas used") } + if _, ok := st.evm.FounderGasMap[st.evm.Coinbase]; !ok { + st.evm.FounderGasMap[st.evm.Coinbase] = vm.DistributeGas{ + Value: int64(st.gasUsed()) - totalGas, + TypeID: vm.CoinbaseGas} + } else { + dGas := vm.DistributeGas{ + Value: int64(st.gasUsed()) - totalGas, + TypeID: vm.CoinbaseGas} + dGas.Value = st.evm.FounderGasMap[st.evm.Coinbase].Value + dGas.Value + st.evm.FounderGasMap[st.evm.Coinbase] = dGas + } st.account.AddAccountBalanceByID(st.evm.Coinbase, st.assetID, new(big.Int).Mul(st.gasPrice, new(big.Int).SetUint64(st.gasUsed()-uint64(totalGas)))) return nil } diff --git a/processor/validator.go b/processor/validator.go index c521daf4..071fa545 100644 --- a/processor/validator.go +++ b/processor/validator.go @@ -50,13 +50,18 @@ func NewBlockValidator(blockchain ChainContext, engine consensus.IEngine) *Block // ValidateHeader checks whether a header conforms to the consensus rules of the // stock engine. func (v *BlockValidator) ValidateHeader(header *types.Header, seal bool) error { + // Short circuit if the header is known, or it's parent not if v.bc.HasBlockAndState(header.Hash(), header.Number.Uint64()) { return ErrKnownBlock } number := header.Number.Uint64() + parent := v.bc.GetHeader(header.ParentHash, number-1) + if parent == nil { + return errParentBlock + } // Ensure that the header's extra-data section is of a reasonable size if uint64(len(header.Extra)) > params.MaximumExtraDataSize { diff --git a/processor/vm/contract.go b/processor/vm/contract.go index a3b33803..08ec6141 100644 --- a/processor/vm/contract.go +++ b/processor/vm/contract.go @@ -39,7 +39,7 @@ type AccountRef common.Name // Name casts AccountRef to a Name func (ar AccountRef) Name() common.Name { return (common.Name)(ar) } -// Contract represents an ethereum contract in the state database. It contains +// Contract represents an contract in the state database. It contains // the the contract code, calling arguments. Contract implements ContractRef type Contract struct { // CallerName is the result of the caller which initialised this diff --git a/processor/vm/contracts.go b/processor/vm/contracts.go index 481487e5..1c8dc0b9 100644 --- a/processor/vm/contracts.go +++ b/processor/vm/contracts.go @@ -37,7 +37,7 @@ type PrecompiledContract interface { Run(input []byte) ([]byte, error) // Run runs the precompiled contract } -// PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum +// PrecompiledContractsHomestead contains the default set of pre-compiled // contracts used in the Frontier and Homestead releases. var PrecompiledContractsHomestead = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{1}): &ecrecover{}, @@ -46,7 +46,7 @@ var PrecompiledContractsHomestead = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{4}): &dataCopy{}, } -// PrecompiledContractsByzantium contains the default set of pre-compiled Ethereum +// PrecompiledContractsByzantium contains the default set of pre-compiled // contracts used in the Byzantium release. var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{ common.BytesToAddress([]byte{1}): &ecrecover{}, diff --git a/processor/vm/errors.go b/processor/vm/errors.go index 39ccb493..a5abb493 100644 --- a/processor/vm/errors.go +++ b/processor/vm/errors.go @@ -25,4 +25,6 @@ var ( ErrTraceLimitReached = errors.New("the number of logs reached the specified limit") ErrInsufficientBalance = errors.New("insufficient balance for transfer") ErrContractAddressCollision = errors.New("contract name collision") + ErrContractCodeCollision = errors.New("contract code collision") + ErrAccountNotExist = errors.New("account not exist") ) diff --git a/processor/vm/gas_table.go b/processor/vm/gas_table.go index 312195c0..33109ead 100644 --- a/processor/vm/gas_table.go +++ b/processor/vm/gas_table.go @@ -319,14 +319,6 @@ func gasCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem gas = gt.Calls transfersValue = stack.Back(2).Sign() != 0 ) - name, err := common.BigToName(stack.Back(1)) - if err != nil { - return 0, err - } - accountExist, _ := evm.AccountDB.AccountIsExist(name) - if transfersValue && accountExist { - gas += params.CallNewAccountGas - } if transfersValue { gas += params.CallValueTransferGas } @@ -381,6 +373,10 @@ func gasRevert(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, m return memoryGasCost(mem, memorySize) } +func gasInvalid(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return 0, nil +} + func gasSuicide(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var gas uint64 //todo @@ -428,6 +424,10 @@ func gasDelegateCall(gt params.GasTable, evm *EVM, contract *Contract, stack *St return gas, nil } +func gasIssueAsset(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return gt.IssueAsset, nil +} + func gasBalanceex(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { return gt.Balance, nil } @@ -437,16 +437,37 @@ func gasGetAssetAmount(gt params.GasTable, evm *EVM, contract *Contract, stack * } func gasSnapBalance(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - return gt.GetAssetAmount, nil + return gt.SnapBalance, nil +} + +func gasGetAccountTime(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return gt.GetAccountTime, nil } func gasGetSnapshotTime(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { return gt.GetSnapshotTime, nil } +func gasCryptoCalc(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return gt.CryptoCalc, nil +} + +func gasGetDelegate(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + return gt.GetDelegate, nil +} func gasAddAsset(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - return params.CallValueTransferGas + gt.Calls, nil + return gt.AddAsset, nil +} + +func gasGetAccountID(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + + return gt.GetAccountID, nil +} + +func gasDestroyAsset(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + + return gt.DestroyAsset, nil } func gasSetAssetOwner(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { diff --git a/processor/vm/instructions.go b/processor/vm/instructions.go index 5ee794e8..45758878 100644 --- a/processor/vm/instructions.go +++ b/processor/vm/instructions.go @@ -18,6 +18,7 @@ package vm import ( "bytes" + "crypto/rand" "errors" "fmt" "math/big" @@ -25,10 +26,12 @@ import ( "strings" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/log" "github.com/fractalplatform/fractal/accountmanager" "github.com/fractalplatform/fractal/asset" "github.com/fractalplatform/fractal/common" "github.com/fractalplatform/fractal/crypto" + "github.com/fractalplatform/fractal/crypto/ecies" "github.com/fractalplatform/fractal/params" "github.com/fractalplatform/fractal/types" "github.com/fractalplatform/fractal/utils/rlp" @@ -395,6 +398,26 @@ func opAddress(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack * return nil, nil } +func opGetAccountTime(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + account := stack.pop() + userID := account.Uint64() + acct, err := evm.AccountDB.GetAccountById(userID) + if err != nil || acct == nil { + stack.push(evm.interpreter.intPool.getZero()) + return nil, nil + } + + number := acct.GetAccountNumber() + head := evm.Context.GetHeaderByNumber(number) + if head == nil { + stack.push(evm.interpreter.intPool.getZero()) + } else { + time := head.Time.Uint64() + stack.push(evm.interpreter.intPool.get().SetUint64(time)) + } + return nil, nil +} + func opGetSnapshotTime(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { time, num := stack.pop(), stack.pop() index := num.Uint64() @@ -423,19 +446,76 @@ func opGetAssetAmount(pc *uint64, evm *EVM, contract *Contract, memory *Memory, return nil, nil } +func opGetDelegate(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + time, account := stack.pop(), stack.pop() + t := time.Uint64() + userID := account.Uint64() + + acct, err := evm.AccountDB.GetAccountById(userID) + if err == nil { + if acct != nil { + name := acct.GetName() + if dbalance, totalDelegate, totalNum, err := evm.Context.GetDelegatedByTime(name.String(), t, evm.StateDB); err == nil { + stack.push(dbalance) + stack.push(totalDelegate) + stack.push(evm.interpreter.intPool.get().SetUint64(totalNum)) + } + } else { + err = errors.New("account object is null") + } + } + + if err != nil { + stack.push(evm.interpreter.intPool.getZero()) + stack.push(evm.interpreter.intPool.getZero()) + stack.push(evm.interpreter.intPool.getZero()) + } + evm.interpreter.intPool.put(time, account) + return nil, nil +} + func opSnapBalance(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - time, assetId, account := stack.pop(), stack.pop(), stack.pop() + opt, time, assetId, account := stack.pop(), stack.pop(), stack.pop(), stack.pop() + o := opt.Uint64() assetID := assetId.Uint64() t := time.Uint64() - name, err := common.BigToName(account) - if err != nil { - return nil, err + userID := account.Uint64() + + var rbalance = big.NewInt(0) + + acct, err := evm.AccountDB.GetAccountById(userID) + + if o >= 4 { + err = errors.New("type id is error") } - balance, err := evm.AccountDB.GetBalanceByTime(name, assetID, t) + + if err == nil { + if acct != nil { + name := acct.GetName() + var id uint64 + if o == 2 || o == 3 { + id = 1 + } + + if balance, err := evm.AccountDB.GetBalanceByTime(name, assetID, id, t); err == nil { + if (o == 1 || o == 3) && (assetID == evm.chainConfig.SysTokenID) { + if dbalance, _, _, err := evm.Context.GetDelegatedByTime(name.String(), t, evm.StateDB); err == nil { + rbalance = new(big.Int).Add(balance, dbalance) + } + } else { + rbalance = balance + } + + } + } else { + err = errors.New("account object is null") + } + } + if err != nil { stack.push(evm.interpreter.intPool.getZero()) } else { - stack.push(balance) + stack.push(rbalance) } evm.interpreter.intPool.put(time, assetId) return nil, nil @@ -443,12 +523,13 @@ func opSnapBalance(pc *uint64, evm *EVM, contract *Contract, memory *Memory, sta func opBalanceex(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { assetId := stack.pop() slot := stack.peek() - name, err := common.BigToName(slot) + userID := slot.Uint64() + account, err := evm.AccountDB.GetAccountById(userID) if err != nil { slot.Set(big.NewInt(0)) return nil, nil } - account, _ := evm.AccountDB.GetAccountByName(name) + if account == nil { slot.Set(big.NewInt(0)) return nil, nil @@ -460,12 +541,14 @@ func opBalanceex(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack func opBalance(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { slot := stack.peek() - name, err := common.BigToName(slot) + userID := slot.Uint64() + //name, err := common.BigToName(slot) + account, err := evm.AccountDB.GetAccountById(userID) if err != nil { slot.Set(big.NewInt(0)) return nil, nil } - account, _ := evm.AccountDB.GetAccountByName(name) + //account, _ := evm.AccountDB.GetAccountByName(name) if account == nil { slot.Set(big.NewInt(0)) return nil, nil @@ -537,17 +620,19 @@ func opReturnDataCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, func opExtCodeSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { slot := stack.peek() - name, err := common.BigToName(slot) + //name, err := common.BigToName(slot) + userID := slot.Uint64() + acct, err := evm.AccountDB.GetAccountById(userID) if err != nil { slot.SetUint64(0) return nil, nil } - account, _ := evm.AccountDB.GetAccountByName(name) - if account == nil { + if acct == nil { slot.SetUint64(0) return nil, nil } - codeSize := account.GetCodeSize() + + codeSize := acct.GetCodeSize() slot.SetUint64(uint64(codeSize)) return nil, nil } @@ -574,18 +659,14 @@ func opCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack func opExtCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { var ( + addr = stack.pop() memOffset = stack.pop() codeOffset = stack.pop() length = stack.pop() ) - name, err := common.BigToName(stack.pop()) - if err != nil { - memory.Set(memOffset.Uint64(), length.Uint64(), nil) - evm.interpreter.intPool.put(memOffset, codeOffset, length) - return nil, nil - } - account, _ := evm.AccountDB.GetAccountByName(name) - if account == nil { + + account, err := evm.AccountDB.GetAccountById(addr.Uint64()) + if err != nil || account == nil { memory.Set(memOffset.Uint64(), length.Uint64(), nil) evm.interpreter.intPool.put(memOffset, codeOffset, length) } else { @@ -640,6 +721,11 @@ func opGasLimit(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack return nil, nil } +func opCallAssetId(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(evm.interpreter.intPool.get().SetUint64(contract.AssetId)) + return nil, nil +} + func opPop(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { evm.interpreter.intPool.put(stack.pop()) return nil, nil @@ -776,8 +862,16 @@ func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta gas := evm.callGasTemp // Pop other call parameters. name, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() - toName, _ := common.BigToName(name) value = math.U256(value) + //toName, _ := common.BigToName(name) + userID := name.Uint64() + acct, err := evm.AccountDB.GetAccountById(userID) + if err != nil || acct == nil { + stack.push(evm.interpreter.intPool.getZero()) + return nil, nil + } + toName := acct.GetName() + // Get the arguments from the memory. args := memory.Get(inOffset.Int64(), inSize.Int64()) @@ -785,7 +879,7 @@ func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta gas += params.CallStipend } - action := types.NewAction(types.Transfer, contract.Name(), toName, 0, evm.AssetID, gas, value, args) + action := types.NewAction(types.CallContract, contract.Name(), toName, 0, evm.AssetID, gas, value, args) ret, returnGas, err := evm.Call(contract, action, gas) if err != nil { @@ -799,6 +893,14 @@ func opCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta contract.Gas += returnGas evm.interpreter.intPool.put(name, value, inOffset, inSize, retOffset, retSize) + if evm.vmConfig.ContractLogFlag { + errmsg := "" + if err != nil { + errmsg = err.Error() + } + internalAction := &types.InternalAction{Action: action.NewRPCAction(0), ActionType: "call", GasUsed: gas - returnGas, GasLimit: gas, Depth: uint64(evm.depth), Error: errmsg} + evm.InternalTxs = append(evm.InternalTxs, internalAction) + } return ret, nil } @@ -810,7 +912,16 @@ func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack //addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() name, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() //addr, assetId,value, inOffset, inSize, retOffset, retSize := stack.pop(),stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() - toName, _ := common.BigToName(name) + //toName, _ := common.BigToName(name) + userID := name.Uint64() + acct, err := evm.AccountDB.GetAccountById(userID) + if err != nil || acct == nil { + stack.push(evm.interpreter.intPool.getZero()) + return nil, nil + } + + toName := acct.GetName() + value = math.U256(value) // Get arguments from the memory. args := memory.Get(inOffset.Int64(), inSize.Int64()) @@ -819,7 +930,7 @@ func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack gas += params.CallStipend } // todo - action := types.NewAction(types.Transfer, contract.Name(), toName, 0, evm.AssetID, gas, value, args) + action := types.NewAction(types.CallContract, contract.Name(), toName, 0, evm.AssetID, gas, value, args) ret, returnGas, err := evm.CallCode(contract, action, gas) if err != nil { @@ -833,6 +944,14 @@ func opCallCode(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack contract.Gas += returnGas evm.interpreter.intPool.put(name, value, inOffset, inSize, retOffset, retSize) + if evm.vmConfig.ContractLogFlag { + errmsg := "" + if err != nil { + errmsg = err.Error() + } + internalAction := &types.InternalAction{Action: action.NewRPCAction(0), ActionType: "callcode", GasUsed: gas - returnGas, GasLimit: gas, Depth: uint64(evm.depth), Error: errmsg} + evm.InternalTxs = append(evm.InternalTxs, internalAction) + } return ret, nil } @@ -842,11 +961,26 @@ func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, st gas := evm.callGasTemp // Pop other call parameters. name, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() - toName, _ := common.BigToName(name) + //change id to name + acct, err := evm.AccountDB.GetAccountById(name.Uint64()) + if err != nil || acct == nil { + stack.push(evm.interpreter.intPool.getZero()) + return nil, nil + } + // if err != nil { + // stack.push(evm.interpreter.intPool.getZero()) + // return nil, err + // } + // if acct == nil { + // stack.push(evm.interpreter.intPool.getZero()) + // return nil, fmt.Errorf("account is not exist") + // } + //toName, _ := common.BigToName(name) + // Get arguments from the memory. args := memory.Get(inOffset.Int64(), inSize.Int64()) - ret, returnGas, err := evm.DelegateCall(contract, toName, args, gas) + ret, returnGas, err := evm.DelegateCall(contract, acct.GetName(), args, gas) if err != nil { stack.push(evm.interpreter.intPool.getZero()) } else { @@ -864,32 +998,163 @@ func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, st //multi-asset //Increase asset already exist func opAddAsset(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - value, assetId := stack.pop(), stack.pop() + value, to, assetId := stack.pop(), stack.pop(), stack.pop() assetID := assetId.Uint64() + //toName, _ := common.BigToName(to) value = math.U256(value) + userID := to.Uint64() + acct, err := evm.AccountDB.GetAccountById(userID) + if err != nil || acct == nil { + stack.push(evm.interpreter.intPool.getZero()) + return nil, nil + } - err := execAddAsset(evm, contract, assetID, value) + err = execAddAsset(evm, contract, assetID, acct.GetName(), value) if err != nil { stack.push(evm.interpreter.intPool.getZero()) } else { stack.push(evm.interpreter.intPool.get().SetUint64(1)) } - return nil, err + return nil, nil } -func execAddAsset(evm *EVM, contract *Contract, assetID uint64, value *big.Int) error { - asset := &accountmanager.IncAsset{AssetId: assetID, Amount: value, To: contract.CallerName} +func execAddAsset(evm *EVM, contract *Contract, assetID uint64, toName common.Name, value *big.Int) error { + asset := &accountmanager.IncAsset{AssetId: assetID, Amount: value, To: toName} b, err := rlp.EncodeToBytes(asset) if err != nil { return err } - action := types.NewAction(types.IncreaseAsset, contract.CallerName, "", 0, 0, 0, big.NewInt(0), b) - err = evm.AccountDB.Process(action) + action := types.NewAction(types.IncreaseAsset, contract.CallerName, common.Name(evm.chainConfig.AccountName), 0, evm.chainConfig.SysTokenID, 0, big.NewInt(0), b) + + internalActions, err := evm.AccountDB.Process(&types.AccountManagerContext{Action: action, Number: evm.Context.BlockNumber.Uint64()}) + if evm.vmConfig.ContractLogFlag { + errmsg := "" + if err != nil { + errmsg = err.Error() + } + internalAction := &types.InternalAction{Action: action.NewRPCAction(0), ActionType: "addasset", GasUsed: 0, GasLimit: contract.Gas, Depth: uint64(evm.depth), Error: errmsg} + evm.InternalTxs = append(evm.InternalTxs, internalAction) + if len(internalActions) > 0 { + for _, iLog := range internalActions { + iLog.Depth = uint64(evm.depth) + } + evm.InternalTxs = append(evm.InternalTxs, internalActions...) + } + } return err } +func opDestroyAsset(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + value, assetID := stack.pop(), stack.pop() + astID := assetID.Uint64() + + action := types.NewAction(types.DestroyAsset, contract.CallerName, common.Name(evm.chainConfig.AccountName), 0, astID, 0, value, nil) + + internalActions, err := evm.AccountDB.Process(&types.AccountManagerContext{Action: action, Number: evm.Context.BlockNumber.Uint64()}) + if evm.vmConfig.ContractLogFlag { + errmsg := "" + if err != nil { + errmsg = err.Error() + } + internalAction := &types.InternalAction{Action: action.NewRPCAction(0), ActionType: "destroyasset", GasUsed: 0, GasLimit: contract.Gas, Depth: uint64(evm.depth), Error: errmsg} + evm.InternalTxs = append(evm.InternalTxs, internalAction) + if len(internalActions) > 0 { + for _, iLog := range internalActions { + iLog.Depth = uint64(evm.depth) + } + evm.InternalTxs = append(evm.InternalTxs, internalActions...) + } + } + + if err != nil { + stack.push(evm.interpreter.intPool.getZero()) + } else { + stack.push(evm.interpreter.intPool.get().SetUint64(astID)) + } + evm.interpreter.intPool.put(assetID) + return nil, nil +} + +func opGetAccountID(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + account := stack.pop() + name, _ := common.BigToName(account) + + if acct, err := evm.AccountDB.GetAccountByName(name); err == nil { + if acct != nil { + stack.push(evm.interpreter.intPool.get().SetUint64(acct.GetAccountID())) + } else { + stack.push(evm.interpreter.intPool.getZero()) + } + } else { + stack.push(evm.interpreter.intPool.getZero()) + } + + evm.interpreter.intPool.put(account) + return nil, nil +} + +// opCryptoCalc to encrypt or decrypt bytes +func opCryptoCalc(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + typeID, retOffset, retSize, offset2, size2, offset, size := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() + // + data := memory.Get(offset.Int64(), size.Int64()) + key := memory.Get(offset2.Int64(), size2.Int64()) + i := typeID.Uint64() + // + var ret = make([]byte, retSize.Int64()*32) + var datalen int + var err error + + //consume gas per byte + contract.Gas = contract.Gas - uint64(size.Int64())*params.GasTableInstanse.CryptoByte + + if i == 0 { + //Encrypt + ecdsapubkey, err := crypto.UnmarshalPubkey(key) + if err == nil { + eciespubkey := ecies.ImportECDSAPublic(ecdsapubkey) + ret, err = ecies.Encrypt(rand.Reader, eciespubkey, data, nil, nil) + if err == nil { + datalen = len(ret) + if uint64(datalen) > retSize.Uint64()*32 { + err = errors.New("Encrypt error") + } + } + + } + + } else if i == 1 { + ecdsaprikey, err := crypto.ToECDSA(key) + // + if err == nil { + eciesprikey := ecies.ImportECDSA(ecdsaprikey) + //ret, err = prv1.Decrypt(data, nil, nil) + ret, err = eciesprikey.Decrypt(data, nil, nil) + //pintg + if err == nil { + datalen = len(ret) + if uint64(datalen) > retSize.Uint64()*32 { + err = errors.New("Decrypt error") + } + } + } + } + + if err != nil { + stack.push(evm.interpreter.intPool.getZero()) + } else { + //write datalen data real length + stack.push(evm.interpreter.intPool.get().SetUint64(uint64(datalen))) + //write data + memory.Set(retOffset.Uint64(), uint64(datalen), ret) + } + + evm.interpreter.intPool.put(offset, size, offset2, size2, typeID) + return nil, nil +} + //issue an asset for multi-asset func opIssueAsset(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { offset, size := stack.pop(), stack.pop() @@ -897,63 +1162,92 @@ func opIssueAsset(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stac ret = bytes.TrimRight(ret, "\x00") desc := string(ret) - err := executeIssuseAsset(evm, contract, desc) + assetId, err := executeIssuseAsset(evm, contract, desc) if err != nil { stack.push(evm.interpreter.intPool.getZero()) } else { - stack.push(evm.interpreter.intPool.get().SetUint64(1)) + stack.push(evm.interpreter.intPool.get().SetUint64(assetId)) } evm.interpreter.intPool.put(offset, size) - return nil, err + return nil, nil } -func executeIssuseAsset(evm *EVM, contract *Contract, desc string) error { +func executeIssuseAsset(evm *EVM, contract *Contract, desc string) (uint64, error) { input := strings.Split(desc, ",") if len(input) != 7 { - return fmt.Errorf("invalid desc string") + return 0, fmt.Errorf("invalid desc string") } name := input[0] symbol := input[1] total, ifOK := new(big.Int).SetString(input[2], 10) if !ifOK { - return fmt.Errorf("amount not correct") + return 0, fmt.Errorf("amount not correct") } decimal, err := strconv.ParseUint(input[3], 10, 64) if err != nil { - return err + return 0, err } owner := common.Name(input[4]) limit, ifOK := new(big.Int).SetString(input[5], 10) if !ifOK { - return fmt.Errorf("amount not correct") + return 0, fmt.Errorf("amount not correct") } founder := common.Name(input[6]) asset := &asset.AssetObject{AssetName: name, Symbol: symbol, Amount: total, Owner: owner, Founder: founder, Decimals: decimal, UpperLimit: limit} b, err := rlp.EncodeToBytes(asset) if err != nil { - return err + return 0, err } - action := types.NewAction(types.IssueAsset, contract.CallerName, "", 0, 0, 0, big.NewInt(0), b) + action := types.NewAction(types.IssueAsset, contract.CallerName, common.Name(evm.chainConfig.AccountName), 0, evm.chainConfig.SysTokenID, 0, big.NewInt(0), b) - return evm.AccountDB.Process(action) + internalActions, err := evm.AccountDB.Process(&types.AccountManagerContext{Action: action, Number: evm.Context.BlockNumber.Uint64()}) + if err != nil { + return 0, err + } else { + assetInfo, err := evm.AccountDB.GetAssetInfoByName(name) + if err != nil || assetInfo == nil { + return 0, err + } else { + if evm.vmConfig.ContractLogFlag { + errmsg := "" + if err != nil { + errmsg = err.Error() + } + internalAction := &types.InternalAction{Action: action.NewRPCAction(0), ActionType: "issueasset", GasUsed: 0, GasLimit: contract.Gas, Depth: uint64(evm.depth), Error: errmsg} + evm.InternalTxs = append(evm.InternalTxs, internalAction) + if len(internalActions) > 0 { + for _, iLog := range internalActions { + iLog.Depth = uint64(evm.depth) + } + evm.InternalTxs = append(evm.InternalTxs, internalActions...) + } + } + return assetInfo.AssetId, nil + } + } } //issue an asset for multi-asset func opSetAssetOwner(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { newOwner, assetId := stack.pop(), stack.pop() - newOwnerName, _ := common.BigToName(newOwner) + //newOwnerName, _ := common.BigToName(newOwner) + userID := newOwner.Uint64() + acct, err := evm.AccountDB.GetAccountById(userID) + if err != nil || acct == nil { + stack.push(evm.interpreter.intPool.getZero()) + return nil, nil + } assetID := assetId.Uint64() - err := execSetAssetOwner(evm, contract, assetID, newOwnerName) + err = execSetAssetOwner(evm, contract, assetID, acct.GetName()) if err != nil { stack.push(evm.interpreter.intPool.getZero()) } else { stack.push(evm.interpreter.intPool.get().SetUint64(1)) } - //todo evm.interpreter.intPool.put(newOwner, assetId) - return nil, err + return nil, nil } func execSetAssetOwner(evm *EVM, contract *Contract, assetID uint64, owner common.Name) error { @@ -963,9 +1257,23 @@ func execSetAssetOwner(evm *EVM, contract *Contract, assetID uint64, owner commo return err } - action := types.NewAction(types.SetAssetOwner, contract.CallerName, "", 0, 0, 0, big.NewInt(0), b) - return evm.AccountDB.Process(action) - + action := types.NewAction(types.SetAssetOwner, contract.CallerName, common.Name(evm.chainConfig.AccountName), 0, evm.chainConfig.SysTokenID, 0, big.NewInt(0), b) + internalActions, err := evm.AccountDB.Process(&types.AccountManagerContext{Action: action, Number: evm.Context.BlockNumber.Uint64()}) + if evm.vmConfig.ContractLogFlag { + errmsg := "" + if err != nil { + errmsg = err.Error() + } + internalAction := &types.InternalAction{Action: action.NewRPCAction(0), ActionType: "setassetowner", GasUsed: 0, GasLimit: contract.Gas, Depth: uint64(evm.depth), Error: errmsg} + evm.InternalTxs = append(evm.InternalTxs, internalAction) + if len(internalActions) > 0 { + for _, iLog := range internalActions { + iLog.Depth = uint64(evm.depth) + } + evm.InternalTxs = append(evm.InternalTxs, internalActions...) + } + } + return err } func opCallEx(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { @@ -973,7 +1281,16 @@ func opCallEx(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S evm.interpreter.intPool.put(stack.pop()) gas := evm.callGasTemp name, assetId, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() - toName, _ := common.BigToName(name) + + //toName, _ := common.BigToName(name) + userID := name.Uint64() + acct, err := evm.AccountDB.GetAccountById(userID) + if err != nil || acct == nil { + stack.push(evm.interpreter.intPool.getZero()) + return nil, nil + } + toName := acct.GetName() + assetID := assetId.Uint64() value = math.U256(value) args := memory.Get(inOffset.Int64(), inSize.Int64()) @@ -982,7 +1299,7 @@ func opCallEx(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S gas += params.CallStipend } - action := types.NewAction(types.Transfer, contract.Name(), toName, 0, assetID, gas, value, args) + action := types.NewAction(types.CallContract, contract.Name(), toName, 0, assetID, gas, value, args) ret, returnGas, err := evm.Call(contract, action, gas) if err != nil { @@ -996,6 +1313,14 @@ func opCallEx(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S contract.Gas += returnGas evm.interpreter.intPool.put(name, value, inOffset, inSize, retOffset, retSize) + if evm.vmConfig.ContractLogFlag { + errmsg := "" + if err != nil { + errmsg = err.Error() + } + internalAction := &types.InternalAction{Action: action.NewRPCAction(0), ActionType: "transferex", GasUsed: 0, GasLimit: gas, Depth: uint64(evm.depth), Error: errmsg} + evm.InternalTxs = append(evm.InternalTxs, internalAction) + } return ret, nil } @@ -1005,7 +1330,16 @@ func opStaticCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stac gas := evm.callGasTemp // Pop other call parameters. name, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop() - toName, _ := common.BigToName(name) + + //toName, _ := common.BigToName(name) + userID := name.Uint64() + acct, err := evm.AccountDB.GetAccountById(userID) + if err != nil || acct == nil { + stack.push(evm.interpreter.intPool.getZero()) + return nil, nil + } + toName := acct.GetName() + // Get arguments from the memory. args := memory.Get(inOffset.Int64(), inSize.Int64()) @@ -1021,6 +1355,14 @@ func opStaticCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stac contract.Gas += returnGas evm.interpreter.intPool.put(name, inOffset, inSize, retOffset, retSize) + if evm.vmConfig.ContractLogFlag { + errmsg := "" + if err != nil { + errmsg = err.Error() + } + internalAction := &types.InternalAction{ActionType: "staticcall", GasUsed: gas - returnGas, GasLimit: gas, Depth: uint64(evm.depth), Error: errmsg} + evm.InternalTxs = append(evm.InternalTxs, internalAction) + } return ret, nil } @@ -1044,6 +1386,11 @@ func opStop(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta return nil, nil } +func opInvalid(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + log.Error("invalid opcode ") + return nil, nil +} + func opSuicide(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { //todo // contractCreater := common.BigToAddress(stack.pop()) diff --git a/processor/vm/interpreter.go b/processor/vm/interpreter.go index 73a4d703..baacbff8 100644 --- a/processor/vm/interpreter.go +++ b/processor/vm/interpreter.go @@ -44,10 +44,11 @@ type Config struct { // JumpTable contains the EVM instruction table. This // may be left uninitialised and will be set to the default // table. - JumpTable [256]operation + JumpTable [256]operation + ContractLogFlag bool } -// Interpreter is used to run Ethereum based contracts and will utilise the +// Interpreter is used to run based contracts and will utilise the // passed evmironment to query external sources for state information. // The Interpreter will run the byte code VM or JIT VM based on the passed // configuration. diff --git a/processor/vm/jump_table.go b/processor/vm/jump_table.go index 8f348cd4..718637a9 100644 --- a/processor/vm/jump_table.go +++ b/processor/vm/jump_table.go @@ -91,6 +91,14 @@ func NewByzantiumInstructionSet() [256]operation { instructionSet := NewHomesteadInstructionSet() //multi-asset InstructionSet + instructionSet[GETACCOUNTTIME] = operation{ + execute: opGetAccountTime, + gasCost: gasGetAccountTime, + validateStack: makeStackFunc(1, 1), + valid: true, + returns: true, + } + instructionSet[SNAPSHOTTIME] = operation{ execute: opGetSnapshotTime, gasCost: gasGetSnapshotTime, @@ -99,6 +107,22 @@ func NewByzantiumInstructionSet() [256]operation { returns: true, } + instructionSet[CRYPTOCALC] = operation{ + execute: opCryptoCalc, + gasCost: gasCryptoCalc, + validateStack: makeStackFunc(2, 1), + valid: true, + returns: true, + } + + instructionSet[GETDELEGATE] = operation{ + execute: opGetDelegate, + gasCost: gasGetDelegate, + validateStack: makeStackFunc(2, 1), + valid: true, + returns: true, + } + instructionSet[ASSETAMOUNT] = operation{ execute: opGetAssetAmount, gasCost: gasGetAssetAmount, @@ -133,13 +157,29 @@ func NewByzantiumInstructionSet() [256]operation { instructionSet[ISSUEASSET] = operation{ execute: opIssueAsset, - gasCost: gasCreate, + gasCost: gasIssueAsset, validateStack: makeStackFunc(1, 1), memorySize: memoryReturn, valid: true, returns: true, } + instructionSet[DESTROYASSET] = operation{ + execute: opDestroyAsset, + gasCost: gasDestroyAsset, + validateStack: makeStackFunc(1, 1), + valid: true, + returns: true, + } + + instructionSet[GETACCOUNTID] = operation{ + execute: opGetAccountID, + gasCost: gasGetAccountID, + validateStack: makeStackFunc(1, 1), + valid: true, + returns: true, + } + instructionSet[SETASSETOWNER] = operation{ execute: opSetAssetOwner, gasCost: gasSetAssetOwner, @@ -186,6 +226,14 @@ func NewByzantiumInstructionSet() [256]operation { reverts: true, returns: true, } + instructionSet[INVALID] = operation{ + execute: opInvalid, + gasCost: gasInvalid, + validateStack: makeStackFunc(0, 0), + valid: false, + reverts: true, + returns: true, + } return instructionSet } @@ -471,6 +519,12 @@ func NewFrontierInstructionSet() [256]operation { validateStack: makeStackFunc(0, 1), valid: true, }, + CALLASSETID: { + execute: opCallAssetId, + gasCost: constGasFunc(GasQuickStep), + validateStack: makeStackFunc(0, 1), + valid: true, + }, POP: { execute: opPop, gasCost: constGasFunc(GasQuickStep), diff --git a/processor/vm/memory.go b/processor/vm/memory.go index de5f4359..f9dc3a3e 100644 --- a/processor/vm/memory.go +++ b/processor/vm/memory.go @@ -18,7 +18,7 @@ package vm import "fmt" -// Memory implements a simple memory model for the ethereum virtual machine. +// Memory implements a simple memory model for the virtual machine. type Memory struct { store []byte lastGasCost uint64 diff --git a/processor/vm/opcodes.go b/processor/vm/opcodes.go index 398abc77..1347d435 100644 --- a/processor/vm/opcodes.go +++ b/processor/vm/opcodes.go @@ -97,6 +97,7 @@ const ( NUMBER DIFFICULTY GASLIMIT + CALLASSETID ) const ( @@ -209,6 +210,13 @@ const ( //snapshot num SNAPSHOTTIME = 0xc6 SNAPBALANCE = 0xc7 + // + DESTROYASSET = 0xc8 + GETACCOUNTID = 0xc9 + + GETDELEGATE = 0xca + GETACCOUNTTIME = 0xcb + CRYPTOCALC = 0xcc ) const ( @@ -220,7 +228,9 @@ const ( DELEGATECALL STATICCALL = 0xfa - REVERT = 0xfd + REVERT = 0xfd + ///< invalid instruction for expressing runtime errors (e.g., division-by-zero) + INVALID = 0xfe SELFDESTRUCT = 0xff ) @@ -277,12 +287,13 @@ var opCodeToString = map[OpCode]string{ RETURNDATACOPY: "RETURNDATACOPY", // 0x40 range - block operations - BLOCKHASH: "BLOCKHASH", - COINBASE: "COINBASE", - TIMESTAMP: "TIMESTAMP", - NUMBER: "NUMBER", - DIFFICULTY: "DIFFICULTY", - GASLIMIT: "GASLIMIT", + BLOCKHASH: "BLOCKHASH", + COINBASE: "COINBASE", + TIMESTAMP: "TIMESTAMP", + NUMBER: "NUMBER", + DIFFICULTY: "DIFFICULTY", + GASLIMIT: "GASLIMIT", + CALLASSETID: "CALLASSETID", // 0x50 range - 'storage' and execution POP: "POP", @@ -379,7 +390,7 @@ var opCodeToString = map[OpCode]string{ RETURN: "RETURN", CALLCODE: "CALLCODE", DELEGATECALL: "DELEGATECALL", - //add new asset for multi-asset + //0xc0 range add new asset for multi-asset BALANCEEX: "BALANCEEX", SETASSETOWNER: "SETASSETOWNER", ADDASSET: "ADDASSET", @@ -388,6 +399,7 @@ var opCodeToString = map[OpCode]string{ ASSETAMOUNT: "ASSETAMOUNT", SNAPBALANCE: "SNAPBALANCE", CALLEX: "CALLEX", + CRYPTOCALC: "CRYPTOCALC", //add end STATICCALL: "STATICCALL", REVERT: "REVERT", @@ -459,6 +471,7 @@ var stringToOp = map[string]OpCode{ "NUMBER": NUMBER, "DIFFICULTY": DIFFICULTY, "GASLIMIT": GASLIMIT, + "CALLASSETID": CALLASSETID, "POP": POP, "MLOAD": MLOAD, "MSTORE": MSTORE, diff --git a/processor/vm/runtime/contract/Asset/Asset.abi b/processor/vm/runtime/contract/Asset/Asset.abi index 0c0ed6ed..0653a58b 100644 --- a/processor/vm/runtime/contract/Asset/Asset.abi +++ b/processor/vm/runtime/contract/Asset/Asset.abi @@ -1 +1 @@ -[{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"},{"name":"assetId","type":"address"}],"name":"setname","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"desc","type":"string"}],"name":"reg","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"assetId","type":"address"}],"name":"getbalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"t","type":"uint256"},{"name":"time","type":"uint256"}],"name":"getSnapshotTime","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"assetId","type":"uint256"},{"name":"time","type":"uint256"}],"name":"getAssetAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"assetId","type":"address"},{"name":"value","type":"uint256"}],"name":"transAsset","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"assetId","type":"uint256"},{"name":"time","type":"uint256"}],"name":"getSnapBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"assetId","type":"address"},{"name":"value","type":"uint256"}],"name":"add","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}] +[{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"desc","type":"string"}],"name":"reg","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"assetId","type":"uint256"},{"name":"value","type":"uint256"}],"name":"transAsset","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"assetId","type":"uint256"}],"name":"getbalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"i","type":"uint256"},{"name":"t","type":"uint256"}],"name":"getSnapshotTime","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"},{"name":"assetId","type":"uint256"}],"name":"setname","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"assetId","type":"uint256"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"add","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"assetId","type":"uint256"},{"name":"t","type":"uint256"}],"name":"getAssetAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"getAssetId","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"assetId","type":"uint256"},{"name":"t","type":"uint256"}],"name":"getSnapBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}] \ No newline at end of file diff --git a/processor/vm/runtime/contract/Asset/Asset.bin b/processor/vm/runtime/contract/Asset/Asset.bin index a84b7d24..ec2d9bd3 100644 --- a/processor/vm/runtime/contract/Asset/Asset.bin +++ b/processor/vm/runtime/contract/Asset/Asset.bin @@ -1 +1 @@ -608060405234801561001057600080fd5b50600a60008190555061057e806100286000396000f300608060405260043610610099576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806318160ddd1461009e578063373db6e7146100c95780634708293e1461012c57806350ec07c3146101955780637a727a411461020c578063d639ec4214610257578063ef9ba08c146102a2578063f208415e14610302578063f5d82b6b1461036d575b600080fd5b3480156100aa57600080fd5b506100b36103ba565b6040518082815260200191505060405180910390f35b3480156100d557600080fd5b5061012a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506103c0565b005b34801561013857600080fd5b50610193600480360381019080803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050610405565b005b3480156101a157600080fd5b506101f6600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610426565b6040518082815260200191505060405180910390f35b34801561021857600080fd5b50610241600480360381019080803590602001909291908035906020019092919050505061045f565b6040518082815260200191505060405180910390f35b34801561026357600080fd5b5061028c600480360381019080803590602001909291908035906020019092919050505061047e565b6040518082815260200191505060405180910390f35b610300600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061049d565b005b34801561030e57600080fd5b50610357600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919080359060200190929190505050610503565b6040518082815260200191505060405180910390f35b34801561037957600080fd5b506103b8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610523565b005b60005481565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16c2158015610400573d6000803e3d6000fd5b505050565b8060200160405181900390c0158015610422573d6000803e3d6000fd5b5050565b60008273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16c3905092915050565b60008282c5158015610475573d6000803e3d6000fd5b50905092915050565b60008282c7158015610494573d6000803e3d6000fd5b50905092915050565b8273ffffffffffffffffffffffffffffffffffffffff166108fc8373ffffffffffffffffffffffffffffffffffffffff168391821502919060405160006040518083038186868a8ac49450505050501580156104fd573d6000803e3d6000fd5b50505050565b60008282c7158015610519573d6000803e3d6000fd5b5090509392505050565b8173ffffffffffffffffffffffffffffffffffffffff1681c115801561054d573d6000803e3d6000fd5b5050505600a165627a7a7230582066c0f473454398e8329129efd6ffe74c03d2c99b6af9bff1698952d6299457ae0029 +608060405234801561001057600080fd5b50600a60008190555061052d806100286000396000f3006080604052600436106100a4576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806318160ddd146100a95780634708293e146100d457806368669bc8146101515780636d1837e21461019b5780637a727a41146101fc578063a9a9591c14610247578063b9955e3914610294578063d639ec42146102eb578063d6e2280c14610336578063f208415e14610361575b600080fd5b3480156100b557600080fd5b506100be6103cc565b6040518082815260200191505060405180910390f35b3480156100e057600080fd5b5061013b600480360381019080803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506103d2565b6040518082815260200191505060405180910390f35b610199600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001909291905050506103e7565b005b3480156101a757600080fd5b506101e6600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610437565b6040518082815260200191505060405180910390f35b34801561020857600080fd5b50610231600480360381019080803590602001909291908035906020019092919050505061045a565b6040518082815260200191505060405180910390f35b34801561025357600080fd5b50610292600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610467565b005b3480156102a057600080fd5b506102e960048036038101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610496565b005b3480156102f757600080fd5b5061032060048036038101908080359060200190929190803590602001909291905050506104c7565b6040518082815260200191505060405180910390f35b34801561034257600080fd5b5061034b6104d4565b6040518082815260200191505060405180910390f35b34801561036d57600080fd5b506103b6600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001909291905050506104dc565b6040518082815260200191505060405180910390f35b60005481565b60008160200160405181900390c09050919050565b8273ffffffffffffffffffffffffffffffffffffffff166108fc838391821502919060405160006040518083038186868a8ac4945050505050158015610431573d6000803e3d6000fd5b50505050565b60008273ffffffffffffffffffffffffffffffffffffffff1682c3905092915050565b60008282c6905092915050565b808273ffffffffffffffffffffffffffffffffffffffff16c2158015610491573d6000803e3d6000fd5b505050565b828273ffffffffffffffffffffffffffffffffffffffff1682c11580156104c1573d6000803e3d6000fd5b50505050565b60008282c5905092915050565b600046905090565b60008373ffffffffffffffffffffffffffffffffffffffff168383c7905093925050505600a165627a7a723058201544c4304e893e9e0562168fc30a297842bc2c02e97895027ebab587362711e00029 \ No newline at end of file diff --git a/processor/vm/runtime/contract/Asset/Asset.sol b/processor/vm/runtime/contract/Asset/Asset.sol index a52aac95..2a12b665 100644 --- a/processor/vm/runtime/contract/Asset/Asset.sol +++ b/processor/vm/runtime/contract/Asset/Asset.sol @@ -7,19 +7,22 @@ contract Asset { totalSupply = 10; } - function reg(string desc) public { - issueasset(desc); + function reg(string desc) public returns(uint256){ + return issueasset(desc); } - function add(address assetId, uint256 value) public { - addasset(assetId,value); + function add(uint assetId, address to, uint256 value) public { + addasset(assetId,to,value); } - function transAsset(address to, address assetId, uint256 value) public payable { + function getAssetId() public returns (uint256) { + return msg.assetid; + } + function transAsset(address to, uint assetId, uint256 value) public payable { to.transferex(assetId, value); } - function setname(address newOwner, address assetId) public { + function setname(address newOwner, uint assetId) public { setassetowner(assetId, newOwner); } - function getbalance(address to, address assetId) public returns(uint) { + function getbalance(address to, uint assetId) public returns(uint256) { return to.balanceex(assetId); } function getAssetAmount(uint256 assetId, uint256 t) public returns (uint256){ diff --git a/processor/vm/runtime/contract/Ven/VEN.bin b/processor/vm/runtime/contract/Ven/VEN.bin index acd439b4..aedd5afb 100644 --- a/processor/vm/runtime/contract/Ven/VEN.bin +++ b/processor/vm/runtime/contract/Ven/VEN.bin @@ -1 +1 @@ -608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506120fe806100606000396000f3006080604052600436106100f1576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde0314610103578063095ea7b31461019357806313af4035146101f857806318160ddd1461023b57806323b872dd14610266578063313ce567146102eb5780633fb27b851461031c578063534eb1d414610333578063631f98521461036057806370a082311461038f5780637ba49b81146103e65780638da5cb5b1461044957806395d89b41146104a0578063a9059cbb14610530578063b5e7324914610595578063cae9ca51146105fe578063dd62ed3e146106a9575b3480156100fd57600080fd5b50600080fd5b34801561010f57600080fd5b50610118610720565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561015857808201518184015260208101905061013d565b50505050905090810190601f1680156101855780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561019f57600080fd5b506101de600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610759565b604051808215151515815260200191505060405180910390f35b34801561020457600080fd5b50610239600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061084b565b005b34801561024757600080fd5b506102506108e9565b6040518082815260200191505060405180910390f35b34801561027257600080fd5b506102d1600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610924565b604051808215151515815260200191505060405180910390f35b3480156102f757600080fd5b50610300610ce7565b604051808260ff1660ff16815260200191505060405180910390f35b34801561032857600080fd5b50610331610cec565b005b34801561033f57600080fd5b5061035e60048036038101908080359060200190929190505050610d53565b005b34801561036c57600080fd5b50610375610ea5565b604051808215151515815260200191505060405180910390f35b34801561039b57600080fd5b506103d0600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ee6565b6040518082815260200191505060405180910390f35b3480156103f257600080fd5b50610427600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061129d565b604051808263ffffffff1663ffffffff16815260200191505060405180910390f35b34801561045557600080fd5b5061045e6112f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156104ac57600080fd5b506104b561131e565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104f55780820151818401526020810190506104da565b50505050905090810190601f1680156105225780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561053c57600080fd5b5061057b600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611357565b604051808215151515815260200191505060405180910390f35b3480156105a157600080fd5b506105fc600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803515159060200190929190803563ffffffff169060200190929190505050611607565b005b34801561060a57600080fd5b5061068f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050611a2d565b604051808215151515815260200191505060405180910390f35b3480156106b557600080fd5b5061070a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611c7c565b6040518082815260200191505060405180910390f35b6040805190810160405280600d81526020017f5665436861696e20546f6b656e0000000000000000000000000000000000000081525081565b600081600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156108a657600080fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000600160000160009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16905090565b600061092e610ea5565b151561093957600080fd5b61094284611d03565b61094b83611d03565b81600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff1610158015610a43575081600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b8015610a4f5750600082115b15610cdb5781600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160008282829054906101000a90046dffffffffffffffffffffffffffff160392506101000a8154816dffffffffffffffffffffffffffff02191690836dffffffffffffffffffffffffffff16021790555081600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550610bf9610bf4600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff168461200190919063ffffffff16565b61201f565b600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a8154816dffffffffffffffffffffffffffff02191690836dffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610ce0565b600090505b9392505050565b601281565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610d4757600080fd5b610d51600061084b565b565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610dae57600080fd5b610dc38160045461200190919063ffffffff16565b600481905550610e17610e12600160000160009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff168361200190919063ffffffff16565b612044565b600160000160006101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055503073ffffffffffffffffffffffffffffffffffffffff1660007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a350565b6000806000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614905090565b6000806000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600e9054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff161415610fd257600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff169150611297565b600060045411156111a9576110a3600160000160109054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16611095600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600e9054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff1660045461206b90919063ffffffff16565b61209e90919063ffffffff16565b90506111a2600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600e9054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff16611194600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff168461200190919063ffffffff16565b61200190919063ffffffff16565b9150611297565b611294600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600e9054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff16600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff1661200190919063ffffffff16565b91505b50919050565b6000600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001601c9054906101000a900463ffffffff169050919050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6040805190810160405280600381526020017f56454e000000000000000000000000000000000000000000000000000000000081525081565b6000611361610ea5565b151561136c57600080fd5b61137533611d03565b61137e83611d03565b81600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff16101580156113fa5750600082115b156115fc5781600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160008282829054906101000a90046dffffffffffffffffffffffffffff160392506101000a8154816dffffffffffffffffffffffffffff02191690836dffffffffffffffffffffffffffff16021790555061151a611515600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff168461200190919063ffffffff16565b61201f565b600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a8154816dffffffffffffffffffffffffffff02191690836dffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050611601565b600090505b92915050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561166257600080fd5b81156117f1576116ef6116ea600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600e9054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff168561200190919063ffffffff16565b61201f565b600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600e6101000a8154816dffffffffffffffffffffffffffff02191690836dffffffffffffffffffffffffffff1602179055506117b16117ac600160000160109054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff168561200190919063ffffffff16565b612044565b600160000160106101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055506118ed565b611878611873600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff168561200190919063ffffffff16565b61201f565b600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a8154816dffffffffffffffffffffffffffff02191690836dffffffffffffffffffffffffffff1602179055505b80600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001601c6101000a81548163ffffffff021916908363ffffffff16021790555061199c611997600160000160009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff168561200190919063ffffffff16565b612044565b600160000160006101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055508373ffffffffffffffffffffffffffffffffffffffff1660007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a350505050565b600082600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925856040518082815260200191505060405180910390a38373ffffffffffffffffffffffffffffffffffffffff16638f4ffcb1338530866040518563ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360005b83811015611c0a578082015181840152602081019050611bef565b50505050905090810190601f168015611c375780820380516001836020036101000a031916815260200191505b5095505050505050600060405180830381600087803b158015611c5957600080fd5b505af1158015611c6d573d6000803e3d6000fd5b50505050600190509392505050565b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600080611d0e610ea5565b1515611d1957600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600e9054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff16141515611ffc57611d9883610ee6565b9150611e97600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600e9054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff16611e89600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff16856120b990919063ffffffff16565b6120b990919063ffffffff16565b9050611ea28261201f565b600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a8154816dffffffffffffffffffffffffffff02191690836dffffffffffffffffffffffffffff1602179055506000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600e6101000a8154816dffffffffffffffffffffffffffff02191690836dffffffffffffffffffffffffffff1602179055506000811115611ffb578273ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a35b5b505050565b600080828401905083811015151561201557fe5b8091505092915050565b600081826dffffffffffffffffffffffffffff1614151561203c57fe5b819050919050565b600081826fffffffffffffffffffffffffffffffff1614151561206357fe5b819050919050565b6000808284029050600084148061208c575082848281151561208957fe5b04145b151561209457fe5b8091505092915050565b60008082848115156120ac57fe5b0490508091505092915050565b60008282111515156120c757fe5b8183039050929150505600a165627a7a723058202ff76a5df53438c019cbf5aa49ea7ebb97fb08962bea8bacb83815c11e9440bc0029 \ No newline at end of file +608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506120fe806100606000396000f3006080604052600436106100f1576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde0314610103578063095ea7b31461019357806313af4035146101f857806318160ddd1461023b57806323b872dd14610266578063313ce567146102eb5780633fb27b851461031c578063534eb1d414610333578063631f98521461036057806370a082311461038f5780637ba49b81146103e65780638da5cb5b1461044957806395d89b41146104a0578063a9059cbb14610530578063b5e7324914610595578063cae9ca51146105fe578063dd62ed3e146106a9575b3480156100fd57600080fd5b50600080fd5b34801561010f57600080fd5b50610118610720565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561015857808201518184015260208101905061013d565b50505050905090810190601f1680156101855780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561019f57600080fd5b506101de600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610759565b604051808215151515815260200191505060405180910390f35b34801561020457600080fd5b50610239600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061084b565b005b34801561024757600080fd5b506102506108e9565b6040518082815260200191505060405180910390f35b34801561027257600080fd5b506102d1600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610924565b604051808215151515815260200191505060405180910390f35b3480156102f757600080fd5b50610300610ce7565b604051808260ff1660ff16815260200191505060405180910390f35b34801561032857600080fd5b50610331610cec565b005b34801561033f57600080fd5b5061035e60048036038101908080359060200190929190505050610d53565b005b34801561036c57600080fd5b50610375610ea5565b604051808215151515815260200191505060405180910390f35b34801561039b57600080fd5b506103d0600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610ee6565b6040518082815260200191505060405180910390f35b3480156103f257600080fd5b50610427600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061129d565b604051808263ffffffff1663ffffffff16815260200191505060405180910390f35b34801561045557600080fd5b5061045e6112f9565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156104ac57600080fd5b506104b561131e565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104f55780820151818401526020810190506104da565b50505050905090810190601f1680156105225780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561053c57600080fd5b5061057b600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050611357565b604051808215151515815260200191505060405180910390f35b3480156105a157600080fd5b506105fc600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803515159060200190929190803563ffffffff169060200190929190505050611607565b005b34801561060a57600080fd5b5061068f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050611a2d565b604051808215151515815260200191505060405180910390f35b3480156106b557600080fd5b5061070a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611c7c565b6040518082815260200191505060405180910390f35b6040805190810160405280600d81526020017f5665436861696e20546f6b656e0000000000000000000000000000000000000081525081565b600081600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156108a657600080fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000600160000160009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16905090565b600061092e610ea5565b151561093957600080fd5b61094284611d03565b61094b83611d03565b81600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff1610158015610a43575081600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410155b8015610a4f5750600082115b15610cdb5781600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160008282829054906101000a90046dffffffffffffffffffffffffffff160392506101000a8154816dffffffffffffffffffffffffffff02191690836dffffffffffffffffffffffffffff16021790555081600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282540392505081905550610bf9610bf4600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff168461200190919063ffffffff16565b61201f565b600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a8154816dffffffffffffffffffffffffffff02191690836dffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050610ce0565b600090505b9392505050565b601281565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610d4757600080fd5b610d51600061084b565b565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610dae57600080fd5b610dc38160045461200190919063ffffffff16565b600481905550610e17610e12600160000160009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff168361200190919063ffffffff16565b612044565b600160000160006101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055503073ffffffffffffffffffffffffffffffffffffffff1660007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a350565b6000806000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614905090565b6000806000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600e9054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff161415610fd257600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff169150611297565b600060045411156111a9576110a3600160000160109054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16611095600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600e9054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff1660045461206b90919063ffffffff16565b61209e90919063ffffffff16565b90506111a2600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600e9054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff16611194600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff168461200190919063ffffffff16565b61200190919063ffffffff16565b9150611297565b611294600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600e9054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff16600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff1661200190919063ffffffff16565b91505b50919050565b6000600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001601c9054906101000a900463ffffffff169050919050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6040805190810160405280600381526020017f56454e000000000000000000000000000000000000000000000000000000000081525081565b6000611361610ea5565b151561136c57600080fd5b61137533611d03565b61137e83611d03565b81600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff16101580156113fa5750600082115b156115fc5781600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160008282829054906101000a90046dffffffffffffffffffffffffffff160392506101000a8154816dffffffffffffffffffffffffffff02191690836dffffffffffffffffffffffffffff16021790555061151a611515600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff168461200190919063ffffffff16565b61201f565b600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a8154816dffffffffffffffffffffffffffff02191690836dffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a360019050611601565b600090505b92915050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561166257600080fd5b81156117f1576116ef6116ea600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600e9054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff168561200190919063ffffffff16565b61201f565b600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600e6101000a8154816dffffffffffffffffffffffffffff02191690836dffffffffffffffffffffffffffff1602179055506117b16117ac600160000160109054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff168561200190919063ffffffff16565b612044565b600160000160106101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055506118ed565b611878611873600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff168561200190919063ffffffff16565b61201f565b600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a8154816dffffffffffffffffffffffffffff02191690836dffffffffffffffffffffffffffff1602179055505b80600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001601c6101000a81548163ffffffff021916908363ffffffff16021790555061199c611997600160000160009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff168561200190919063ffffffff16565b612044565b600160000160006101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055508373ffffffffffffffffffffffffffffffffffffffff1660007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a350505050565b600082600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925856040518082815260200191505060405180910390a38373ffffffffffffffffffffffffffffffffffffffff16638f4ffcb1338530866040518563ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018481526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360005b83811015611c0a578082015181840152602081019050611bef565b50505050905090810190601f168015611c375780820380516001836020036101000a031916815260200191505b5095505050505050600060405180830381600087803b158015611c5957600080fd5b505af1158015611c6d573d6000803e3d6000fd5b50505050600190509392505050565b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600080611d0e610ea5565b1515611d1957600080fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600e9054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff16141515611ffc57611d9883610ee6565b9150611e97600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600e9054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff16611e89600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160009054906101000a90046dffffffffffffffffffffffffffff166dffffffffffffffffffffffffffff16856120b990919063ffffffff16565b6120b990919063ffffffff16565b9050611ea28261201f565b600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000160006101000a8154816dffffffffffffffffffffffffffff02191690836dffffffffffffffffffffffffffff1602179055506000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001600e6101000a8154816dffffffffffffffffffffffffffff02191690836dffffffffffffffffffffffffffff1602179055506000811115611ffb578273ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a35b5b505050565b600080828401905083811015151561201557fe5b8091505092915050565b600081826dffffffffffffffffffffffffffff1614151561203c57fe5b819050919050565b600081826fffffffffffffffffffffffffffffffff1614151561206357fe5b819050919050565b6000808284029050600084148061208c575082848281151561208957fe5b04145b151561209457fe5b8091505092915050565b60008082848115156120ac57fe5b0490508091505092915050565b60008282111515156120c757fe5b8183039050929150505600a165627a7a72305820d98b3e8d875a9111c363e3ed54adcdeca8e2b8ad2bd564788aad4821ff5bcb020029 \ No newline at end of file diff --git a/processor/vm/runtime/contract/Ven/BNB.sol b/processor/vm/runtime/contract/Ven/VEN.sol similarity index 96% rename from processor/vm/runtime/contract/Ven/BNB.sol rename to processor/vm/runtime/contract/Ven/VEN.sol index 3b88ca8e..edede68e 100644 --- a/processor/vm/runtime/contract/Ven/BNB.sol +++ b/processor/vm/runtime/contract/Ven/VEN.sol @@ -382,7 +382,7 @@ contract VENSale is Owned{ address venVault; // the account to keep non-public offered VEN tokens uint public constant startTime = 1503057600; // time to start sale - uint public constant endTime = 1812010800; // tiem to close sale + uint public constant endTime = 1504180800; // tiem to close sale uint public constant earlyStageLasts = 3 days; // early bird stage lasts in seconds bool initialized; diff --git a/processor/vm/runtime/contract/Ven/VENSale.bin b/processor/vm/runtime/contract/Ven/VENSale.bin index b33dd04d..269e6306 100644 --- a/processor/vm/runtime/contract/Ven/VENSale.bin +++ b/processor/vm/runtime/contract/Ven/VENSale.bin @@ -1 +1 @@ -608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060018060000160006101000a81548161ffff021916908361ffff160217905550611c66806100806000396000f300608060405260043610610107576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806313af403514610111578063165a0e031461015457806318160ddd1461017f5780633197cbb6146101aa5780633ba0b9a9146101d557806348b15166146102005780634bb278f3146102375780635e84d7231461024e57806369d4f28e1461027957806378e97925146102a457806384386004146102cf578063873c56d7146102fa5780638da5cb5b14610325578063a6f2ae3a1461037c578063ab96dd1814610386578063c040e6b8146103b1578063c0c53b8b146103ea578063de009e7f1461046d578063ff91b949146104ba575b61010f6104e5565b005b34801561011d57600080fd5b50610152600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109ca565b005b34801561016057600080fd5b50610169610a68565b6040518082815260200191505060405180910390f35b34801561018b57600080fd5b50610194610af9565b6040518082815260200191505060405180910390f35b3480156101b657600080fd5b506101bf610b09565b6040518082815260200191505060405180910390f35b3480156101e157600080fd5b506101ea610b11565b6040518082815260200191505060405180910390f35b34801561020c57600080fd5b50610215610b8e565b604051808263ffffffff1663ffffffff16815260200191505060405180910390f35b34801561024357600080fd5b5061024c610b96565b005b34801561025a57600080fd5b50610263610ec3565b6040518082815260200191505060405180910390f35b34801561028557600080fd5b5061028e610f47565b6040518082815260200191505060405180910390f35b3480156102b057600080fd5b506102b9610f80565b6040518082815260200191505060405180910390f35b3480156102db57600080fd5b506102e4610f88565b6040518082815260200191505060405180910390f35b34801561030657600080fd5b5061030f610fc1565b6040518082815260200191505060405180910390f35b34801561033157600080fd5b5061033a610fd0565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103846104e5565b005b34801561039257600080fd5b5061039b610ff5565b6040518082815260200191505060405180910390f35b3480156103bd57600080fd5b506103c6610ffc565b604051808260068111156103d657fe5b60ff16815260200191505060405180910390f35b3480156103f657600080fd5b5061046b600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506111c4565b005b34801561047957600080fd5b506104b8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061175c565b005b3480156104c657600080fd5b506104cf611add565b6040518082815260200191505060405180910390f35b60008060008060006104f633611b53565b15151561050257600080fd5b662386f26fc10000341015151561051857600080fd5b610520610b11565b945060008511151561053157600080fd5b610708600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16637ba49b81336040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b1580156105f157600080fd5b505af1158015610605573d6000803e3d6000fd5b505050506040513d602081101561061b57600080fd5b810190808051906020019092919050505063ffffffff160161063b610b8e565b63ffffffff161015151561064e57600080fd5b6801a055690d9db800003411156106825761067b856801a055690d9db80000611b8f90919063ffffffff16565b9350610698565b6106958534611b8f90919063ffffffff16565b93505b6106e7600160000160029054906101000a90046effffffffffffffffffffffffffffff166effffffffffffffffffffffffffffff166a353f43a026da2053240000611bc290919063ffffffff16565b9250828411156106f5578293505b6107088585611bdb90919063ffffffff16565b9150600084111561095d57600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b5e732493386600161075d610b8e565b6040518563ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001831515151581526020018263ffffffff1663ffffffff168152602001945050505050600060405180830381600087803b15801561080057600080fd5b505af1158015610814573d6000803e3d6000fd5b50505050600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc839081150290604051600060405180830381858888f19350505050158015610880573d6000803e3d6000fd5b506108cd6108c8600160000160029054906101000a90046effffffffffffffffffffffffffffff166effffffffffffffffffffffffffffff1686611bf690919063ffffffff16565b611c14565b600160000160026101000a8154816effffffffffffffffffffffffffffff02191690836effffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff167f4d1636c3e75537f2cbca3bdf3cf2c7825eb501463bc634e6923119dee5d1cc438584604051808381526020018281526020019250505060405180910390a25b6109708234611bc290919063ffffffff16565b905060008111156109c3573373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501580156109c1573d6000803e3d6000fd5b505b5050505050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610a2557600080fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6a353f43a026da2053240000606460166b033b2e3c9fd0803ce800000002811515610a8f57fe5b04606460056b033b2e3c9fd0803ce800000002811515610aab57fe5b04606460176b033b2e3c9fd0803ce800000002811515610ac757fe5b04606460096b033b2e3c9fd0803ce800000002811515610ae357fe5b040101016b033b2e3c9fd0803ce8000000030381565b6b033b2e3c9fd0803ce800000081565b636c01173081565b600060036006811115610b2057fe5b610b28610ffc565b6006811115610b3357fe5b1415610b56576064600f610dac02811515610b4a57fe5b04610dac019050610b8b565b60046006811115610b6357fe5b610b6b610ffc565b6006811115610b7657fe5b1415610b8657610dac9050610b8b565b600090505b90565b600042905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610bf357600080fd5b60056006811115610c0057fe5b610c08610ffc565b6006811115610c1357fe5b141515610c1f57600080fd5b610d26600160000160119054906101000a90046effffffffffffffffffffffffffffff166effffffffffffffffffffffffffffff16610d18600160000160029054906101000a90046effffffffffffffffffffffffffffff166effffffffffffffffffffffffffffff16606460166b033b2e3c9fd0803ce800000002811515610ca457fe5b04606460056b033b2e3c9fd0803ce800000002811515610cc057fe5b04606460176b033b2e3c9fd0803ce800000002811515610cdc57fe5b04606460096b033b2e3c9fd0803ce800000002811515610cf857fe5b040101016b033b2e3c9fd0803ce800000003611bc290919063ffffffff16565b611bc290919063ffffffff16565b90506000811115610ddb57600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663534eb1d4826040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b158015610dc257600080fd5b505af1158015610dd6573d6000803e3d6000fd5b505050505b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633fb27b856040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401600060405180830381600087803b158015610e6157600080fd5b505af1158015610e75573d6000803e3d6000fd5b505050506001600460156101000a81548160ff0219169083151502179055507f6c750432a9907ef296a47d6b38305b8ad777291aefc349e462a0a3285799425460405160405180910390a150565b606460166b033b2e3c9fd0803ce800000002811515610ede57fe5b04606460056b033b2e3c9fd0803ce800000002811515610efa57fe5b04606460176b033b2e3c9fd0803ce800000002811515610f1657fe5b04606460096b033b2e3c9fd0803ce800000002811515610f3257fe5b040101016b033b2e3c9fd0803ce80000000381565b6000600160000160029054906101000a90046effffffffffffffffffffffffffffff166effffffffffffffffffffffffffffff16905090565b635996d6c081565b6000600160000160119054906101000a90046effffffffffffffffffffffffffffff166effffffffffffffffffffffffffffff16905090565b6a353f43a026da205324000081565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6203f48081565b6000600460159054906101000a900460ff161561101c57600690506111c1565b600460149054906101000a900460ff16151561103b57600190506111c1565b635996d6c0611048610b8e565b63ffffffff16101561105d57600290506111c1565b606460166b033b2e3c9fd0803ce80000000281151561107857fe5b04606460056b033b2e3c9fd0803ce80000000281151561109457fe5b04606460176b033b2e3c9fd0803ce8000000028115156110b057fe5b04606460096b033b2e3c9fd0803ce8000000028115156110cc57fe5b040101016b033b2e3c9fd0803ce800000003611153600160000160119054906101000a90046effffffffffffffffffffffffffffff166effffffffffffffffffffffffffffff16600160000160029054906101000a90046effffffffffffffffffffffffffffff166effffffffffffffffffffffffffffff16611bf690919063ffffffff16565b10151561116357600590506111c1565b636c011730611170610b8e565b63ffffffff1610156111bc576111966203f480635996d6c0611bf690919063ffffffff16565b61119e610b8e565b63ffffffff1610156111b357600390506111c1565b600490506111c1565b600590505b90565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561121f57600080fd5b6001600681111561122c57fe5b611234610ffc565b600681111561123f57fe5b14151561124b57600080fd5b3073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b1580156112c657600080fd5b505af11580156112da573d6000803e3d6000fd5b505050506040513d60208110156112f057600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff1614151561132357600080fd5b60008273ffffffffffffffffffffffffffffffffffffffff161415151561134957600080fd5b60008173ffffffffffffffffffffffffffffffffffffffff161415151561136f57600080fd5b82600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b5e73249600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166114dc606460166b033b2e3c9fd0803ce8000000028115156114b157fe5b04606460056b033b2e3c9fd0803ce8000000028115156114cd57fe5b04611bf690919063ffffffff16565b60006114e6610b8e565b6040518563ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001831515151581526020018263ffffffff1663ffffffff168152602001945050505050600060405180830381600087803b15801561158957600080fd5b505af115801561159d573d6000803e3d6000fd5b50505050600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b5e73249600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1661164b606460176b033b2e3c9fd0803ce80000000281151561162057fe5b04606460096b033b2e3c9fd0803ce80000000281151561163c57fe5b04611bf690919063ffffffff16565b6001611655610b8e565b6040518563ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001831515151581526020018263ffffffff1663ffffffff168152602001945050505050600060405180830381600087803b1580156116f857600080fd5b505af115801561170c573d6000803e3d6000fd5b505050506001600460146101000a81548160ff0219169083151502179055507f1b0e25e2531ab5dbde6724b973ffb1819ece38c4a44247d242e5079db9ce461160405160405180910390a1505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156117b957600080fd5b6117c1610ffc565b9050600360068111156117d057fe5b8160068111156117dc57fe5b14806117fe5750600460068111156117f057fe5b8160068111156117fc57fe5b145b8061181f57506005600681111561181157fe5b81600681111561181d57fe5b145b151561182a57600080fd5b611876611871600160000160119054906101000a90046effffffffffffffffffffffffffffff166effffffffffffffffffffffffffffff1684611bf690919063ffffffff16565b611c14565b600160000160116101000a8154816effffffffffffffffffffffffffffff02191690836effffffffffffffffffffffffffffff1602179055506a353f43a026da2053240000606460166b033b2e3c9fd0803ce8000000028115156118d657fe5b04606460056b033b2e3c9fd0803ce8000000028115156118f257fe5b04606460176b033b2e3c9fd0803ce80000000281151561190e57fe5b04606460096b033b2e3c9fd0803ce80000000281151561192a57fe5b040101016b033b2e3c9fd0803ce80000000303600160000160119054906101000a90046effffffffffffffffffffffffffffff166effffffffffffffffffffffffffffff161115151561197c57600080fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b5e73249848460016119c6610b8e565b6040518563ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001831515151581526020018263ffffffff1663ffffffff168152602001945050505050600060405180830381600087803b158015611a6957600080fd5b505af1158015611a7d573d6000803e3d6000fd5b505050508273ffffffffffffffffffffffffffffffffffffffff167f4d1636c3e75537f2cbca3bdf3cf2c7825eb501463bc634e6923119dee5d1cc43836000604051808381526020018281526020019250505060405180910390a2505050565b606460166b033b2e3c9fd0803ce800000002811515611af857fe5b04606460056b033b2e3c9fd0803ce800000002811515611b1457fe5b04606460176b033b2e3c9fd0803ce800000002811515611b3057fe5b04606460096b033b2e3c9fd0803ce800000002811515611b4c57fe5b0401010181565b60008060008373ffffffffffffffffffffffffffffffffffffffff161415611b7e5760009150611b89565b823b90506000811191505b50919050565b60008082840290506000841480611bb05750828482811515611bad57fe5b04145b1515611bb857fe5b8091505092915050565b6000828211151515611bd057fe5b818303905092915050565b6000808284811515611be957fe5b0490508091505092915050565b6000808284019050838110151515611c0a57fe5b8091505092915050565b600081826effffffffffffffffffffffffffffff16141515611c3257fe5b8190509190505600a165627a7a72305820506bfe61c8d6a70469fddddac3928edff623cced30f7854e0db3d83f79a030090029 \ No newline at end of file +608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060018060000160006101000a81548161ffff021916908361ffff160217905550611c66806100806000396000f300608060405260043610610107576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806313af403514610111578063165a0e031461015457806318160ddd1461017f5780633197cbb6146101aa5780633ba0b9a9146101d557806348b15166146102005780634bb278f3146102375780635e84d7231461024e57806369d4f28e1461027957806378e97925146102a457806384386004146102cf578063873c56d7146102fa5780638da5cb5b14610325578063a6f2ae3a1461037c578063ab96dd1814610386578063c040e6b8146103b1578063c0c53b8b146103ea578063de009e7f1461046d578063ff91b949146104ba575b61010f6104e5565b005b34801561011d57600080fd5b50610152600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109ca565b005b34801561016057600080fd5b50610169610a68565b6040518082815260200191505060405180910390f35b34801561018b57600080fd5b50610194610af9565b6040518082815260200191505060405180910390f35b3480156101b657600080fd5b506101bf610b09565b6040518082815260200191505060405180910390f35b3480156101e157600080fd5b506101ea610b11565b6040518082815260200191505060405180910390f35b34801561020c57600080fd5b50610215610b8e565b604051808263ffffffff1663ffffffff16815260200191505060405180910390f35b34801561024357600080fd5b5061024c610b96565b005b34801561025a57600080fd5b50610263610ec3565b6040518082815260200191505060405180910390f35b34801561028557600080fd5b5061028e610f47565b6040518082815260200191505060405180910390f35b3480156102b057600080fd5b506102b9610f80565b6040518082815260200191505060405180910390f35b3480156102db57600080fd5b506102e4610f88565b6040518082815260200191505060405180910390f35b34801561030657600080fd5b5061030f610fc1565b6040518082815260200191505060405180910390f35b34801561033157600080fd5b5061033a610fd0565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103846104e5565b005b34801561039257600080fd5b5061039b610ff5565b6040518082815260200191505060405180910390f35b3480156103bd57600080fd5b506103c6610ffc565b604051808260068111156103d657fe5b60ff16815260200191505060405180910390f35b3480156103f657600080fd5b5061046b600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506111c4565b005b34801561047957600080fd5b506104b8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061175c565b005b3480156104c657600080fd5b506104cf611add565b6040518082815260200191505060405180910390f35b60008060008060006104f633611b53565b15151561050257600080fd5b662386f26fc10000341015151561051857600080fd5b610520610b11565b945060008511151561053157600080fd5b610708600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16637ba49b81336040518263ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050602060405180830381600087803b1580156105f157600080fd5b505af1158015610605573d6000803e3d6000fd5b505050506040513d602081101561061b57600080fd5b810190808051906020019092919050505063ffffffff160161063b610b8e565b63ffffffff161015151561064e57600080fd5b6801a055690d9db800003411156106825761067b856801a055690d9db80000611b8f90919063ffffffff16565b9350610698565b6106958534611b8f90919063ffffffff16565b93505b6106e7600160000160029054906101000a90046effffffffffffffffffffffffffffff166effffffffffffffffffffffffffffff166a353f43a026da2053240000611bc290919063ffffffff16565b9250828411156106f5578293505b6107088585611bdb90919063ffffffff16565b9150600084111561095d57600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b5e732493386600161075d610b8e565b6040518563ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001831515151581526020018263ffffffff1663ffffffff168152602001945050505050600060405180830381600087803b15801561080057600080fd5b505af1158015610814573d6000803e3d6000fd5b50505050600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc839081150290604051600060405180830381858888f19350505050158015610880573d6000803e3d6000fd5b506108cd6108c8600160000160029054906101000a90046effffffffffffffffffffffffffffff166effffffffffffffffffffffffffffff1686611bf690919063ffffffff16565b611c14565b600160000160026101000a8154816effffffffffffffffffffffffffffff02191690836effffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff167f4d1636c3e75537f2cbca3bdf3cf2c7825eb501463bc634e6923119dee5d1cc438584604051808381526020018281526020019250505060405180910390a25b6109708234611bc290919063ffffffff16565b905060008111156109c3573373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501580156109c1573d6000803e3d6000fd5b505b5050505050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610a2557600080fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6a353f43a026da2053240000606460166b033b2e3c9fd0803ce800000002811515610a8f57fe5b04606460056b033b2e3c9fd0803ce800000002811515610aab57fe5b04606460176b033b2e3c9fd0803ce800000002811515610ac757fe5b04606460096b033b2e3c9fd0803ce800000002811515610ae357fe5b040101016b033b2e3c9fd0803ce8000000030381565b6b033b2e3c9fd0803ce800000081565b636c01173081565b600060036006811115610b2057fe5b610b28610ffc565b6006811115610b3357fe5b1415610b56576064600f610dac02811515610b4a57fe5b04610dac019050610b8b565b60046006811115610b6357fe5b610b6b610ffc565b6006811115610b7657fe5b1415610b8657610dac9050610b8b565b600090505b90565b600042905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610bf357600080fd5b60056006811115610c0057fe5b610c08610ffc565b6006811115610c1357fe5b141515610c1f57600080fd5b610d26600160000160119054906101000a90046effffffffffffffffffffffffffffff166effffffffffffffffffffffffffffff16610d18600160000160029054906101000a90046effffffffffffffffffffffffffffff166effffffffffffffffffffffffffffff16606460166b033b2e3c9fd0803ce800000002811515610ca457fe5b04606460056b033b2e3c9fd0803ce800000002811515610cc057fe5b04606460176b033b2e3c9fd0803ce800000002811515610cdc57fe5b04606460096b033b2e3c9fd0803ce800000002811515610cf857fe5b040101016b033b2e3c9fd0803ce800000003611bc290919063ffffffff16565b611bc290919063ffffffff16565b90506000811115610ddb57600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663534eb1d4826040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b158015610dc257600080fd5b505af1158015610dd6573d6000803e3d6000fd5b505050505b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633fb27b856040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401600060405180830381600087803b158015610e6157600080fd5b505af1158015610e75573d6000803e3d6000fd5b505050506001600460156101000a81548160ff0219169083151502179055507f6c750432a9907ef296a47d6b38305b8ad777291aefc349e462a0a3285799425460405160405180910390a150565b606460166b033b2e3c9fd0803ce800000002811515610ede57fe5b04606460056b033b2e3c9fd0803ce800000002811515610efa57fe5b04606460176b033b2e3c9fd0803ce800000002811515610f1657fe5b04606460096b033b2e3c9fd0803ce800000002811515610f3257fe5b040101016b033b2e3c9fd0803ce80000000381565b6000600160000160029054906101000a90046effffffffffffffffffffffffffffff166effffffffffffffffffffffffffffff16905090565b635996d6c081565b6000600160000160119054906101000a90046effffffffffffffffffffffffffffff166effffffffffffffffffffffffffffff16905090565b6a353f43a026da205324000081565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6203f48081565b6000600460159054906101000a900460ff161561101c57600690506111c1565b600460149054906101000a900460ff16151561103b57600190506111c1565b635996d6c0611048610b8e565b63ffffffff16101561105d57600290506111c1565b606460166b033b2e3c9fd0803ce80000000281151561107857fe5b04606460056b033b2e3c9fd0803ce80000000281151561109457fe5b04606460176b033b2e3c9fd0803ce8000000028115156110b057fe5b04606460096b033b2e3c9fd0803ce8000000028115156110cc57fe5b040101016b033b2e3c9fd0803ce800000003611153600160000160119054906101000a90046effffffffffffffffffffffffffffff166effffffffffffffffffffffffffffff16600160000160029054906101000a90046effffffffffffffffffffffffffffff166effffffffffffffffffffffffffffff16611bf690919063ffffffff16565b10151561116357600590506111c1565b636c011730611170610b8e565b63ffffffff1610156111bc576111966203f480635996d6c0611bf690919063ffffffff16565b61119e610b8e565b63ffffffff1610156111b357600390506111c1565b600490506111c1565b600590505b90565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561121f57600080fd5b6001600681111561122c57fe5b611234610ffc565b600681111561123f57fe5b14151561124b57600080fd5b3073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b1580156112c657600080fd5b505af11580156112da573d6000803e3d6000fd5b505050506040513d60208110156112f057600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff1614151561132357600080fd5b60008273ffffffffffffffffffffffffffffffffffffffff161415151561134957600080fd5b60008173ffffffffffffffffffffffffffffffffffffffff161415151561136f57600080fd5b82600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b5e73249600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166114dc606460166b033b2e3c9fd0803ce8000000028115156114b157fe5b04606460056b033b2e3c9fd0803ce8000000028115156114cd57fe5b04611bf690919063ffffffff16565b60006114e6610b8e565b6040518563ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001831515151581526020018263ffffffff1663ffffffff168152602001945050505050600060405180830381600087803b15801561158957600080fd5b505af115801561159d573d6000803e3d6000fd5b50505050600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b5e73249600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1661164b606460176b033b2e3c9fd0803ce80000000281151561162057fe5b04606460096b033b2e3c9fd0803ce80000000281151561163c57fe5b04611bf690919063ffffffff16565b6001611655610b8e565b6040518563ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001831515151581526020018263ffffffff1663ffffffff168152602001945050505050600060405180830381600087803b1580156116f857600080fd5b505af115801561170c573d6000803e3d6000fd5b505050506001600460146101000a81548160ff0219169083151502179055507f1b0e25e2531ab5dbde6724b973ffb1819ece38c4a44247d242e5079db9ce461160405160405180910390a1505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156117b957600080fd5b6117c1610ffc565b9050600360068111156117d057fe5b8160068111156117dc57fe5b14806117fe5750600460068111156117f057fe5b8160068111156117fc57fe5b145b8061181f57506005600681111561181157fe5b81600681111561181d57fe5b145b151561182a57600080fd5b611876611871600160000160119054906101000a90046effffffffffffffffffffffffffffff166effffffffffffffffffffffffffffff1684611bf690919063ffffffff16565b611c14565b600160000160116101000a8154816effffffffffffffffffffffffffffff02191690836effffffffffffffffffffffffffffff1602179055506a353f43a026da2053240000606460166b033b2e3c9fd0803ce8000000028115156118d657fe5b04606460056b033b2e3c9fd0803ce8000000028115156118f257fe5b04606460176b033b2e3c9fd0803ce80000000281151561190e57fe5b04606460096b033b2e3c9fd0803ce80000000281151561192a57fe5b040101016b033b2e3c9fd0803ce80000000303600160000160119054906101000a90046effffffffffffffffffffffffffffff166effffffffffffffffffffffffffffff161115151561197c57600080fd5b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663b5e73249848460016119c6610b8e565b6040518563ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001831515151581526020018263ffffffff1663ffffffff168152602001945050505050600060405180830381600087803b158015611a6957600080fd5b505af1158015611a7d573d6000803e3d6000fd5b505050508273ffffffffffffffffffffffffffffffffffffffff167f4d1636c3e75537f2cbca3bdf3cf2c7825eb501463bc634e6923119dee5d1cc43836000604051808381526020018281526020019250505060405180910390a2505050565b606460166b033b2e3c9fd0803ce800000002811515611af857fe5b04606460056b033b2e3c9fd0803ce800000002811515611b1457fe5b04606460176b033b2e3c9fd0803ce800000002811515611b3057fe5b04606460096b033b2e3c9fd0803ce800000002811515611b4c57fe5b0401010181565b60008060008373ffffffffffffffffffffffffffffffffffffffff161415611b7e5760009150611b89565b823b90506000811191505b50919050565b60008082840290506000841480611bb05750828482811515611bad57fe5b04145b1515611bb857fe5b8091505092915050565b6000828211151515611bd057fe5b818303905092915050565b6000808284811515611be957fe5b0490508091505092915050565b6000808284019050838110151515611c0a57fe5b8091505092915050565b600081826effffffffffffffffffffffffffffff16141515611c3257fe5b8190509190505600a165627a7a723058207ff151982c527ec14ce88e9241d33f9b671a9c1ecaa16178d690ceef9c98d9b00029 \ No newline at end of file diff --git a/processor/vm/runtime/contract/crypto/crypto.sol b/processor/vm/runtime/contract/crypto/crypto.sol new file mode 100644 index 00000000..63528950 --- /dev/null +++ b/processor/vm/runtime/contract/crypto/crypto.sol @@ -0,0 +1,22 @@ +pragma solidity ^0.4.11; + +contract testencrypt { + + function myencode() external returns (uint256 len){ + bytes memory mycode = "Hello, world."; + bytes memory pubkey = "047db227d7094ce215c3a0f57e1bcc732551fe351f94249471934567e0f5dc1bf795962b8cccb87a2eb56b29fbe37d614e2f4c3c45b789ae4f1f51f4cb21972ffd"; + bytes memory aa = new bytes(10); + len = cryptocalc( mycode,pubkey,aa,0); + return len; + } + + function mydecode() external returns (uint256 len){ + bytes memory mycode = "Hello, world."; + bytes memory prikey = "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"; + bytes memory aa = new bytes(10); + len = cryptocalc( mycode,prikey,aa,1); + return len; + } + + +} diff --git a/processor/vm/runtime/contract/crypto/testcrypto.abi b/processor/vm/runtime/contract/crypto/testcrypto.abi new file mode 100644 index 00000000..bc5f2c7b --- /dev/null +++ b/processor/vm/runtime/contract/crypto/testcrypto.abi @@ -0,0 +1 @@ +[{"constant":false,"inputs":[],"name":"mydecode","outputs":[{"name":"ret","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"myencode","outputs":[{"name":"ret","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/processor/vm/runtime/contract/crypto/testcrypto.bin b/processor/vm/runtime/contract/crypto/testcrypto.bin new file mode 100644 index 00000000..ae8f946a --- /dev/null +++ b/processor/vm/runtime/contract/crypto/testcrypto.bin @@ -0,0 +1 @@ +60806040526040805190810160405280600d81526020017f48656c6c6f2c20776f726c642e000000000000000000000000000000000000008152506000908051906020019061004f929190610105565b5060c0604051908101604052806082815260200161049b608291396001908051906020019061007f929190610105565b50606060405190810160405280604081526020017f323839633238353764343539386533376662393634373530376534376133303981526020017f6436313333353339626632316138623963623664663838666435323332303332815250600290805190602001906100f2929190610105565b503480156100ff57600080fd5b506101aa565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061014657805160ff1916838001178555610174565b82800160010185558215610174579182015b82811115610173578251825591602001919060010190610158565b5b5090506101819190610185565b5090565b6101a791905b808211156101a357600081600090555060010161018b565b5090565b90565b6102e2806101b96000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806342d6c33514610051578063f1db5416146100e1575b600080fd5b34801561005d57600080fd5b50610066610171565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100a657808201518184015260208101905061008b565b50505050905090810190601f1680156100d35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156100ed57600080fd5b506100f6610176565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561013657808201518184015260208101905061011b565b50505050905090810190601f1680156101635780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b606090565b606060008054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561020e5780601f106101e35761010080835404028352916020019161020e565b820191906000526020600020905b8154815290600101906020018083116101f157829003601f168201915b505050505060018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102a95780601f1061027e576101008083540402835291602001916102a9565b820191906000526020600020905b81548152906001019060200180831161028c57829003601f168201915b50505050506000cb9050905600a165627a7a72305820b5e62e768b7e9dc1578783fa08247b0fa4949b31d4da49ae2c14576b8f94e979002930343764623232376437303934636532313563336130663537653162636337333235353166653335316639343234393437313933343536376530663564633162663739353936326238636363623837613265623536623239666265333764363134653266346333633435623738396165346631663531663463623231393732666664 \ No newline at end of file diff --git a/processor/vm/runtime/run_test.go b/processor/vm/runtime/run_test.go new file mode 100644 index 00000000..c1a28d4f --- /dev/null +++ b/processor/vm/runtime/run_test.go @@ -0,0 +1,115 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package runtime + +import ( + "fmt" + "math/big" + "testing" + + "github.com/fractalplatform/fractal/accountmanager" + "github.com/fractalplatform/fractal/common" + "github.com/fractalplatform/fractal/state" + "github.com/fractalplatform/fractal/types" + mdb "github.com/fractalplatform/fractal/utils/fdb/memdb" +) + +//TestRunCode run runtime code directly +func TestRunCode(t *testing.T) { + fmt.Println("in TestRunCode ...") + state, _ := state.New(common.Hash{}, state.NewDatabase(mdb.NewMemDatabase())) + account, _ := accountmanager.NewAccountManager(state) + fmt.Println("in TestRunCode2 ...") + //sender + senderName := common.Name("jacobwolf") + senderPubkey := common.HexToPubKey("12345") + // + receiverName := common.Name("denverfolk") + receiverPubkey := common.HexToPubKey("12345") + // + toName := common.Name("fractal.account") + + fmt.Println("in TestRunCode3 ...") + if err := account.CreateAccount(senderName, "", 0, 0, senderPubkey); err != nil { + fmt.Println("create sender account error\n", err) + return + } + + if err := account.CreateAccount(receiverName, "", 0, 0, receiverPubkey); err != nil { + fmt.Println("create receiver account error\n", err) + return + } + + action := issueAssetAction(senderName, toName) + if _, err := account.Process(&types.AccountManagerContext{Action: action, Number: 0}); err != nil { + fmt.Println("issue asset error\n", err) + return + } + + runtimeConfig := Config{ + Origin: senderName, + FromPubkey: senderPubkey, + State: state, + Account: account, + AssetID: 1, + GasLimit: 10000000000, + GasPrice: big.NewInt(0), + Value: big.NewInt(0), + BlockNumber: new(big.Int).SetUint64(0), + } + + var code = common.Hex2Bytes("60806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806342d6c33514610051578063f1db54161461007c575b600080fd5b34801561005d57600080fd5b506100666100a7565b6040518082815260200191505060405180910390f35b34801561008857600080fd5b506100916100b2565b6040518082815260200191505060405180910390f35b60006001cb80905090565b600060608060606040805190810160405280600d81526020017f48656c6c6f2c20776f726c642e00000000000000000000000000000000000000815250925060c06040519081016040528060828152602001610177608291399150600a6040519080825280601f01601f1916602001820160405280156101415781602001602082028038833980820191505090505b5090508280519060200183805190602001848051906020016000cc80601f01601f191660405101604052935083935050505090560030343764623232376437303934636532313563336130663537653162636337333235353166653335316639343234393437313933343536376530663564633162663739353936326238636363623837613265623536623239666265333764363134653266346333633435623738396165346631663531663463623231393732666664a165627a7a7230582058439b3945a21edad9fde8f2430422ca01d9a2c5092de618efa65db15f44d60e0029") + // myBinfile := "./contract/Ven/VEN.bin" + myAbifile := "./contract/crypto/testcrypto.abi" + + // myContractName := common.Name("mycontract") + + // err = createContract(myAbifile, myBinfile, venContractName, runtimeConfig) + // if err != nil { + // fmt.Println("create venContractAddress error") + // return + // } + + receiverAcct, err := account.GetAccountByName(receiverName) + if err != nil { + fmt.Println("GetAccountByName receiverAcct error") + return + } + + if receiverAcct != nil { + receiverAcct.SetCode(code) + account.SetAccount(receiverAcct) + } + + myInput, err := input(myAbifile, "mydecode") + //myInput, err := input(myAbifile, "myencode") + if err != nil { + fmt.Println("initialize myInput error ", err) + return + } + + action = types.NewAction(types.Transfer, runtimeConfig.Origin, receiverName, 0, runtimeConfig.AssetID, runtimeConfig.GasLimit, runtimeConfig.Value, myInput) + + ret, _, err := Call(action, &runtimeConfig) + if err != nil { + fmt.Println("call error ", err) + return + } + + fmt.Println("ret =", ret) + //go test -v -test.run TestRunCode +} diff --git a/processor/vm/runtime/runtime.go b/processor/vm/runtime/runtime.go index 19e35608..18c871a9 100644 --- a/processor/vm/runtime/runtime.go +++ b/processor/vm/runtime/runtime.go @@ -55,10 +55,7 @@ type Config struct { // sets defaults on the config func setDefaults(cfg *Config) { if cfg.ChainConfig == nil { - cfg.ChainConfig = ¶ms.ChainConfig{ - ContractChargeRatio: 80, - AssetChargeRatio: 80, - } + cfg.ChainConfig = params.DefaultChainconfig //cfg.ChainConfig = ¶ms.ChainConfig{ // ChainID: big.NewInt(1), // HomesteadBlock: new(big.Int), @@ -102,10 +99,12 @@ func NewEnv(cfg *Config) *vm.EVM { context := vm.Context{ //CanTransfer: vm.CanTransfer, //Transfer: vm.Transfer, - GetHash: func(uint64) common.Hash { return common.Hash{} }, + GetHash: func(uint64) common.Hash { return common.Hash{} }, + GetDelegatedByTime: func(string, uint64, *state.StateDB) (*big.Int, *big.Int, uint64, error) { + return big.NewInt(0), big.NewInt(0), 0, nil + }, Origin: cfg.Origin, From: cfg.Origin, - FromPubkey: cfg.FromPubkey, Coinbase: cfg.Coinbase, BlockNumber: cfg.BlockNumber, Time: cfg.Time, diff --git a/processor/vm/runtime/runtime_test.go b/processor/vm/runtime/runtime_test.go index c8027a12..b7882050 100644 --- a/processor/vm/runtime/runtime_test.go +++ b/processor/vm/runtime/runtime_test.go @@ -208,18 +208,18 @@ func TestAsset(t *testing.T) { receiverName := common.Name("denverfolk") receiverPubkey := common.HexToPubKey("12345") - if err := account.CreateAccount(senderName, "", 0, senderPubkey); err != nil { + if err := account.CreateAccount(senderName, "", 0, 0, senderPubkey); err != nil { fmt.Println("create sender account error", err) return } - if err := account.CreateAccount(receiverName, "", 0, receiverPubkey); err != nil { + if err := account.CreateAccount(receiverName, "", 0, 0, receiverPubkey); err != nil { fmt.Println("create receiver account error", err) return } action := issueAssetAction(senderName, receiverName) - if err := account.Process(action); err != nil { + if _, err := account.Process(&types.AccountManagerContext{Action: action, Number: 0}); err != nil { fmt.Println("issue asset error", err) return } @@ -239,7 +239,7 @@ func TestAsset(t *testing.T) { binfile := "./contract/Asset/Asset.bin" abifile := "./contract/Asset/Asset.abi" contractName := common.Name("assetcontract") - if err := account.CreateAccount(contractName, "", 0, receiverPubkey); err != nil { + if err := account.CreateAccount(contractName, "", 0, 0, receiverPubkey); err != nil { fmt.Println("create contract account error", err) return } @@ -257,11 +257,15 @@ func TestAsset(t *testing.T) { } action = types.NewAction(types.Transfer, runtimeConfig.Origin, contractName, 0, 1, runtimeConfig.GasLimit, runtimeConfig.Value, issuseAssetInput) - _, _, err = Call(action, &runtimeConfig) + ret, _, err := Call(action, &runtimeConfig) if err != nil { fmt.Println("call error ", err) return } + num := new(big.Int).SetBytes(ret) + if num.Cmp(big.NewInt(2)) != 0 { + t.Error("getBalance fail, want 2, get ", num) + } senderAcc, err := account.GetAccountByName(senderName) if err != nil { @@ -278,7 +282,7 @@ func TestAsset(t *testing.T) { fmt.Println("asset result ", b) } - addAssetInput, err := input(abifile, "add", common.BigToAddress(big.NewInt(2)), big.NewInt(210000)) + addAssetInput, err := input(abifile, "add", big.NewInt(2), common.BytesToAddress([]byte(senderName.String())), big.NewInt(210000)) if err != nil { fmt.Println("addAssetInput error ", err) return @@ -302,7 +306,7 @@ func TestAsset(t *testing.T) { fmt.Println("asset result ", b) } - transferExAssetInput, err := input(abifile, "transAsset", common.BytesToAddress([]byte(receiverName.String())), common.BigToAddress(big.NewInt(2)), big.NewInt(10000)) + transferExAssetInput, err := input(abifile, "transAsset", common.BytesToAddress([]byte(receiverName.String())), big.NewInt(2), big.NewInt(10000)) if err != nil { fmt.Println("transferExAssetInput error ", err) return @@ -339,7 +343,7 @@ func TestAsset(t *testing.T) { fmt.Println("asset receiver result ", b) } - setOwnerInput, err := input(abifile, "setname", common.BytesToAddress([]byte(receiverName.String())), common.BigToAddress(big.NewInt(2))) + setOwnerInput, err := input(abifile, "setname", common.BytesToAddress([]byte(receiverName.String())), big.NewInt(2)) if err != nil { fmt.Println("setOwnerInput error ", err) return @@ -353,22 +357,39 @@ func TestAsset(t *testing.T) { return } - getBalanceInput, err := input(abifile, "getbalance", common.BytesToAddress([]byte(receiverName.String())), common.BigToAddress(big.NewInt(2))) + getBalanceInput, err := input(abifile, "getbalance", common.BytesToAddress([]byte(receiverName.String())), big.NewInt(2)) if err != nil { fmt.Println("getBalanceInput error ", err) return } action = types.NewAction(types.Transfer, runtimeConfig.Origin, contractName, 0, runtimeConfig.AssetID, runtimeConfig.GasLimit, runtimeConfig.Value, getBalanceInput) - ret, _, err := Call(action, &runtimeConfig) + ret, _, err = Call(action, &runtimeConfig) if err != nil { fmt.Println("call error ", err) return } - num := new(big.Int).SetBytes(ret) + num = new(big.Int).SetBytes(ret) if num.Cmp(big.NewInt(10000)) != 0 { t.Error("getBalance fail, want 10000, get ", num) } + + getAssetIDInput, err := input(abifile, "getAssetId") + if err != nil { + fmt.Println("getBalanceInput error ", err) + return + } + action = types.NewAction(types.Transfer, runtimeConfig.Origin, contractName, 0, runtimeConfig.AssetID, runtimeConfig.GasLimit, runtimeConfig.Value, getAssetIDInput) + + ret, _, err = Call(action, &runtimeConfig) + if err != nil { + fmt.Println("call error ", err) + return + } + num = new(big.Int).SetBytes(ret) + if num.Cmp(big.NewInt(2)) != 0 { + t.Error("getBalance fail, want 10000, get ", num) + } } func TestBNB(t *testing.T) { state, _ := state.New(common.Hash{}, state.NewDatabase(mdb.NewMemDatabase())) @@ -380,18 +401,18 @@ func TestBNB(t *testing.T) { receiverName := common.Name("denverfolk") receiverPubkey := common.HexToPubKey("12345") - if err := account.CreateAccount(senderName, common.Name(""), 0, senderPubkey); err != nil { + if err := account.CreateAccount(senderName, common.Name(""), 0, 0, senderPubkey); err != nil { fmt.Println("create sender account error", err) return } - if err := account.CreateAccount(receiverName, common.Name(""), 0, receiverPubkey); err != nil { + if err := account.CreateAccount(receiverName, common.Name(""), 0, 0, receiverPubkey); err != nil { fmt.Println("create receiver account error", err) return } action := issueAssetAction(senderName, receiverName) - if err := account.Process(action); err != nil { + if _, err := account.Process(&types.AccountManagerContext{Action: action, Number: 0}); err != nil { fmt.Println("issue asset error", err) return } @@ -417,16 +438,16 @@ func TestBNB(t *testing.T) { ethvaultName := common.Name("ethvault") venvaultName := common.Name("venvault") - if err := account.CreateAccount(venContractName, common.Name(""), 0, receiverPubkey); err != nil { + if err := account.CreateAccount(venContractName, common.Name(""), 0, 0, receiverPubkey); err != nil { fmt.Println("create venContractName account error", err) return } - if err := account.CreateAccount(venSaleContractName, common.Name(""), 0, receiverPubkey); err != nil { + if err := account.CreateAccount(venSaleContractName, common.Name(""), 0, 0, receiverPubkey); err != nil { fmt.Println("create venSaleContractName account error", err) return } - account.CreateAccount(ethvaultName, common.Name(""), 0, senderPubkey) - account.CreateAccount(venvaultName, common.Name(""), 0, senderPubkey) + account.CreateAccount(ethvaultName, common.Name(""), 0, 0, senderPubkey) + account.CreateAccount(venvaultName, common.Name(""), 0, 0, senderPubkey) err := createContract(VenSaleAbifile, VenSaleBinfile, venSaleContractName, runtimeConfig) if err != nil { diff --git a/processor/vm/stack.go b/processor/vm/stack.go index 5a72e3cc..d33705bf 100644 --- a/processor/vm/stack.go +++ b/processor/vm/stack.go @@ -90,3 +90,15 @@ func (st *Stack) Print() { } fmt.Println("#############") } + +func (st *Stack) DebugPrint() { + fmt.Println("### stack ###") + if len(st.data) > 0 { + for i, val := range st.data { + fmt.Printf("%-3d %x\n", i, val) + } + } else { + fmt.Println("-- empty --") + } + fmt.Println("#############") +} diff --git a/processor/vm/vm.go b/processor/vm/vm.go index 5b0ffdba..f3449600 100644 --- a/processor/vm/vm.go +++ b/processor/vm/vm.go @@ -33,6 +33,10 @@ import ( type ( // GetHashFunc returns the nth block hash in the blockchain and is used by the BLOCKHASH EVM op code. GetHashFunc func(uint64) common.Hash + // GetDelegatedByTimeFunc returns the delegated balance + GetDelegatedByTimeFunc func(string, uint64, *state.StateDB) (*big.Int, *big.Int, uint64, error) + // GetHeaderByNumberFunc + GetHeaderByNumberFunc func(number uint64) *types.Header ) // Context provides the EVM with auxiliary information. Once provided @@ -41,12 +45,17 @@ type Context struct { // GetHash returns the hash corresponding to n GetHash GetHashFunc + // GetDelegatedByTime returns the delegated balance + GetDelegatedByTime GetDelegatedByTimeFunc + + //GetHeaderByNumber + GetHeaderByNumber GetHeaderByNumberFunc + // Message information - Origin common.Name // Provides information for ORIGIN - From common.Name // Provides information for ORIGIN - FromPubkey common.PubKey // provides from pubkey - AssetID uint64 // provides assetId - GasPrice *big.Int // Provides information for GASPRICE + Origin common.Name // Provides information for ORIGIN + From common.Name // Provides information for ORIGIN + AssetID uint64 // provides assetId + GasPrice *big.Int // Provides information for GASPRICE // Block information Coinbase common.Name // Provides information for COINBASE @@ -89,9 +98,22 @@ type EVM struct { // applied in opCall*. callGasTemp uint64 - FounderGasMap map[common.Name]int64 + FounderGasMap map[common.Name]DistributeGas + + InternalTxs []*types.InternalAction +} + +type DistributeGas struct { + Value int64 + TypeID uint64 } +const ( + AssetGas = 0 + ContractGas = 1 + CoinbaseGas = 2 +) + // NewEVM retutrns a new EVM . The returned EVM is not thread safe and should // only ever be used *once*. func NewEVM(ctx Context, accountdb *accountmanager.AccountManager, statedb *state.StateDB, chainCfg *params.ChainConfig, vmConfig Config) *EVM { @@ -103,7 +125,7 @@ func NewEVM(ctx Context, accountdb *accountmanager.AccountManager, statedb *stat vmConfig: vmConfig, } evm.interpreter = NewInterpreter(evm, vmConfig) - evm.FounderGasMap = map[common.Name]int64{} + evm.FounderGasMap = map[common.Name]DistributeGas{} return evm } @@ -167,17 +189,23 @@ func (evm *EVM) Call(caller ContractRef, action *types.Action, gas uint64) (ret return nil, gas, err } + var assetName common.Name assetFounder, _ := evm.AccountDB.GetAssetFounder(action.AssetID()) //get asset founder name - assetFounderRatio := evm.chainConfig.AssetChargeRatio //get asset founder charge ratio - contractFounder, _ := evm.AccountDB.GetFounder(toName) - if len(contractFounder.String()) == 0 { - contractFounder = toName - } - contratFounderRatio := evm.chainConfig.ContractChargeRatio - callerFounder, _ := evm.AccountDB.GetFounder(caller.Name()) - if len(callerFounder.String()) == 0 { - callerFounder = caller.Name() + + if len(assetFounder.String()) > 0 { + assetInfo, _ := evm.AccountDB.GetAssetInfoByID(action.AssetID()) + assetName = common.Name(assetInfo.GetAssetName()) } + + assetFounderRatio := evm.chainConfig.ChargeCfg.AssetRatio //get asset founder charge ratio + + // + contractName := toName + + contratFounderRatio := evm.chainConfig.ChargeCfg.ContractRatio + // + callerName := caller.Name() + // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. @@ -186,6 +214,9 @@ func (evm *EVM) Call(caller ContractRef, action *types.Action, gas uint64) (ret if err != nil { return nil, gas, err } + if acct == nil { + return nil, gas, ErrAccountNotExist + } codeHash, err := acct.GetCodeHash() if err != nil { return nil, gas, err @@ -208,30 +239,41 @@ func (evm *EVM) Call(caller ContractRef, action *types.Action, gas uint64) (ret ret, err = run(evm, contract, action.Data()) runGas := gas - contract.Gas - if runGas > 0 && len(contractFounder.String()) > 0 { - if _, ok := evm.FounderGasMap[contractFounder]; !ok { - evm.FounderGasMap[contractFounder] = int64(runGas * contratFounderRatio / 100) + if runGas > 0 && len(contractName.String()) > 0 { + if _, ok := evm.FounderGasMap[contractName]; !ok { + dGas := DistributeGas{int64(runGas * contratFounderRatio / 100), ContractGas} + evm.FounderGasMap[contractName] = dGas } else { - evm.FounderGasMap[contractFounder] += int64(runGas * contratFounderRatio / 100) + dGas := DistributeGas{int64(runGas * contratFounderRatio / 100), ContractGas} + dGas.Value = evm.FounderGasMap[contractName].Value + dGas.Value + evm.FounderGasMap[contractName] = dGas } } + if action.Value().Sign() != 0 && evm.depth != 0 { callValueGas := int64(params.CallValueTransferGas - contract.Gas) if callValueGas < 0 { callValueGas = 0 } - if len(assetFounder.String()) > 0 { - if _, ok := evm.FounderGasMap[assetFounder]; !ok { - evm.FounderGasMap[assetFounder] = int64(callValueGas * int64(assetFounderRatio) / 100) + if len(assetName.String()) > 0 { + if _, ok := evm.FounderGasMap[assetName]; !ok { + dGas := DistributeGas{int64(callValueGas * int64(assetFounderRatio) / 100), AssetGas} + evm.FounderGasMap[assetName] = dGas } else { - evm.FounderGasMap[assetFounder] += int64(callValueGas * int64(assetFounderRatio) / 100) + dGas := DistributeGas{int64(callValueGas * int64(assetFounderRatio) / 100), AssetGas} + dGas.Value = evm.FounderGasMap[assetName].Value + dGas.Value + evm.FounderGasMap[assetName] = dGas } } - if len(callerFounder.String()) > 0 { - if _, ok := evm.FounderGasMap[callerFounder]; !ok { - evm.FounderGasMap[callerFounder] = -int64(callValueGas * int64(assetFounderRatio) / 100) + if len(callerName.String()) > 0 { + if _, ok := evm.FounderGasMap[callerName]; !ok { + dGas := DistributeGas{-int64(callValueGas * int64(assetFounderRatio) / 100), AssetGas} + dGas.Value = evm.FounderGasMap[callerName].Value - dGas.Value + evm.FounderGasMap[callerName] = dGas } else { - evm.FounderGasMap[callerFounder] -= int64(callValueGas * int64(assetFounderRatio) / 100) + dGas := DistributeGas{int64(callValueGas * int64(assetFounderRatio) / 100), AssetGas} + dGas.Value = evm.FounderGasMap[callerName].Value - dGas.Value + evm.FounderGasMap[callerName] = dGas } } } @@ -248,8 +290,9 @@ func (evm *EVM) Call(caller ContractRef, action *types.Action, gas uint64) (ret actualUsedGas := gas - contract.Gas if evm.depth == 0 && actualUsedGas != runGas { - for _, gas := range evm.FounderGasMap { - gas = (gas / int64(runGas)) * int64(actualUsedGas) + for name, gas := range evm.FounderGasMap { + v := DistributeGas{(gas.Value / int64(runGas)) * int64(actualUsedGas), gas.TypeID} + evm.FounderGasMap[name] = v } } @@ -303,18 +346,24 @@ func (evm *EVM) CallCode(caller ContractRef, action *types.Action, gas uint64) ( ret, err = run(evm, contract, action.Data()) runGas := gas - contract.Gas + var contractName common.Name contractFounder, _ := evm.AccountDB.GetFounder(toName) if len(contractFounder.String()) == 0 { - contractFounder = toName + contractName = toName } - contratFounderRatio := evm.chainConfig.ContractChargeRatio - if runGas > 0 && len(contractFounder.String()) > 0 { - if _, ok := evm.FounderGasMap[contractFounder]; !ok { - evm.FounderGasMap[contractFounder] = int64(runGas * contratFounderRatio / 100) + + contratFounderRatio := evm.chainConfig.ChargeCfg.ContractRatio + if runGas > 0 && len(contractName.String()) > 0 { + if _, ok := evm.FounderGasMap[contractName]; !ok { + dGas := DistributeGas{int64(runGas * contratFounderRatio / 100), ContractGas} + evm.FounderGasMap[contractName] = dGas } else { - evm.FounderGasMap[contractFounder] += int64(runGas * contratFounderRatio / 100) + dGas := DistributeGas{int64(runGas * contratFounderRatio / 100), ContractGas} + dGas.Value = evm.FounderGasMap[contractName].Value + dGas.Value + evm.FounderGasMap[contractName] = dGas } } + if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != errExecutionReverted { @@ -361,16 +410,21 @@ func (evm *EVM) DelegateCall(caller ContractRef, name common.Name, input []byte, ret, err = run(evm, contract, input) runGas := gas - contract.Gas + var contractName common.Name contractFounder, _ := evm.AccountDB.GetFounder(name) if len(contractFounder.String()) == 0 { - contractFounder = name + contractName = name } - contratFounderRatio := evm.chainConfig.ContractChargeRatio - if runGas > 0 && len(contractFounder.String()) > 0 { - if _, ok := evm.FounderGasMap[contractFounder]; !ok { - evm.FounderGasMap[contractFounder] = int64(runGas * contratFounderRatio / 100) + + contratFounderRatio := evm.chainConfig.ChargeCfg.ContractRatio + if runGas > 0 && len(contractName.String()) > 0 { + if _, ok := evm.FounderGasMap[contractName]; !ok { + dGas := DistributeGas{int64(runGas * contratFounderRatio / 100), ContractGas} + evm.FounderGasMap[contractName] = dGas } else { - evm.FounderGasMap[contractFounder] += int64(runGas * contratFounderRatio / 100) + dGas := DistributeGas{int64(runGas * contratFounderRatio / 100), ContractGas} + dGas.Value = evm.FounderGasMap[contractName].Value + dGas.Value + evm.FounderGasMap[contractName] = dGas } } @@ -430,18 +484,24 @@ func (evm *EVM) StaticCall(caller ContractRef, name common.Name, input []byte, g ret, err = run(evm, contract, input) runGas := gas - contract.Gas + var contractName common.Name contractFounder, _ := evm.AccountDB.GetFounder(to.Name()) if len(contractFounder.String()) == 0 { - contractFounder = to.Name() + contractName = to.Name() } - contratFounderRatio := evm.chainConfig.ContractChargeRatio - if runGas > 0 && len(contractFounder.String()) > 0 { - if _, ok := evm.FounderGasMap[contractFounder]; !ok { - evm.FounderGasMap[contractFounder] = int64(runGas * contratFounderRatio / 100) + + contratFounderRatio := evm.chainConfig.ChargeCfg.ContractRatio + if runGas > 0 && len(contractName.String()) > 0 { + if _, ok := evm.FounderGasMap[contractName]; !ok { + dGas := DistributeGas{int64(runGas * contratFounderRatio / 100), ContractGas} + evm.FounderGasMap[contractName] = dGas } else { - evm.FounderGasMap[contractFounder] += int64(runGas * contratFounderRatio / 100) + dGas := DistributeGas{int64(runGas * contratFounderRatio / 100), ContractGas} + dGas.Value = evm.FounderGasMap[contractName].Value + dGas.Value + evm.FounderGasMap[contractName] = dGas } } + if err != nil { evm.StateDB.RevertToSnapshot(snapshot) if err != errExecutionReverted { @@ -466,9 +526,11 @@ func (evm *EVM) Create(caller ContractRef, action *types.Action, gas uint64) (re contractName := action.Recipient() snapshot := evm.StateDB.Snapshot() - // if err := evm.AccountDB.CreateAccount(contractName, evm.FromPubkey); err != nil { - // return nil, 0, err - // } + if b, err := evm.AccountDB.AccountHaveCode(contractName); err != nil { + return nil, 0, err + } else if b == true { + return nil, 0, ErrContractCodeCollision + } if err := evm.AccountDB.TransferAsset(action.Sender(), action.Recipient(), evm.AssetID, action.Value()); err != nil { evm.StateDB.RevertToSnapshot(snapshot) @@ -491,6 +553,19 @@ func (evm *EVM) Create(caller ContractRef, action *types.Action, gas uint64) (re start := time.Now() ret, err = run(evm, contract, nil) + runGas := gas - contract.Gas + + contratFounderRatio := evm.chainConfig.ChargeCfg.ContractRatio + if runGas > 0 && len(contractName.String()) > 0 { + if _, ok := evm.FounderGasMap[contractName]; !ok { + dGas := DistributeGas{int64(runGas * contratFounderRatio / 100), ContractGas} + evm.FounderGasMap[contractName] = dGas + } else { + dGas := DistributeGas{int64(runGas * contratFounderRatio / 100), ContractGas} + dGas.Value = evm.FounderGasMap[contractName].Value + dGas.Value + evm.FounderGasMap[contractName] = dGas + } + } // check whether the max code size has been exceeded //maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize diff --git a/rawdb/accessors_chain.go b/rawdb/accessors_chain.go index 2718b4a3..d8e61a66 100644 --- a/rawdb/accessors_chain.go +++ b/rawdb/accessors_chain.go @@ -77,35 +77,38 @@ func WriteHeadHeaderHash(db DatabaseWriter, hash common.Hash) { } } -// ReadHeadBlockHash retrieves the hash of the current canonical head block. -func ReadHeadBlockHash(db DatabaseReader) common.Hash { - data, _ := db.Get(headBlockKey) +// ReadIrreversibleNumber retrieves the irreversible number of chain. +func ReadIrreversibleNumber(db DatabaseReader) uint64 { + data, err := db.Get(irreversibleNumberKey) + if err != nil { + log.Crit("Failed to get irreversible number ", "err", err) + } if len(data) == 0 { - return common.Hash{} + return 0 } - return common.BytesToHash(data) + return decodeBlockNumber(data) } -// WriteHeadBlockHash stores the head block's hash. -func WriteHeadBlockHash(db DatabaseWriter, hash common.Hash) { - if err := db.Put(headBlockKey, hash.Bytes()); err != nil { - log.Crit("Failed to store last block's hash", "err", err) +// WriteIrreversibleNumber stores the irreversible number of chain. +func WriteIrreversibleNumber(db DatabaseWriter, number uint64) { + if err := db.Put(irreversibleNumberKey, encodeBlockNumber(number)); err != nil { + log.Crit("Failed to store irreversible number ", "err", err) } } -// ReadHeadFastBlockHash retrieves the hash of the current fast-sync head block. -func ReadHeadFastBlockHash(db DatabaseReader) common.Hash { - data, _ := db.Get(headFastBlockKey) +// ReadHeadBlockHash retrieves the hash of the current canonical head block. +func ReadHeadBlockHash(db DatabaseReader) common.Hash { + data, _ := db.Get(headBlockKey) if len(data) == 0 { return common.Hash{} } return common.BytesToHash(data) } -// WriteHeadFastBlockHash stores the hash of the current fast-sync head block. -func WriteHeadFastBlockHash(db DatabaseWriter, hash common.Hash) { - if err := db.Put(headFastBlockKey, hash.Bytes()); err != nil { - log.Crit("Failed to store last fast block's hash", "err", err) +// WriteHeadBlockHash stores the head block's hash. +func WriteHeadBlockHash(db DatabaseWriter, hash common.Hash) { + if err := db.Put(headBlockKey, hash.Bytes()); err != nil { + log.Crit("Failed to store last block's hash", "err", err) } } @@ -249,6 +252,7 @@ func WriteBlock(db DatabaseWriter, block *types.Block) { // DeleteBlock removes all block data associated with a hash. func DeleteBlock(db DatabaseDeleter, hash common.Hash, number uint64) { DeleteReceipts(db, hash, number) + DeleteDetailTxs(db, hash, number) DeleteHeader(db, hash, number) DeleteBody(db, hash, number) DeleteTd(db, hash, number) @@ -330,6 +334,50 @@ func DeleteReceipts(db DatabaseDeleter, hash common.Hash, number uint64) { } } +// ReadDetailTxs retrieves all the contract log belonging to a block. +func ReadDetailTxs(db DatabaseReader, hash common.Hash, number uint64) []*types.DetailTx { + // Retrieve the flattened receipt slice + data, _ := db.Get(blockDetailTxsKey(number, hash)) + if len(data) == 0 { + return nil + } + // Convert the revceipts from their storage form to their internal representation + storageDetailTxs := []*types.DetailTx{} + if err := rlp.DecodeBytes(data, &storageDetailTxs); err != nil { + fmt.Println("Invalid detailtxs array RLP", "hash", hash.String(), "err", err) + return nil + } + detailtxs := make([]*types.DetailTx, len(storageDetailTxs)) + for i, detailtx := range storageDetailTxs { + detailtxs[i] = (*types.DetailTx)(detailtx) + } + return detailtxs +} + +// WriteDetailTxs stores all the contract log belonging to a block. +func WriteDetailTxs(db DatabaseWriter, hash common.Hash, number uint64, dtxs []*types.DetailTx) { + // Convert the receipts into their storage form and serialize them + storageDetailTxs := make([]*types.DetailTx, len(dtxs)) + for i, dtx := range dtxs { + storageDetailTxs[i] = (*types.DetailTx)(dtx) + } + bytes, err := rlp.EncodeToBytes(storageDetailTxs) + if err != nil { + log.Crit("Failed to encode block detailtxs", "err", err) + } + // Store the flattened receipt slice + if err := db.Put(blockDetailTxsKey(number, hash), bytes); err != nil { + log.Crit("Failed to store block detailtxs", "err", err) + } +} + +// DeleteDetailTxs removes all contract log data associated with a block hash. +func DeleteDetailTxs(db DatabaseDeleter, hash common.Hash, number uint64) { + if err := db.Delete(blockDetailTxsKey(number, hash)); err != nil { + log.Crit("Failed to delete block detailtxs", "err", err) + } +} + // FindCommonAncestor returns the last common ancestor of two block headers func FindCommonAncestor(db DatabaseReader, a, b *types.Header) *types.Header { for bn := b.Number.Uint64(); a.Number.Uint64() > bn; { diff --git a/rawdb/accessors_chain_test.go b/rawdb/accessors_chain_test.go index ca73b987..e6a237d4 100644 --- a/rawdb/accessors_chain_test.go +++ b/rawdb/accessors_chain_test.go @@ -225,7 +225,6 @@ func TestHeadStorage(t *testing.T) { Head: &types.Header{Extra: []byte("test block header")}, } blockFull := &types.Block{Head: &types.Header{Extra: []byte("test block full"), Coinbase: "coinbase"}} - blockFast := types.Block{Head: &types.Header{Extra: []byte("test block fast"), Coinbase: "coinbase"}} // Check that no head entries are in a pristine database if entry := ReadHeadHeaderHash(db); entry != (common.Hash{}) { @@ -234,13 +233,10 @@ func TestHeadStorage(t *testing.T) { if entry := ReadHeadBlockHash(db); entry != (common.Hash{}) { t.Fatalf("Non head block entry returned: %v", entry) } - if entry := ReadHeadFastBlockHash(db); entry != (common.Hash{}) { - t.Fatalf("Non fast head block entry returned: %v", entry) - } + // Assign separate entries for the head header and block WriteHeadHeaderHash(db, blockHead.Hash()) WriteHeadBlockHash(db, blockFull.Hash()) - WriteHeadFastBlockHash(db, blockFast.Hash()) // Check that both heads are present, and different (i.e. two heads maintained) if entry := ReadHeadHeaderHash(db); entry != blockHead.Hash() { @@ -249,9 +245,7 @@ func TestHeadStorage(t *testing.T) { if entry := ReadHeadBlockHash(db); entry != blockFull.Hash() { t.Fatalf("Head block hash mismatch: have %v, want %v", entry, blockFull.Hash()) } - if entry := ReadHeadFastBlockHash(db); entry != blockFast.Hash() { - t.Fatalf("Fast head block hash mismatch: have %v, want %v", entry, blockFast.Hash()) - } + } // Tests that receipts associated with a single block can be stored and retrieved. @@ -308,3 +302,16 @@ func TestBlockReceiptStorage(t *testing.T) { t.Fatalf("deleted receipts returned: %v", rs) } } + +func TestIrreversibleNumberStore(t *testing.T) { + db := mdb.NewMemDatabase() + + number := uint64(100) + + WriteIrreversibleNumber(db, number) + + if n := ReadIrreversibleNumber(db); n != number { + t.Fatalf("number mismatch: have %v, want %v", n, number) + } + +} diff --git a/rawdb/accessors_metadata.go b/rawdb/accessors_metadata.go index f158bf22..fef258a0 100644 --- a/rawdb/accessors_metadata.go +++ b/rawdb/accessors_metadata.go @@ -22,27 +22,8 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/fractalplatform/fractal/common" "github.com/fractalplatform/fractal/params" - "github.com/fractalplatform/fractal/utils/rlp" ) -// ReadDatabaseVersion retrieves the version number of the database. -func ReadDatabaseVersion(db DatabaseReader) int { - var version int - - enc, _ := db.Get(databaseVerisionKey) - rlp.DecodeBytes(enc, &version) - - return version -} - -// WriteDatabaseVersion stores the version number of the database -func WriteDatabaseVersion(db DatabaseWriter, version int) { - enc, _ := rlp.EncodeToBytes(version) - if err := db.Put(databaseVerisionKey, enc); err != nil { - log.Crit("Failed to store the database version", "err", err) - } -} - // ReadChainConfig retrieves the consensus settings based on the given genesis hash. func ReadChainConfig(db DatabaseReader, hash common.Hash) *params.ChainConfig { data, _ := db.Get(configKey(hash)) diff --git a/rawdb/schema.go b/rawdb/schema.go index c1eca4de..3352fb5d 100644 --- a/rawdb/schema.go +++ b/rawdb/schema.go @@ -25,26 +25,23 @@ import ( // The fields below define the low level database schema prefixing. var ( - // databaseVerisionKey tracks the current database version. - databaseVerisionKey = []byte("DatabaseVersion") - + //irreversibleNumberKey tracks the blcokchain irreversible number + irreversibleNumberKey = []byte("irreversibleNumber") // headHeaderKey tracks the latest know header's hash. headHeaderKey = []byte("LastHeader") // headBlockKey tracks the latest know full block's hash. headBlockKey = []byte("LastBlock") - // headFastBlockKey tracks the latest known incomplete block's hash duirng fast sync. - headFastBlockKey = []byte("LastFast") - // Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes). headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td headerHashSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + headerHashSuffix -> hash headerNumberPrefix = []byte("H") // headerNumberPrefix + hash -> num (uint64 big endian) - blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body - blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts + blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body + blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts + blockDetailTxsPrefix = []byte("d") txLookupPrefix = []byte("l") // txLookupPrefix + hash -> transaction/receipt lookup metadata bloomBitsPrefix = []byte("B") // bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -> bloom bits @@ -78,6 +75,11 @@ func encodeBlockNumber(number uint64) []byte { return enc } +// decodeBlockNumber decodes bytes as uint64 +func decodeBlockNumber(dec []byte) uint64 { + return binary.BigEndian.Uint64(dec) +} + // headerKey = headerPrefix + num (uint64 big endian) + hash func headerKey(number uint64, hash common.Hash) []byte { return append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...) @@ -107,6 +109,11 @@ func blockReceiptsKey(number uint64, hash common.Hash) []byte { return append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...) } +// blockDetailTxsKey = blockDetailTxsPrefix + num (uint64 big endian) + hash +func blockDetailTxsKey(number uint64, hash common.Hash) []byte { + return append(append(blockDetailTxsPrefix, encodeBlockNumber(number)...), hash.Bytes()...) +} + // blockStatePrefix + num (uint64 big endian) + hash -> block revert info func blockStateOutKey(hash common.Hash) []byte { return append(blockStateOutPrefix, hash.Bytes()...) diff --git a/rpc/subscription.go b/rpc/subscription.go index 6ce7befa..c0ce9b84 100644 --- a/rpc/subscription.go +++ b/rpc/subscription.go @@ -1,18 +1,18 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. // -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // -// The go-ethereum library is distributed in the hope that it will be useful, +// This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. +// GNU General Public License for more details. // -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . package rpc diff --git a/internal/api/account.go b/rpcapi/account.go similarity index 81% rename from internal/api/account.go rename to rpcapi/account.go index 0537b126..4df2d0b3 100644 --- a/internal/api/account.go +++ b/rpcapi/account.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package api +package rpcapi import ( "context" @@ -51,6 +51,18 @@ func (aapi *AccountAPI) AccountIsExist(ctx context.Context, acctName common.Name return acct.AccountIsExist(acctName) } +//GetAccountByID +func (aapi *AccountAPI) GetAccountByID(ctx context.Context, accountID uint64) (*accountmanager.Account, error) { + am, err := aapi.b.GetAccountManager() + if err != nil { + return nil, err + } + if am == nil { + return nil, ErrGetAccounManagerErr + } + return am.GetAccountById(accountID) +} + //GetAccountByName func (aapi *AccountAPI) GetAccountByName(ctx context.Context, accountName common.Name) (*accountmanager.Account, error) { am, err := aapi.b.GetAccountManager() @@ -64,7 +76,7 @@ func (aapi *AccountAPI) GetAccountByName(ctx context.Context, accountName common } //GetAccountBalanceByID -func (aapi *AccountAPI) GetAccountBalanceByID(ctx context.Context, accountName common.Name, assetID uint64) (*big.Int, error) { +func (aapi *AccountAPI) GetAccountBalanceByID(ctx context.Context, accountName common.Name, assetID uint64, typeID uint64) (*big.Int, error) { am, err := aapi.b.GetAccountManager() if err != nil { return nil, err @@ -72,33 +84,9 @@ func (aapi *AccountAPI) GetAccountBalanceByID(ctx context.Context, accountName c if am == nil { return nil, ErrGetAccounManagerErr } - return am.GetAccountBalanceByID(accountName, assetID) + return am.GetAccountBalanceByID(accountName, assetID, typeID) } -//func (aapi *AccountAPI) GetAccountBalanceByName(ctx context.Context, accountName common.Name, assetName string) (*big.Int, error) { -// acct, err := aapi.b.GetAccountManager() -// if err != nil { -// return nil, err -// } -// if acct == nil { -// return nil, ErrGetAccounManagerErr -// } -// a, err := acct.GetAccountByName(accountName) -// if err != nil { -// return nil, err -// } -// return a.GetBalanceByID(assetID) -//} - -// -//func (aapi *AccountAPI) GetBalancesList(ctx context.Context,accountName common.Name) ([]*AssetBalance, error){ -// acct := aapi.b.GetAccountManager() -// if acct == nil { -// return nil,ErrGetAccounManagerErr -// } -// return acct.GetBalancesList(accountName) -//} - //GetCode func (aapi *AccountAPI) GetCode(ctx context.Context, accountName common.Name) (hexutil.Bytes, error) { acct, err := aapi.b.GetAccountManager() @@ -154,8 +142,20 @@ func (aapi *AccountAPI) GetAssetInfoByID(ctx context.Context, assetID uint64) (* return acct.GetAssetInfoByID(assetID) } +//GetAssetAmountByTime +func (aapi *AccountAPI) GetAssetAmountByTime(ctx context.Context, assetID uint64, time uint64) (*big.Int, error) { + am, err := aapi.b.GetAccountManager() + if err != nil { + return nil, err + } + if am == nil { + return nil, ErrGetAccounManagerErr + } + return am.GetAssetAmountByTime(assetID, time) +} + //GetAccountBalanceByTime -func (aapi *AccountAPI) GetAccountBalanceByTime(ctx context.Context, accountName common.Name, assetID uint64, time uint64) (*big.Int, error) { +func (aapi *AccountAPI) GetAccountBalanceByTime(ctx context.Context, accountName common.Name, assetID uint64, typeID uint64, time uint64) (*big.Int, error) { am, err := aapi.b.GetAccountManager() if err != nil { return nil, err @@ -163,7 +163,7 @@ func (aapi *AccountAPI) GetAccountBalanceByTime(ctx context.Context, accountName if am == nil { return nil, ErrGetAccounManagerErr } - return am.GetBalanceByTime(accountName, assetID, time) + return am.GetBalanceByTime(accountName, assetID, typeID, time) } //GetSnapshotLast get last snapshot time diff --git a/internal/api/addrlock.go b/rpcapi/addrlock.go similarity index 99% rename from internal/api/addrlock.go rename to rpcapi/addrlock.go index 77e59ef8..00824de9 100644 --- a/internal/api/addrlock.go +++ b/rpcapi/addrlock.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package api +package rpcapi import ( "sync" diff --git a/internal/api/backend.go b/rpcapi/backend.go similarity index 86% rename from internal/api/backend.go rename to rpcapi/backend.go index 557825b6..8842552c 100644 --- a/internal/api/backend.go +++ b/rpcapi/backend.go @@ -14,24 +14,23 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// Package api implements the general Ethereum API functions. -package api +// package rpcapi implements the general API functions. + +package rpcapi import ( "context" "math/big" - "github.com/fractalplatform/fractal/consensus" - "github.com/fractalplatform/fractal/accountmanager" "github.com/fractalplatform/fractal/common" + "github.com/fractalplatform/fractal/consensus" "github.com/fractalplatform/fractal/params" "github.com/fractalplatform/fractal/processor/vm" "github.com/fractalplatform/fractal/rpc" "github.com/fractalplatform/fractal/state" "github.com/fractalplatform/fractal/types" "github.com/fractalplatform/fractal/utils/fdb" - "github.com/fractalplatform/fractal/wallet" ) // Backend interface provides the common API services (that are provided by @@ -49,8 +48,13 @@ type Backend interface { StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error) GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) GetReceipts(ctx context.Context, blockHash common.Hash) ([]*types.Receipt, error) + GetDetailTxsLog(ctx context.Context, hash common.Hash) ([]*types.DetailTx, error) + GetBlockDetailLog(ctx context.Context, blockNr rpc.BlockNumber) *types.BlockAndResult GetTd(blockHash common.Hash) *big.Int GetEVM(ctx context.Context, account *accountmanager.AccountManager, state *state.StateDB, from common.Name, assetID uint64, gasPrice *big.Int, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) + GetDetailTxByFilter(ctx context.Context, filterFn func(common.Name) bool, blockNr rpc.BlockNumber, lookbackNum uint64) []*types.DetailTx + GetTxsByFilter(ctx context.Context, filterFn func(common.Name) bool, blockNr rpc.BlockNumber, lookbackNum uint64) []common.Hash + GetBadBlocks(ctx context.Context) ([]*types.Block, error) // TxPool API SendTx(ctx context.Context, signedTx *types.Transaction) error @@ -64,9 +68,6 @@ type Backend interface { SetGasPrice(gasPrice *big.Int) bool - //Wallet - Wallet() *wallet.Wallet - // P2P AddPeer(url string) error RemovePeer(url string) error @@ -75,9 +76,7 @@ type Backend interface { PeerCount() int Peers() []string SelfNode() string - Engine() consensus.IEngine - APIs() []rpc.API } @@ -98,11 +97,6 @@ func GetAPIs(apiBackend Backend) []rpc.API { Version: "1.0", Service: NewPublicFractalAPI(apiBackend), Public: true, - }, { - Namespace: "keystore", - Version: "1.0", - Service: NewPrivateKeyStoreAPI(apiBackend), - Public: true, }, { Namespace: "account", diff --git a/internal/api/blockchain.go b/rpcapi/blockchain.go similarity index 72% rename from internal/api/blockchain.go rename to rpcapi/blockchain.go index d0295a49..fd8243cb 100644 --- a/internal/api/blockchain.go +++ b/rpcapi/blockchain.go @@ -14,10 +14,11 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package api +package rpcapi import ( "context" + "encoding/json" "fmt" "math" "math/big" @@ -35,17 +36,17 @@ import ( "github.com/fractalplatform/fractal/types" ) -// PublicBlockChainAPI provides an API to access the Ethereum blockchain. +// PublicBlockChainAPI provides an API to access the blockchain. // It offers only methods that operate on public data that is freely available to anyone. const ( - defaultGasPrice = params.GWei + defaultGasPrice = 1e9 ) type PublicBlockChainAPI struct { b Backend } -// NewPublicBlockChainAPI creates a new Ethereum blockchain API. +// NewPublicBlockChainAPI creates a new blockchain API. func NewPublicBlockChainAPI(b Backend) *PublicBlockChainAPI { return &PublicBlockChainAPI{b} } @@ -83,51 +84,6 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc. } return nil, err } -func (s *PublicBlockChainAPI) GetTxNumByBlockHash(ctx context.Context, blockHash common.Hash) (int, error) { - block, err := s.b.GetBlock(ctx, blockHash) - if block != nil { - return len(block.Transactions()), nil - } - return 0, err -} -func (s *PublicBlockChainAPI) GetTxNumByBlockNum(ctx context.Context, blockNr rpc.BlockNumber) (int, error) { - block, err := s.b.BlockByNumber(ctx, blockNr) - if block != nil { - return len(block.Transactions()), nil - } - return 0, err -} -func (s *PublicBlockChainAPI) GetTotalTxNumByBlockHash(ctx context.Context, blockHash common.Hash, lookbackNum uint64) (*big.Int, error) { - block, err := s.b.GetBlock(ctx, blockHash) - if block != nil { - txNum := len(block.Transactions()) - totalTxNum := big.NewInt(int64(txNum)) - height := block.Number().Uint64() - for i := height - 1; i >= 0 && i > height-lookbackNum; i-- { - block, err := s.b.BlockByNumber(ctx, rpc.BlockNumber(i)) - if block != nil { - totalTxNum = totalTxNum.Add(totalTxNum, big.NewInt(int64(len(block.Transactions())))) - } else { - return nil, err - } - } - return totalTxNum, nil - } - - return nil, err -} -func (s *PublicBlockChainAPI) GetTotalTxNumByBlockNum(ctx context.Context, blockNr rpc.BlockNumber, lookbackNum uint64) (*big.Int, error) { - totalTxNum := big.NewInt(0) - for i := blockNr; i >= 0 && i > blockNr-rpc.BlockNumber(lookbackNum); i-- { - block, err := s.b.BlockByNumber(ctx, i) - if block != nil { - totalTxNum = totalTxNum.Add(totalTxNum, big.NewInt(int64(len(block.Transactions())))) - } else { - return nil, err - } - } - return totalTxNum, nil -} // rpcOutputBlock uses the generalized output filler, then adds the total difficulty field, which requires // a `PublicBlockchainAPI`. @@ -170,6 +126,78 @@ func (s *PublicBlockChainAPI) GetTransactionReceipt(ctx context.Context, hash co return receipt.NewRPCReceipt(blockHash, blockNumber, index, tx), nil } +func (s *PublicBlockChainAPI) GetBlockAndResultByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.BlockAndResult, error) { + r := s.b.GetBlockDetailLog(ctx, blockNr) + if r == nil { + return nil, nil + } + block, err := s.GetBlockByNumber(ctx, blockNr, true) + r.Block = block + return r, err +} + +func (s *PublicBlockChainAPI) GetTxsByAccount(ctx context.Context, acctName common.Name, blockNr rpc.BlockNumber, lookbackNum uint64) ([]common.Hash, error) { + filterFn := func(name common.Name) bool { + return name == acctName + } + + return s.b.GetTxsByFilter(ctx, filterFn, blockNr, lookbackNum), nil +} + +func (s *PublicBlockChainAPI) GetTxsByBloom(ctx context.Context, bloomByte hexutil.Bytes, blockNr rpc.BlockNumber, lookbackNum uint64) ([]common.Hash, error) { + bloom := types.BytesToBloom(bloomByte) + + filterFn := func(name common.Name) bool { + return bloom.TestBytes([]byte(name)) + } + return s.b.GetTxsByFilter(ctx, filterFn, blockNr, lookbackNum), nil +} + +func (s *PublicBlockChainAPI) GetInternalTxByAccount(ctx context.Context, acctName common.Name, blockNr rpc.BlockNumber, lookbackNum uint64) ([]*types.DetailTx, error) { + filterFn := func(name common.Name) bool { + return name == acctName + } + + return s.b.GetDetailTxByFilter(ctx, filterFn, blockNr, lookbackNum), nil +} + +func (s *PublicBlockChainAPI) GetInternalTxByBloom(ctx context.Context, bloomByte hexutil.Bytes, blockNr rpc.BlockNumber, lookbackNum uint64) ([]*types.DetailTx, error) { + bloom := types.BytesToBloom(bloomByte) + + filterFn := func(name common.Name) bool { + return bloom.TestBytes([]byte(name)) + } + return s.b.GetDetailTxByFilter(ctx, filterFn, blockNr, lookbackNum), nil +} + +func (s *PublicBlockChainAPI) GetInternalTxByHash(ctx context.Context, hash common.Hash) (*types.DetailTx, error) { + tx, blockHash, blockNumber, index := rawdb.ReadTransaction(s.b.ChainDb(), hash) + if tx == nil { + return nil, nil + } + + detailtxs := rawdb.ReadDetailTxs(s.b.ChainDb(), blockHash, blockNumber) + if len(detailtxs) <= int(index) { + return nil, nil + } + + return detailtxs[index], nil +} + +func (s *PublicBlockChainAPI) GetBadBlocks(ctx context.Context, fullTx bool) ([]map[string]interface{}, error) { + blocks, err := s.b.GetBadBlocks(ctx) + if len(blocks) != 0 { + ret_block := make([]map[string]interface{}, len(blocks)) + + for i, b := range blocks { + ret_block[i] = s.rpcOutputBlock(s.b.ChainConfig().ChainID, b, true, fullTx) + } + + return ret_block, nil + } + return nil, err +} + type CallArgs struct { ActionType types.ActionType `json:"actionType"` From common.Name `json:"from"` @@ -286,3 +314,27 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (h } return hexutil.Uint64(hi), nil } + +// GetChainConfig returns chain config. +func (s *PublicBlockChainAPI) GetChainConfig() map[string]interface{} { + ret := map[string]interface{}{} + g, err := s.b.BlockByNumber(context.Background(), 0) + if err != nil { + return ret + } + cfg := rawdb.ReadChainConfig(s.b.ChainDb(), g.Hash()) + bts, _ := json.Marshal(cfg) + json.Unmarshal(bts, &ret) + return ret +} + +// GetGeneisisJson returns geneisis config. +func (s *PublicBlockChainAPI) GetGeneisis() map[string]interface{} { + ret := map[string]interface{}{} + g, err := s.b.BlockByNumber(context.Background(), 0) + if err != nil { + return ret + } + json.Unmarshal(g.Head.Extra, &ret) + return ret +} diff --git a/internal/api/fractal.go b/rpcapi/fractal.go similarity index 51% rename from internal/api/fractal.go rename to rpcapi/fractal.go index 3919c170..47c41903 100644 --- a/internal/api/fractal.go +++ b/rpcapi/fractal.go @@ -14,16 +14,14 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package api +package rpcapi import ( "context" - "errors" "math/big" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/fractalplatform/fractal/common" - "github.com/fractalplatform/fractal/crypto" "github.com/fractalplatform/fractal/types" "github.com/fractalplatform/fractal/utils/rlp" ) @@ -52,56 +50,3 @@ func (s *PublicFractalAPI) SendRawTransaction(ctx context.Context, encodedTx hex } return submitTransaction(ctx, s.b, tx) } - -type SendArgs struct { - ChainID *big.Int `json:"chainID"` - ActionType types.ActionType `json:"actionType"` - GasAssetID uint64 `json:"gasAssetId"` - From common.Name `json:"from"` - To common.Name `json:"to"` - Nonce uint64 `json:"nonce"` - AssetID uint64 `json:"assetId"` - Gas uint64 `json:"gas"` - GasPrice *big.Int `json:"gasPrice"` - Value *big.Int `json:"value"` - Data hexutil.Bytes `json:"data"` - Passphrase string `json:"password"` -} - -func (s *PublicFractalAPI) SendTransaction(ctx context.Context, args SendArgs) (common.Hash, error) { - acct, err := s.b.GetAccountManager() - if err != nil { - return common.Hash{}, err - } - if acct == nil { - return common.Hash{}, ErrGetAccounManagerErr - } - fromAcct, err := acct.GetAccountByName(args.From) - if err != nil { - return common.Hash{}, err - } - if fromAcct == nil { - return common.Hash{}, errors.New("invalid user") - } - - pubByte, _ := crypto.UnmarshalPubkey(fromAcct.PublicKey.Bytes()) - if !s.b.Wallet().HasAddress(crypto.PubkeyToAddress(*pubByte)) { - return common.Hash{}, errors.New("user not in local wallet") - } - cacheAcct, err := s.b.Wallet().Find(crypto.PubkeyToAddress(*pubByte)) - if err != nil { - return common.Hash{}, err - } - - assetID := uint64(args.AssetID) - gas := uint64(args.Gas) - action := types.NewAction(args.ActionType, args.From, args.To, args.Nonce, assetID, gas, args.Value, args.Data) - tx := types.NewTransaction(args.GasAssetID, args.GasPrice, action) - - tx, err = s.b.Wallet().SignTxWithPassphrase(cacheAcct, args.Passphrase, tx, action, args.ChainID) - if err != nil { - return common.Hash{}, err - } - - return submitTransaction(ctx, s.b, tx) -} diff --git a/internal/api/p2p.go b/rpcapi/p2p.go similarity index 99% rename from internal/api/p2p.go rename to rpcapi/p2p.go index 0fed9caa..8ecbe341 100644 --- a/internal/api/p2p.go +++ b/rpcapi/p2p.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package api +package rpcapi import ( "context" diff --git a/internal/api/txpool.go b/rpcapi/txpool.go similarity index 99% rename from internal/api/txpool.go rename to rpcapi/txpool.go index ef33b149..d36bea81 100644 --- a/internal/api/txpool.go +++ b/rpcapi/txpool.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package api +package rpcapi import ( "fmt" diff --git a/internal/api/utils.go b/rpcapi/utils.go similarity index 99% rename from internal/api/utils.go rename to rpcapi/utils.go index c10989c1..ed1899d1 100644 --- a/internal/api/utils.go +++ b/rpcapi/utils.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package api +package rpcapi import ( "context" diff --git a/scripts/test.sh b/scripts/test.sh index 7a030f6a..fe486fc4 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -16,6 +16,19 @@ #!/usr/bin/env bash +# clear test_sdk data +rm -r ./build/test_sdk + +# generate test directory +mkdir ./build/test_sdk + +# clear test node +ps -ef | grep ./build/test_sdk/ft | grep -v grep | awk -F ' ' '{print $2}' | xargs kill -9 + +# start test node +./build/bin/ft --datadir ./build/test_sdk/ft --log_level=4 --miner_start > ./build/test_sdk/test.log 2>&1 & + +# collect code coverrage data set -e echo "mode: count" >coverage.out @@ -28,3 +41,9 @@ for d in $(go list ./... | grep -v vendor | grep -v test); do fi done +# kill test node +ps -ef | grep ./build/test_sdk/ft | grep -v grep | awk -F ' ' '{print $2}' | xargs kill -9 + +# clear test_sdk data +rm -r ./build/test_sdk + diff --git a/sdk/account.go b/sdk/account.go new file mode 100644 index 00000000..7580a9d3 --- /dev/null +++ b/sdk/account.go @@ -0,0 +1,931 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package sdk + +import ( + "crypto/ecdsa" + "math" + "math/big" + "time" + + "github.com/fractalplatform/fractal/accountmanager" + "github.com/fractalplatform/fractal/asset" + "github.com/fractalplatform/fractal/common" + "github.com/fractalplatform/fractal/consensus/dpos" + "github.com/fractalplatform/fractal/crypto" + "github.com/fractalplatform/fractal/types" + "github.com/fractalplatform/fractal/utils/rlp" +) + +var ( + timeout = int64(time.Second) * 10 +) + +// Account account object +type Account struct { + api *API + name common.Name + priv *ecdsa.PrivateKey + gasprice *big.Int + feeid uint64 + nonce uint64 // nonce == math.MaxUint64, auto get + checked bool // check result + chainID *big.Int +} + +// NewAccount new account object +func NewAccount(api *API, name common.Name, priv *ecdsa.PrivateKey, feeid uint64, nonce uint64, checked bool, chainID *big.Int) *Account { + return &Account{ + api: api, + name: name, + priv: priv, + gasprice: big.NewInt(1e10), + feeid: feeid, + nonce: nonce, + checked: checked, + chainID: chainID, + } +} + +// Pubkey account pub key +func (acc *Account) Pubkey() common.PubKey { + return common.BytesToPubKey(crypto.FromECDSAPub(&acc.priv.PublicKey)) +} + +//===================================================================================== +// Transactions +//===================================================================================== + +// CreateAccount new account +func (acc *Account) CreateAccount(to common.Name, value *big.Int, id uint64, gas uint64, newacct *accountmanager.AccountAction) (hash common.Hash, err error) { + nonce := acc.nonce + if nonce == math.MaxUint64 { + nonce, err = acc.api.AccountNonce(acc.name.String()) + if err != nil { + return + } + } + payload, _ := rlp.EncodeToBytes(newacct) + action := types.NewAction(types.CreateAccount, acc.name, to, nonce, id, gas, value, payload) + tx := types.NewTransaction(acc.feeid, acc.gasprice, []*types.Action{action}...) + key := types.MakeKeyPair(acc.priv, []uint64{0}) + err = types.SignActionWithMultiKey(action, tx, types.NewSigner(acc.chainID), []*types.KeyPair{key}) + if err != nil { + return + } + rawtx, _ := rlp.EncodeToBytes(tx) + checked := acc.checked || acc.nonce == math.MaxUint64 + var checkedfunc func() error + if checked { + // before + checkedfunc, err = acc.checkCreateAccount(action) + if err != nil { + return + } + } + hash, err = acc.api.SendRawTransaction(rawtx) + if err != nil { + return + } + if checked { + //after + err = acc.utilReceipt(hash, timeout) + if err != nil { + return + } + err = checkedfunc() + if err != nil { + return + } + } + + if acc.nonce != math.MaxUint64 { + acc.nonce++ + } + return +} + +// UpdateAccount update accout +func (acc *Account) UpdateAccount(to common.Name, value *big.Int, id uint64, gas uint64, newacct *accountmanager.AccountAction) (hash common.Hash, err error) { + nonce := acc.nonce + if nonce == math.MaxUint64 { + nonce, err = acc.api.AccountNonce(acc.name.String()) + if err != nil { + return + } + } + + bts, _ := rlp.EncodeToBytes(newacct) + action := types.NewAction(types.UpdateAccount, acc.name, to, nonce, id, gas, value, bts) + tx := types.NewTransaction(acc.feeid, acc.gasprice, []*types.Action{action}...) + key := types.MakeKeyPair(acc.priv, []uint64{0}) + err = types.SignActionWithMultiKey(action, tx, types.NewSigner(acc.chainID), []*types.KeyPair{key}) + if err != nil { + return + } + rawtx, _ := rlp.EncodeToBytes(tx) + checked := acc.checked || acc.nonce == math.MaxUint64 + var checkedfunc func() error + if checked { + // before + checkedfunc, err = acc.checkUpdateAccount(action) + if err != nil { + return + } + } + hash, err = acc.api.SendRawTransaction(rawtx) + if err != nil { + return + } + if checked { + //after + err = acc.utilReceipt(hash, timeout) + if err != nil { + return + } + err = checkedfunc() + if err != nil { + return + } + } + + if acc.nonce != math.MaxUint64 { + acc.nonce++ + } + return +} + +// Transfer transfer tokens +func (acc *Account) Transfer(to common.Name, value *big.Int, id uint64, gas uint64) (hash common.Hash, err error) { + nonce := acc.nonce + if nonce == math.MaxUint64 { + nonce, err = acc.api.AccountNonce(acc.name.String()) + if err != nil { + return + } + } + + // transfer + action := types.NewAction(types.Transfer, acc.name, to, nonce, id, gas, value, nil) + tx := types.NewTransaction(acc.feeid, acc.gasprice, []*types.Action{action}...) + key := types.MakeKeyPair(acc.priv, []uint64{0}) + err = types.SignActionWithMultiKey(action, tx, types.NewSigner(acc.chainID), []*types.KeyPair{key}) + if err != nil { + return + } + rawtx, _ := rlp.EncodeToBytes(tx) + checked := acc.checked || acc.nonce == math.MaxUint64 + var checkedfunc func() error + if checked { + //before + checkedfunc, err = acc.checkTranfer(action) + if err != nil { + return + } + } + hash, err = acc.api.SendRawTransaction(rawtx) + if err != nil { + return + } + if checked { + //after + err = acc.utilReceipt(hash, timeout) + if err != nil { + return + } + err = checkedfunc() + if err != nil { + return + } + } + + if acc.nonce != math.MaxUint64 { + acc.nonce++ + } + return +} + +// IssueAsset new asset +func (acc *Account) IssueAsset(to common.Name, value *big.Int, id uint64, gas uint64, asset *asset.AssetObject) (hash common.Hash, err error) { + nonce := acc.nonce + if nonce == math.MaxUint64 { + nonce, err = acc.api.AccountNonce(acc.name.String()) + if err != nil { + return + } + } + + payload, _ := rlp.EncodeToBytes(asset) + action := types.NewAction(types.IssueAsset, acc.name, to, nonce, id, gas, value, payload) + tx := types.NewTransaction(acc.feeid, acc.gasprice, []*types.Action{action}...) + key := types.MakeKeyPair(acc.priv, []uint64{0}) + err = types.SignActionWithMultiKey(action, tx, types.NewSigner(acc.chainID), []*types.KeyPair{key}) + if err != nil { + return + } + rawtx, _ := rlp.EncodeToBytes(tx) + checked := acc.checked || acc.nonce == math.MaxUint64 + var checkedfunc func() error + if checked { + // before + checkedfunc, err = acc.checkIssueAsset(action) + if err != nil { + return + } + } + hash, err = acc.api.SendRawTransaction(rawtx) + if err != nil { + return + } + if checked { + //after + err = acc.utilReceipt(hash, timeout) + if err != nil { + return + } + err = checkedfunc() + if err != nil { + return + } + } + if err != nil { + return + } + + if acc.nonce != math.MaxUint64 { + acc.nonce++ + } + return +} + +// UpdateAsset update asset +func (acc *Account) UpdateAsset(to common.Name, value *big.Int, id uint64, gas uint64, asset *asset.AssetObject) (hash common.Hash, err error) { + nonce := acc.nonce + if nonce == math.MaxUint64 { + nonce, err = acc.api.AccountNonce(acc.name.String()) + if err != nil { + return + } + } + + payload, _ := rlp.EncodeToBytes(asset) + action := types.NewAction(types.UpdateAsset, acc.name, to, nonce, id, gas, value, payload) + tx := types.NewTransaction(acc.feeid, acc.gasprice, []*types.Action{action}...) + key := types.MakeKeyPair(acc.priv, []uint64{0}) + + err = types.SignActionWithMultiKey(action, tx, types.NewSigner(acc.chainID), []*types.KeyPair{key}) + if err != nil { + return + } + rawtx, _ := rlp.EncodeToBytes(tx) + checked := acc.checked || acc.nonce == math.MaxUint64 + var checkedfunc func() error + if checked { + // before + checkedfunc, err = acc.checkUpdateAsset(action) + if err != nil { + return + } + } + hash, err = acc.api.SendRawTransaction(rawtx) + if err != nil { + return + } + if checked { + //after + err = acc.utilReceipt(hash, timeout) + if err != nil { + return + } + err = checkedfunc() + if err != nil { + return + } + } + + if acc.nonce != math.MaxUint64 { + acc.nonce++ + } + return +} + +// IncreaseAsset update asset +func (acc *Account) IncreaseAsset(to common.Name, value *big.Int, id uint64, gas uint64, asset *accountmanager.IncAsset) (hash common.Hash, err error) { + nonce := acc.nonce + if nonce == math.MaxUint64 { + nonce, err = acc.api.AccountNonce(acc.name.String()) + if err != nil { + return + } + } + + payload, _ := rlp.EncodeToBytes(asset) + action := types.NewAction(types.IncreaseAsset, acc.name, to, nonce, id, gas, value, payload) + tx := types.NewTransaction(acc.feeid, acc.gasprice, []*types.Action{action}...) + key := types.MakeKeyPair(acc.priv, []uint64{0}) + + err = types.SignActionWithMultiKey(action, tx, types.NewSigner(acc.chainID), []*types.KeyPair{key}) + if err != nil { + return + } + rawtx, _ := rlp.EncodeToBytes(tx) + checked := acc.checked || acc.nonce == math.MaxUint64 + var checkedfunc func() error + if checked { + // before + checkedfunc, err = acc.checkIncreaseAsset(action) + if err != nil { + return + } + } + hash, err = acc.api.SendRawTransaction(rawtx) + if err != nil { + return + } + if checked { + //after + err = acc.utilReceipt(hash, timeout) + if err != nil { + return + } + err = checkedfunc() + if err != nil { + return + } + } + + if acc.nonce != math.MaxUint64 { + acc.nonce++ + } + return +} + +// DestroyAsset destory asset +func (acc *Account) DestroyAsset(to common.Name, value *big.Int, id uint64, gas uint64) (hash common.Hash, err error) { + nonce := acc.nonce + if nonce == math.MaxUint64 { + nonce, err = acc.api.AccountNonce(acc.name.String()) + if err != nil { + return + } + } + + action := types.NewAction(types.DestroyAsset, acc.name, to, nonce, id, gas, value, nil) + tx := types.NewTransaction(acc.feeid, acc.gasprice, []*types.Action{action}...) + key := types.MakeKeyPair(acc.priv, []uint64{0}) + + err = types.SignActionWithMultiKey(action, tx, types.NewSigner(acc.chainID), []*types.KeyPair{key}) + if err != nil { + return + } + rawtx, _ := rlp.EncodeToBytes(tx) + checked := acc.checked || acc.nonce == math.MaxUint64 + var checkedfunc func() error + if checked { + // before + checkedfunc, err = acc.checkDestroyAsset(action) + if err != nil { + return + } + } + hash, err = acc.api.SendRawTransaction(rawtx) + if err != nil { + return + } + if checked { + //after + err = acc.utilReceipt(hash, timeout) + if err != nil { + return + } + err = checkedfunc() + if err != nil { + return + } + } + + if acc.nonce != math.MaxUint64 { + acc.nonce++ + } + return +} + +// SetAssetOwner update asset owner +func (acc *Account) SetAssetOwner(to common.Name, value *big.Int, id uint64, gas uint64, asset *asset.AssetObject) (hash common.Hash, err error) { + nonce := acc.nonce + if nonce == math.MaxUint64 { + nonce, err = acc.api.AccountNonce(acc.name.String()) + if err != nil { + return + } + } + + payload, _ := rlp.EncodeToBytes(asset) + action := types.NewAction(types.SetAssetOwner, acc.name, to, nonce, id, gas, value, payload) + tx := types.NewTransaction(acc.feeid, acc.gasprice, []*types.Action{action}...) + key := types.MakeKeyPair(acc.priv, []uint64{0}) + + err = types.SignActionWithMultiKey(action, tx, types.NewSigner(acc.chainID), []*types.KeyPair{key}) + if err != nil { + return + } + rawtx, _ := rlp.EncodeToBytes(tx) + checked := acc.checked || acc.nonce == math.MaxUint64 + var checkedfunc func() error + if checked { + // before + checkedfunc, err = acc.checkSetAssetOwner(action) + if err != nil { + return + } + } + hash, err = acc.api.SendRawTransaction(rawtx) + if err != nil { + return + } + if checked { + //after + err = acc.utilReceipt(hash, timeout) + if err != nil { + return + } + err = checkedfunc() + if err != nil { + return + } + } + + if acc.nonce != math.MaxUint64 { + acc.nonce++ + } + return +} + +// RegCadidate new cadidate +func (acc *Account) RegCadidate(to common.Name, value *big.Int, id uint64, gas uint64, arg *dpos.RegisterCadidate) (hash common.Hash, err error) { + nonce := acc.nonce + if nonce == math.MaxUint64 { + nonce, err = acc.api.AccountNonce(acc.name.String()) + if err != nil { + return + } + } + + payload, _ := rlp.EncodeToBytes(arg) + action := types.NewAction(types.RegCadidate, acc.name, to, nonce, id, gas, value, payload) + tx := types.NewTransaction(acc.feeid, big.NewInt(1e10), []*types.Action{action}...) + key := types.MakeKeyPair(acc.priv, []uint64{0}) + + err = types.SignActionWithMultiKey(action, tx, types.NewSigner(acc.chainID), []*types.KeyPair{key}) + if err != nil { + return + } + rawtx, _ := rlp.EncodeToBytes(tx) + checked := acc.checked || acc.nonce == math.MaxUint64 + var checkedfunc func() error + if checked { + // before + checkedfunc, err = acc.chekRegProdoucer(action) + if err != nil { + return + } + } + hash, err = acc.api.SendRawTransaction(rawtx) + if err != nil { + return + } + if checked { + // after + err = acc.utilReceipt(hash, timeout) + if err != nil { + return + } + err = checkedfunc() + if err != nil { + return + } + } + + if acc.nonce != math.MaxUint64 { + acc.nonce++ + } + return +} + +// UpdateCadidate update cadidate +func (acc *Account) UpdateCadidate(to common.Name, value *big.Int, id uint64, gas uint64, arg *dpos.UpdateCadidate) (hash common.Hash, err error) { + nonce := acc.nonce + if nonce == math.MaxUint64 { + nonce, err = acc.api.AccountNonce(acc.name.String()) + if err != nil { + return + } + } + + payload, _ := rlp.EncodeToBytes(arg) + action := types.NewAction(types.UpdateCadidate, acc.name, to, nonce, id, gas, value, payload) + tx := types.NewTransaction(acc.feeid, big.NewInt(1e10), []*types.Action{action}...) + key := types.MakeKeyPair(acc.priv, []uint64{0}) + + err = types.SignActionWithMultiKey(action, tx, types.NewSigner(acc.chainID), []*types.KeyPair{key}) + if err != nil { + return + } + rawtx, _ := rlp.EncodeToBytes(tx) + checked := acc.checked || acc.nonce == math.MaxUint64 + var checkedfunc func() error + if checked { + // before + checkedfunc, err = acc.chekUpdateProdoucer(action) + if err != nil { + return + } + } + hash, err = acc.api.SendRawTransaction(rawtx) + if err != nil { + return + } + if checked { + // after + err = acc.utilReceipt(hash, timeout) + if err != nil { + return + } + err = checkedfunc() + if err != nil { + return + } + } + + if acc.nonce != math.MaxUint64 { + acc.nonce++ + } + return +} + +// UnRegCadidate remove cadiate +func (acc *Account) UnRegCadidate(to common.Name, value *big.Int, id uint64, gas uint64) (hash common.Hash, err error) { + nonce := acc.nonce + if nonce == math.MaxUint64 { + nonce, err = acc.api.AccountNonce(acc.name.String()) + if err != nil { + return + } + } + + action := types.NewAction(types.UnregCadidate, acc.name, to, nonce, id, gas, value, nil) + tx := types.NewTransaction(acc.feeid, big.NewInt(1e10), []*types.Action{action}...) + key := types.MakeKeyPair(acc.priv, []uint64{0}) + + err = types.SignActionWithMultiKey(action, tx, types.NewSigner(acc.chainID), []*types.KeyPair{key}) + if err != nil { + panic(err) + } + rawtx, _ := rlp.EncodeToBytes(tx) + checked := acc.checked || acc.nonce == math.MaxUint64 + var checkedfunc func() error + if checked { + // before + checkedfunc, err = acc.chekUnregProdoucer(action) + if err != nil { + return + } + } + hash, err = acc.api.SendRawTransaction(rawtx) + if err != nil { + return + } + if checked { + // after + err = acc.utilReceipt(hash, timeout) + if err != nil { + return + } + err = checkedfunc() + if err != nil { + return + } + } + + if acc.nonce != math.MaxUint64 { + acc.nonce++ + } + return +} + +// VoteCadidate vote cadiate +func (acc *Account) VoteCadidate(to common.Name, value *big.Int, id uint64, gas uint64, arg *dpos.VoteCadidate) (hash common.Hash, err error) { + nonce := acc.nonce + if nonce == math.MaxUint64 { + nonce, err = acc.api.AccountNonce(acc.name.String()) + if err != nil { + return + } + } + + payload, _ := rlp.EncodeToBytes(arg) + action := types.NewAction(types.VoteCadidate, acc.name, to, nonce, id, gas, value, payload) + tx := types.NewTransaction(acc.feeid, big.NewInt(1e10), []*types.Action{action}...) + key := types.MakeKeyPair(acc.priv, []uint64{0}) + + err = types.SignActionWithMultiKey(action, tx, types.NewSigner(acc.chainID), []*types.KeyPair{key}) + if err != nil { + panic(err) + } + rawtx, _ := rlp.EncodeToBytes(tx) + checked := acc.checked || acc.nonce == math.MaxUint64 + var checkedfunc func() error + if checked { + // before + checkedfunc, err = acc.chekVoteProdoucer(action) + if err != nil { + return + } + } + hash, err = acc.api.SendRawTransaction(rawtx) + if err != nil { + return + } + if checked { + // after + err = acc.utilReceipt(hash, timeout) + if err != nil { + return + } + err = checkedfunc() + if err != nil { + return + } + } + + if acc.nonce != math.MaxUint64 { + acc.nonce++ + } + return +} + +// ChangeCadidate change cadidate +func (acc *Account) ChangeCadidate(to common.Name, value *big.Int, id uint64, gas uint64, arg *dpos.ChangeCadidate) (hash common.Hash, err error) { + nonce := acc.nonce + if nonce == math.MaxUint64 { + nonce, err = acc.api.AccountNonce(acc.name.String()) + if err != nil { + return + } + } + + payload, _ := rlp.EncodeToBytes(arg) + action := types.NewAction(types.ChangeCadidate, acc.name, to, nonce, id, gas, value, payload) + tx := types.NewTransaction(acc.feeid, big.NewInt(1e10), []*types.Action{action}...) + key := types.MakeKeyPair(acc.priv, []uint64{0}) + + err = types.SignActionWithMultiKey(action, tx, types.NewSigner(acc.chainID), []*types.KeyPair{key}) + if err != nil { + return + } + rawtx, _ := rlp.EncodeToBytes(tx) + checked := acc.checked || acc.nonce == math.MaxUint64 + var checkedfunc func() error + if checked { + // before + checkedfunc, err = acc.chekChangeProdoucer(action) + if err != nil { + return + } + } + hash, err = acc.api.SendRawTransaction(rawtx) + if err != nil { + return + } + if checked { + // after + err = acc.utilReceipt(hash, timeout) + if err != nil { + return + } + err = checkedfunc() + if err != nil { + return + } + } + + if acc.nonce != math.MaxUint64 { + acc.nonce++ + } + return +} + +// UnvoteCadidate unvote cadidate +func (acc *Account) UnvoteCadidate(to common.Name, value *big.Int, id uint64, gas uint64) (hash common.Hash, err error) { + nonce := acc.nonce + if nonce == math.MaxUint64 { + nonce, err = acc.api.AccountNonce(acc.name.String()) + if err != nil { + return + } + } + + action := types.NewAction(types.UnvoteCadidate, acc.name, to, nonce, id, gas, value, nil) + tx := types.NewTransaction(acc.feeid, big.NewInt(1e10), []*types.Action{action}...) + key := types.MakeKeyPair(acc.priv, []uint64{0}) + + err = types.SignActionWithMultiKey(action, tx, types.NewSigner(acc.chainID), []*types.KeyPair{key}) + if err != nil { + return + } + rawtx, _ := rlp.EncodeToBytes(tx) + checked := acc.checked || acc.nonce == math.MaxUint64 + var checkedfunc func() error + if checked { + // before + checkedfunc, err = acc.chekUnvoteProdoucer(action) + if err != nil { + return + } + } + hash, err = acc.api.SendRawTransaction(rawtx) + if err != nil { + return + } + if checked { + // after + err = acc.utilReceipt(hash, timeout) + if err != nil { + return + } + err = checkedfunc() + if err != nil { + return + } + } + + if acc.nonce != math.MaxUint64 { + acc.nonce++ + } + return +} + +// UnvoteVoter remove voter +func (acc *Account) UnvoteVoter(to common.Name, value *big.Int, id uint64, gas uint64, arg *dpos.RemoveVoter) (hash common.Hash, err error) { + nonce := acc.nonce + if nonce == math.MaxUint64 { + nonce, err = acc.api.AccountNonce(acc.name.String()) + if err != nil { + return + } + } + + payload, _ := rlp.EncodeToBytes(arg) + action := types.NewAction(types.RemoveVoter, acc.name, to, nonce, id, gas, value, payload) + tx := types.NewTransaction(acc.feeid, big.NewInt(1e10), []*types.Action{action}...) + key := types.MakeKeyPair(acc.priv, []uint64{0}) + + err = types.SignActionWithMultiKey(action, tx, types.NewSigner(acc.chainID), []*types.KeyPair{key}) + if err != nil { + return + } + rawtx, _ := rlp.EncodeToBytes(tx) + checked := acc.checked || acc.nonce == math.MaxUint64 + var checkedfunc func() error + if checked { + // before + checkedfunc, err = acc.chekRemoveVoter(action) + if err != nil { + return + } + } + hash, err = acc.api.SendRawTransaction(rawtx) + if err != nil { + return + } + if checked { + // after + err = acc.utilReceipt(hash, timeout) + if err != nil { + return + } + err = checkedfunc() + if err != nil { + return + } + } + + if acc.nonce != math.MaxUint64 { + acc.nonce++ + } + return +} + +// KickedCadidate kicked cadidates +func (acc *Account) KickedCadidate(to common.Name, value *big.Int, id uint64, gas uint64, arg *dpos.KickedCadidate) (hash common.Hash, err error) { + nonce := acc.nonce + if nonce == math.MaxUint64 { + nonce, err = acc.api.AccountNonce(acc.name.String()) + if err != nil { + return + } + } + + payload, _ := rlp.EncodeToBytes(arg) + action := types.NewAction(types.KickedCadidate, acc.name, to, nonce, id, gas, value, payload) + tx := types.NewTransaction(acc.feeid, big.NewInt(1e10), []*types.Action{action}...) + key := types.MakeKeyPair(acc.priv, []uint64{0}) + + err = types.SignActionWithMultiKey(action, tx, types.NewSigner(acc.chainID), []*types.KeyPair{key}) + if err != nil { + return + } + rawtx, _ := rlp.EncodeToBytes(tx) + checked := acc.checked || acc.nonce == math.MaxUint64 + var checkedfunc func() error + if checked { + // before + checkedfunc, err = acc.chekKickedCadidate(action) + if err != nil { + return + } + } + hash, err = acc.api.SendRawTransaction(rawtx) + if err != nil { + return + } + if checked { + // after + err = acc.utilReceipt(hash, timeout) + if err != nil { + return + } + err = checkedfunc() + if err != nil { + return + } + } + + if acc.nonce != math.MaxUint64 { + acc.nonce++ + } + return +} + +// ExitTakeOver exit take over +func (acc *Account) ExitTakeOver(to common.Name, value *big.Int, id uint64, gas uint64) (hash common.Hash, err error) { + nonce := acc.nonce + if nonce == math.MaxUint64 { + nonce, err = acc.api.AccountNonce(acc.name.String()) + if err != nil { + return + } + } + + action := types.NewAction(types.ExitTakeOver, acc.name, to, nonce, id, gas, value, nil) + tx := types.NewTransaction(acc.feeid, big.NewInt(1e10), []*types.Action{action}...) + key := types.MakeKeyPair(acc.priv, []uint64{0}) + err = types.SignActionWithMultiKey(action, tx, types.NewSigner(acc.chainID), []*types.KeyPair{key}) + if err != nil { + return + } + rawtx, _ := rlp.EncodeToBytes(tx) + checked := acc.checked || acc.nonce == math.MaxUint64 + var checkedfunc func() error + if checked { + // before + checkedfunc, err = acc.chekKickedCadidate(action) + if err != nil { + return + } + } + hash, err = acc.api.SendRawTransaction(rawtx) + if err != nil { + return + } + if checked { + // after + err = acc.utilReceipt(hash, timeout) + if err != nil { + return + } + err = checkedfunc() + if err != nil { + return + } + } + + if acc.nonce != math.MaxUint64 { + acc.nonce++ + } + return +} diff --git a/sdk/account_test.go b/sdk/account_test.go new file mode 100644 index 00000000..84016244 --- /dev/null +++ b/sdk/account_test.go @@ -0,0 +1,211 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package sdk + +import ( + "fmt" + "math" + "math/big" + "testing" + + "github.com/fractalplatform/fractal/accountmanager" + "github.com/fractalplatform/fractal/common" + "github.com/fractalplatform/fractal/consensus/dpos" + "github.com/fractalplatform/fractal/crypto" + "github.com/fractalplatform/fractal/params" + . "github.com/smartystreets/goconvey/convey" +) + +var ( + // tAssetName = "testasset" + // tAssetSymbol = "tat" + // tAmount = new(big.Int).Mul(big.NewInt(1000000), big.NewInt(1e8)) + // tDecimals = uint64(8) + // tAssetID uint64 + rpchost = "http://127.0.0.1:8545" + systemaccount = params.DefaultChainconfig.SysName + accountaccount = params.DefaultChainconfig.AccountName + dposaccount = params.DefaultChainconfig.DposName + systemprivkey = "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032" + systemassetname = params.DefaultChainconfig.SysToken + systemassetid = uint64(1) + chainid = big.NewInt(1) + tValue = new(big.Int).Mul(big.NewInt(300000), big.NewInt(1e18)) + tGas = uint64(90000) +) + +func TestAccount(t *testing.T) { + Convey("Account", t, func() { + api := NewAPI(rpchost) + var systempriv, _ = crypto.HexToECDSA(systemprivkey) + sysAcct := NewAccount(api, common.StrToName(systemaccount), systempriv, systemassetid, math.MaxUint64, true, chainid) + // CreateAccount + priv, pub := GenerateKey() + accountName := common.StrToName(GenerateAccountName("test", 8)) + hash, err := sysAcct.CreateAccount(common.StrToName(accountaccount), tValue, systemassetid, tGas, &accountmanager.AccountAction{ + AccountName: accountName, + PublicKey: pub, + }) + So(err, ShouldBeNil) + So(hash, ShouldNotBeNil) + + // Transfer + hash, err = sysAcct.Transfer(accountName, tValue, systemassetid, tGas) + So(err, ShouldBeNil) + So(hash, ShouldNotBeNil) + + // UpdateAccount + acct := NewAccount(api, accountName, priv, systemassetid, math.MaxUint64, true, chainid) + _, npub := GenerateKey() + hash, err = acct.UpdateAccount(common.StrToName(accountaccount), new(big.Int).Mul(tValue, big.NewInt(0)), systemassetid, tGas, &accountmanager.AccountAction{ + AccountName: accountName, + PublicKey: npub, + }) + So(err, ShouldBeNil) + So(hash, ShouldNotBeNil) + + // DestroyAccount + }) +} + +func TestAsset(t *testing.T) { + // Convey("types.IssueAsset", t, func() { + // api := NewAPI(rpchost) + // var systempriv, _ = crypto.HexToECDSA(systemprivkey) + // sysAcct := NewAccount(api, common.StrToName(systemaccount), systempriv, systemassetid, math.MaxUint64, true, chainid) + // priv, pub := GenerateKey() + // accountName := common.StrToName(GenerateAccountName("test", 8)) + // hash, err := sysAcct.CreateAccount(common.StrToName(systemaccount), tValue, systemassetid, tGas, &accountmanager.AccountAction{ + // AccountName: accountName, + // PublicKey: pub, + // }) + // So(err, ShouldBeNil) + // So(hash, ShouldNotBeNil) + + // acct := NewAccount(api, accountName, priv, systemassetid, math.MaxUint64, true, chainid) + // assetname := common.StrToName(GenerateAccountName("asset", 8)).String() + // // IssueAsset + // hash, err = acct.IssueAsset(accountName, new(big.Int).Div(tValue, big.NewInt(10)), systemassetid, tGas, &asset.AssetObject{ + // AssetName: assetname, + // Symbol: assetname[len(assetname)-4:], + // Amount: new(big.Int).Mul(big.NewInt(10000000), big.NewInt(1e18)), + // Decimals: 18, + // Owner: accountName, + // Founder: accountName, + // AddIssue: big.NewInt(0), + // UpperLimit: big.NewInt(0), + // }) + // So(err, ShouldBeNil) + // So(hash, ShouldNotBeNil) + + // // acct.UpdateAsset() + // // acct.IncreaseAsset() + // // acct.SetAssetOwner() + // // acct.DestroyAsset() + // }) +} + +func TestDPOS(t *testing.T) { + Convey("DPOS", t, func() { + api := NewAPI(rpchost) + var systempriv, _ = crypto.HexToECDSA(systemprivkey) + sysAcct := NewAccount(api, common.StrToName(systemaccount), systempriv, systemassetid, math.MaxUint64, true, chainid) + priv, pub := GenerateKey() + accountName := common.StrToName(GenerateAccountName("prod", 8)) + hash, err := sysAcct.CreateAccount(common.StrToName(accountaccount), tValue, systemassetid, tGas, &accountmanager.AccountAction{ + AccountName: accountName, + PublicKey: pub, + }) + So(err, ShouldBeNil) + So(hash, ShouldNotBeNil) + + priv2, pub2 := GenerateKey() + accountName2 := common.StrToName(GenerateAccountName("voter", 8)) + hash, err = sysAcct.CreateAccount(common.StrToName(accountaccount), tValue, systemassetid, tGas, &accountmanager.AccountAction{ + AccountName: accountName2, + PublicKey: pub2, + }) + So(err, ShouldBeNil) + So(hash, ShouldNotBeNil) + + // RegCadidate + acct := NewAccount(api, accountName, priv, systemassetid, math.MaxUint64, true, chainid) + acct2 := NewAccount(api, accountName2, priv2, systemassetid, math.MaxUint64, true, chainid) + hash, err = acct.RegCadidate(common.StrToName(dposaccount), new(big.Int).Div(tValue, big.NewInt(3)), systemassetid, tGas, &dpos.RegisterCadidate{ + Url: fmt.Sprintf("www.%s.com", accountName.String()), + }) + So(err, ShouldBeNil) + So(hash, ShouldNotBeNil) + + // VoteCadidate + hash, err = acct2.VoteCadidate(common.StrToName(dposaccount), new(big.Int).Div(tValue, big.NewInt(3)), systemassetid, tGas, &dpos.VoteCadidate{ + Cadidate: accountName.String(), + }) + So(err, ShouldBeNil) + So(hash, ShouldNotBeNil) + + // UnvoteCadidate + hash, err = acct2.UnvoteCadidate(common.StrToName(dposaccount), new(big.Int).Mul(tValue, big.NewInt(0)), systemassetid, tGas) + So(err, ShouldBeNil) + So(hash, ShouldNotBeNil) + + // VoteCadidate + hash, err = acct2.VoteCadidate(common.StrToName(dposaccount), new(big.Int).Div(tValue, big.NewInt(3)), systemassetid, tGas, &dpos.VoteCadidate{ + Cadidate: systemaccount, + }) + So(err, ShouldBeNil) + So(hash, ShouldNotBeNil) + + // ChangeCadidate + hash, err = acct2.ChangeCadidate(common.StrToName(dposaccount), new(big.Int).Mul(tValue, big.NewInt(0)), systemassetid, tGas, &dpos.ChangeCadidate{ + Cadidate: accountName.String(), + }) + So(err, ShouldBeNil) + So(hash, ShouldNotBeNil) + + // UnvoteVoter + hash, err = acct.UnvoteVoter(common.StrToName(dposaccount), new(big.Int).Mul(tValue, big.NewInt(0)), systemassetid, tGas, &dpos.RemoveVoter{ + Voters: []string{accountName2.String()}, + }) + So(err, ShouldBeNil) + So(hash, ShouldNotBeNil) + + hash, err = sysAcct.KickedCadidate(common.StrToName(dposaccount), new(big.Int).Mul(tValue, big.NewInt(0)), systemassetid, tGas, &dpos.KickedCadidate{ + Cadidates: []string{accountName.String()}, + }) + So(err, ShouldBeNil) + So(hash, ShouldNotBeNil) + + // UnRegCadidate + hash, err = acct.UnRegCadidate(common.StrToName(dposaccount), new(big.Int).Mul(tValue, big.NewInt(0)), systemassetid, tGas) + So(err, ShouldBeNil) + So(hash, ShouldNotBeNil) + }) +} +func TestManual(t *testing.T) { + Convey("Manual", t, func() { + api := NewAPI(rpchost) + var systempriv, _ = crypto.HexToECDSA(systemprivkey) + sysAcct := NewAccount(api, common.StrToName(systemaccount), systempriv, systemassetid, math.MaxUint64, true, chainid) + + hash, err := sysAcct.KickedCadidate(common.StrToName(dposaccount), new(big.Int).Mul(tValue, big.NewInt(0)), systemassetid, tGas, &dpos.KickedCadidate{ + Cadidates: []string{"ftcadidate1", "ftcadidate2", "ftcadidate3"}, + }) + So(err, ShouldBeNil) + So(hash, ShouldNotBeNil) + }) +} diff --git a/sdk/accountck.go b/sdk/accountck.go new file mode 100644 index 00000000..3483efb8 --- /dev/null +++ b/sdk/accountck.go @@ -0,0 +1,173 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package sdk + +import ( + "fmt" + "time" + + "github.com/fractalplatform/fractal/common" + "github.com/fractalplatform/fractal/types" +) + +func (acc *Account) utilReceipt(hash common.Hash, timeout int64) error { + cnt := int64(10) + ticker := time.NewTicker(time.Duration(timeout / cnt)) + defer ticker.Stop() + for { + select { + case <-ticker.C: + r, _ := acc.api.TransactionReceiptByHash(hash) + if r != nil && r.BlockNumber > 0 { + if len(r.ActionResults[0].Error) > 0 { + return fmt.Errorf(r.ActionResults[0].Error) + } + return nil + } + cnt-- + if cnt == 0 { + return fmt.Errorf("not found %v receipt", hash.String()) + } + } + } +} + +func (acc *Account) checkCreateAccount(action *types.Action) (func() error, error) { + function := func() error { + return nil + } + return function, nil +} + +func (acc *Account) checkUpdateAccount(action *types.Action) (func() error, error) { + function := func() error { + return nil + } + return function, nil +} + +func (acc *Account) checkTranfer(action *types.Action) (func() error, error) { + // TODO + function := func() error { + return nil + } + return function, nil +} + +func (acc *Account) checkIssueAsset(action *types.Action) (func() error, error) { + // TODO + function := func() error { + return nil + } + return function, nil +} + +func (acc *Account) checkUpdateAsset(action *types.Action) (func() error, error) { + // TODO + function := func() error { + return nil + } + return function, nil +} + +func (acc *Account) checkDestroyAsset(action *types.Action) (func() error, error) { + // TODO + function := func() error { + return nil + } + return function, nil +} + +func (acc *Account) checkIncreaseAsset(action *types.Action) (func() error, error) { + // TODO + function := func() error { + return nil + } + return function, nil +} + +func (acc *Account) checkSetAssetOwner(action *types.Action) (func() error, error) { + // TODO + function := func() error { + return nil + } + return function, nil +} + +func (acc *Account) chekRegProdoucer(action *types.Action) (func() error, error) { + // TODO + function := func() error { + return nil + } + return function, nil +} + +func (acc *Account) chekUpdateProdoucer(action *types.Action) (func() error, error) { + // TODO + function := func() error { + return nil + } + return function, nil +} + +func (acc *Account) chekUnregProdoucer(action *types.Action) (func() error, error) { + // TODO + function := func() error { + return nil + } + return function, nil +} + +func (acc *Account) chekVoteProdoucer(action *types.Action) (func() error, error) { + // TODO + function := func() error { + return nil + } + return function, nil +} + +func (acc *Account) chekChangeProdoucer(action *types.Action) (func() error, error) { + // TODO + function := func() error { + return nil + } + return function, nil +} + +func (acc *Account) chekUnvoteProdoucer(action *types.Action) (func() error, error) { + // TODO + function := func() error { + return nil + } + return function, nil +} + +func (acc *Account) chekRemoveVoter(action *types.Action) (func() error, error) { + // TODO + function := func() error { + return nil + } + return function, nil +} + +func (acc *Account) chekKickedCadidate(action *types.Action) (func() error, error) { + // TODO + function := func() error { + return nil + } + return function, nil +} diff --git a/wallet/keystore/errors.go b/sdk/api.go similarity index 64% rename from wallet/keystore/errors.go rename to sdk/api.go index 5872ba9f..0a8c12a2 100644 --- a/wallet/keystore/errors.go +++ b/sdk/api.go @@ -14,11 +14,28 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package keystore +package sdk -import "errors" +import ( + "fmt" -var ( - // ErrDecrypt decrypt key error - ErrDecrypt = errors.New("could not decrypt key with given passphrase") + "github.com/fractalplatform/fractal/rpc" ) + +// API rpc api +type API struct { + rpchost string + client *rpc.Client +} + +// NewAPI create api interface +func NewAPI(rpchost string) *API { + client, err := rpc.DialHTTP(rpchost) + if err != nil { + panic(fmt.Sprintf("dial http %v err %v", rpchost, err)) + } + api := &API{} + api.rpchost = rpchost + api.client = client + return api +} diff --git a/sdk/api_account.go b/sdk/api_account.go new file mode 100644 index 00000000..ca44c372 --- /dev/null +++ b/sdk/api_account.go @@ -0,0 +1,72 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package sdk + +import ( + "math/big" + + "github.com/fractalplatform/fractal/asset" +) + +// AccountIsExist account exist +func (api *API) AccountIsExist(name string) (bool, error) { + isExist := false + err := api.client.Call(&isExist, "account_accountIsExist", name) + return isExist, err +} + +// AccountInfo get account by name +func (api *API) AccountInfo(name string) (map[string]interface{}, error) { + account := map[string]interface{}{} + err := api.client.Call(account, "account_getAccountByName", name) + return account, err +} + +// AccountCode get account code +func (api *API) AccountCode(name string) ([]byte, error) { + code := []byte{} + err := api.client.Call(code, "account_getCode", name) + return code, err +} + +// AccountNonce get account nonce +func (api *API) AccountNonce(name string) (uint64, error) { + nonce := uint64(0) + err := api.client.Call(&nonce, "account_getNonce", name) + return nonce, err +} + +// AssetInfoByName get asset info +func (api *API) AssetInfoByName(name string) (*asset.AssetObject, error) { + assetInfo := &asset.AssetObject{} + err := api.client.Call(assetInfo, "account_getAssetInfoByName", name) + return assetInfo, err +} + +// AssetInfoByID get asset info +func (api *API) AssetInfoByID(id uint64) (*asset.AssetObject, error) { + assetInfo := &asset.AssetObject{} + err := api.client.Call(assetInfo, "account_getAssetInfoByID", id) + return assetInfo, err +} + +// BalanceByAssetID get asset balance +func (api *API) BalanceByAssetID(name string, id uint64, typeID uint64) (*big.Int, error) { + balance := big.NewInt(0) + err := api.client.Call(balance, "account_getAccountBalanceByID", name, id, typeID) + return balance, err +} diff --git a/sdk/api_account_test.go b/sdk/api_account_test.go new file mode 100644 index 00000000..c162bc0f --- /dev/null +++ b/sdk/api_account_test.go @@ -0,0 +1,80 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package sdk + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestAccountIsExist(t *testing.T) { + Convey("account_accountIsExist", t, func() { + api := NewAPI(rpchost) + existed, err := api.AccountIsExist(systemaccount) + So(err, ShouldBeNil) + So(existed, ShouldBeTrue) + }) +} +func TestAccountInfo(t *testing.T) { + Convey("account_getAccountByName", t, func() { + api := NewAPI(rpchost) + acct, err := api.AccountInfo(systemaccount) + So(err, ShouldBeNil) + So(acct, ShouldNotBeNil) + }) +} +func TestAccountCode(t *testing.T) { + Convey("account_getCode", t, func() { + api := NewAPI(rpchost) + code, err := api.AccountCode(systemaccount) + So(err, ShouldNotBeNil) + So(code, ShouldBeEmpty) + }) +} +func TestAccountNonce(t *testing.T) { + Convey("account_getNonce", t, func() { + api := NewAPI(rpchost) + nonce, err := api.AccountNonce(systemaccount) + So(err, ShouldBeNil) + So(nonce, ShouldNotBeNil) + }) +} +func TestAssetInfoByName(t *testing.T) { + Convey("account_getAssetInfoByName", t, func() { + api := NewAPI(rpchost) + asset, err := api.AssetInfoByName(systemassetname) + So(err, ShouldBeNil) + So(asset, ShouldNotBeNil) + }) +} +func TestAssetInfoByID(t *testing.T) { + Convey("account_getAssetInfoByID", t, func() { + api := NewAPI(rpchost) + asset, err := api.AssetInfoByID(systemassetid) + So(err, ShouldBeNil) + So(asset, ShouldNotBeNil) + }) +} +func TestBalanceByAssetID(t *testing.T) { + Convey("account_getAccountBalanceByID", t, func() { + api := NewAPI(rpchost) + balance, err := api.BalanceByAssetID(systemaccount, systemassetid, 0) + So(err, ShouldBeNil) + So(balance, ShouldNotBeNil) + }) +} diff --git a/sdk/api_dpos.go b/sdk/api_dpos.go new file mode 100644 index 00000000..6e791f84 --- /dev/null +++ b/sdk/api_dpos.go @@ -0,0 +1,66 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package sdk + +// DposInfo dpos info +func (api *API) DposInfo() (map[string]interface{}, error) { + info := map[string]interface{}{} + err := api.client.Call(&info, "dpos_info") + return info, err +} + +// DposIrreversible dpos irreversible info +func (api *API) DposIrreversible() (map[string]interface{}, error) { + info := map[string]interface{}{} + err := api.client.Call(&info, "dpos_irreversible") + return info, err +} + +// DposEpcho dpos state info by height +func (api *API) DposEpcho(height uint64) (map[string]interface{}, error) { + info := map[string]interface{}{} + err := api.client.Call(&info, "dpos_epcho", height) + return info, err +} + +// DposLatestEpcho dpos state info by height +func (api *API) DposLatestEpcho() (map[string]interface{}, error) { + info := map[string]interface{}{} + err := api.client.Call(&info, "dpos_latestEpcho") + return info, err +} + +// DposValidateEpcho dpos state info +func (api *API) DposValidateEpcho() (map[string]interface{}, error) { + info := map[string]interface{}{} + err := api.client.Call(&info, "dpos_validateEpcho") + return info, err +} + +// DposCadidates dpos cadidate info +func (api *API) DposCadidates() ([]map[string]interface{}, error) { + info := []map[string]interface{}{} + err := api.client.Call(&info, "dpos_cadidates") + return info, err +} + +// DposAccount dpos account info +func (api *API) DposAccount(name string) (map[string]interface{}, error) { + info := map[string]interface{}{} + err := api.client.Call(&info, "dpos_account", name) + return info, err +} diff --git a/sdk/api_dpos_test.go b/sdk/api_dpos_test.go new file mode 100644 index 00000000..98b54c00 --- /dev/null +++ b/sdk/api_dpos_test.go @@ -0,0 +1,80 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package sdk + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestDposInfo(t *testing.T) { + Convey("dpos_info", t, func() { + api := NewAPI(rpchost) + info, err := api.DposInfo() + So(err, ShouldBeNil) + So(info, ShouldNotBeEmpty) + }) +} +func TestDposIrreversible(t *testing.T) { + Convey("dpos_irreversible", t, func() { + api := NewAPI(rpchost) + info, err := api.DposIrreversible() + So(err, ShouldBeNil) + So(info, ShouldNotBeEmpty) + }) +} +func TestDposEpcho(t *testing.T) { + Convey("dpos_epcho", t, func() { + api := NewAPI(rpchost) + info, err := api.DposEpcho(0) + So(err, ShouldBeNil) + So(info, ShouldNotBeEmpty) + }) +} +func TestDposLatestEpcho(t *testing.T) { + Convey("dpos_latestEpcho", t, func() { + api := NewAPI(rpchost) + info, err := api.DposLatestEpcho() + So(err, ShouldBeNil) + So(info, ShouldNotBeEmpty) + }) +} +func TestDposValidateEpcho(t *testing.T) { + Convey("dpos_validateEpcho", t, func() { + api := NewAPI(rpchost) + info, err := api.DposValidateEpcho() + So(err, ShouldBeNil) + So(info, ShouldNotBeEmpty) + }) +} +func TestDposCadidates(t *testing.T) { + Convey("dpos_cadidates", t, func() { + api := NewAPI(rpchost) + info, err := api.DposCadidates() + So(err, ShouldBeNil) + So(info, ShouldNotBeEmpty) + }) +} +func TestDposAccount(t *testing.T) { + Convey("dpos_account", t, func() { + api := NewAPI(rpchost) + info, err := api.DposAccount(systemaccount) + So(err, ShouldBeNil) + So(info, ShouldNotBeEmpty) + }) +} diff --git a/sdk/api_ft.go b/sdk/api_ft.go new file mode 100644 index 00000000..ff2e0f28 --- /dev/null +++ b/sdk/api_ft.go @@ -0,0 +1,75 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package sdk + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/fractalplatform/fractal/common" + "github.com/fractalplatform/fractal/rpc" + "github.com/fractalplatform/fractal/types" +) + +// SendRawTransaction send tx +func (api *API) SendRawTransaction(rawTx []byte) (common.Hash, error) { + hash := new(common.Hash) + err := api.client.Call(hash, "ft_sendRawTransaction", hexutil.Bytes(rawTx)) + return *hash, err +} + +// CurrentBlock current block info +func (api *API) CurrentBlock(fullTx bool) (map[string]interface{}, error) { + block := map[string]interface{}{} + err := api.client.Call(&block, "ft_getCurrentBlock", fullTx) + return block, err +} + +// BlockByHash block info +func (api *API) BlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error) { + block := map[string]interface{}{} + err := api.client.Call(&block, "ft_getBlockByHash", hash, fullTx) + return block, err +} + +// BlockByNumber block info +func (api *API) BlockByNumber(number int64, fullTx bool) (map[string]interface{}, error) { + block := map[string]interface{}{} + err := api.client.Call(&block, "ft_getBlockByNumber", rpc.BlockNumber(number), fullTx) + return block, err +} + +// TransactionByHash tx info +func (api *API) TransactionByHash(hash common.Hash) (*types.RPCTransaction, error) { + tx := &types.RPCTransaction{} + err := api.client.Call(tx, "ft_getTransactionByHash", hash) + return tx, err +} + +// TransactionReceiptByHash tx info +func (api *API) TransactionReceiptByHash(hash common.Hash) (*types.RPCReceipt, error) { + receipt := &types.RPCReceipt{} + err := api.client.Call(receipt, "ft_getTransactionReceipt", hash) + return receipt, err +} + +// GasPrice gas price +func (api *API) GasPrice() (*big.Int, error) { + gasprice := big.NewInt(0) + err := api.client.Call(gasprice, "ft_gasPrice") + return gasprice, err +} diff --git a/sdk/api_ft_test.go b/sdk/api_ft_test.go new file mode 100644 index 00000000..7652eed6 --- /dev/null +++ b/sdk/api_ft_test.go @@ -0,0 +1,82 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package sdk + +import ( + "testing" + + "github.com/fractalplatform/fractal/common" + . "github.com/smartystreets/goconvey/convey" +) + +func TestCurrentBlock(t *testing.T) { + Convey("ft_getCurrentBlock", t, func() { + api := NewAPI(rpchost) + block, err := api.CurrentBlock(false) + So(err, ShouldBeNil) + So(block, ShouldNotBeNil) + }) +} + +func TestBlockByHash(t *testing.T) { + Convey("ft_getBlockByHash", t, func() { + api := NewAPI(rpchost) + block, err := api.CurrentBlock(false) + So(err, ShouldBeNil) + hash := common.HexToHash(block["hash"].(string)) + block, err = api.BlockByHash(hash, false) + So(err, ShouldBeNil) + So(block, ShouldNotBeNil) + }) +} + +func TestBlockByNumber(t *testing.T) { + Convey("ft_getBlockByNumber", t, func() { + api := NewAPI(rpchost) + block, err := api.CurrentBlock(false) + So(err, ShouldBeNil) + block, err = api.BlockByNumber((int64(block["number"].(float64))), false) + So(err, ShouldBeNil) + So(block, ShouldNotBeNil) + }) +} +func TestTransactionByHash(t *testing.T) { + Convey("ft_getTransactionByHash", t, func() { + hash := common.HexToHash("") + api := NewAPI(rpchost) + tx, err := api.TransactionByHash(hash) + So(err, ShouldBeNil) + So(tx, ShouldNotBeNil) + }) +} +func TestTransactionReceiptByHash(t *testing.T) { + Convey("ft_getTransactionReceipt", t, func() { + hash := common.HexToHash("") + api := NewAPI(rpchost) + receipt, err := api.TransactionReceiptByHash(hash) + So(err, ShouldBeNil) + So(receipt, ShouldNotBeNil) + }) +} +func TestGasPrice(t *testing.T) { + Convey("ft_gasPrice", t, func() { + api := NewAPI(rpchost) + gasprice, err := api.GasPrice() + So(err, ShouldBeNil) + So(gasprice, ShouldNotBeNil) + }) +} diff --git a/sdk/api_miner.go b/sdk/api_miner.go new file mode 100644 index 00000000..eefde0ce --- /dev/null +++ b/sdk/api_miner.go @@ -0,0 +1,52 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package sdk + +// MinerStart start +func (api *API) MinerStart() (bool, error) { + ret := false + err := api.client.Call(&ret, "miner_start") + return ret, err +} + +// MinerStop stop +func (api *API) MinerStop() (bool, error) { + ret := false + err := api.client.Call(&ret, "miner_stop") + return ret, err +} + +// MinerMining mining +func (api *API) MinerMining() (bool, error) { + ret := false + err := api.client.Call(&ret, "miner_mining") + return ret, err +} + +// MinerSetExtra extra +func (api *API) MinerSetExtra(extra []byte) (bool, error) { + ret := true + err := api.client.Call(&ret, "miner_setExtra", extra) + return ret, err +} + +// MinerSetCoinbase coinbase +func (api *API) MinerSetCoinbase(name string, privKeys []string) (bool, error) { + ret := true + err := api.client.Call(&ret, "miner_setCoinbase", name, privKeys) + return ret, err +} diff --git a/sdk/api_miner_test.go b/sdk/api_miner_test.go new file mode 100644 index 00000000..75ee68ec --- /dev/null +++ b/sdk/api_miner_test.go @@ -0,0 +1,81 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package sdk + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestMinerMining(t *testing.T) { + Convey("miner_mining", t, func() { + api := NewAPI(rpchost) + _, err := api.MinerMining() + So(err, ShouldBeNil) + }) +} +func TestMinerStart(t *testing.T) { + Convey("miner_start", t, func() { + api := NewAPI(rpchost) + mining, _ := api.MinerMining() + ret, err := api.MinerStart() + So(err, ShouldBeNil) + if mining { + So(ret, ShouldBeFalse) + } else { + So(ret, ShouldBeTrue) + } + }) +} +func TestMinerStop(t *testing.T) { + Convey("miner_stop", t, func() { + api := NewAPI(rpchost) + mining, _ := api.MinerMining() + ret, err := api.MinerStop() + So(err, ShouldBeNil) + if mining { + So(ret, ShouldBeTrue) + } else { + So(ret, ShouldBeFalse) + } + }) +} +func TestMinerSetExtra(t *testing.T) { + Convey("miner_setExtra", t, func() { + api := NewAPI(rpchost) + ret, err := api.MinerSetExtra([]byte("testextra")) + So(err, ShouldBeNil) + So(ret, ShouldBeTrue) + }) +} +func TestMinerSetCoinbase(t *testing.T) { + Convey("miner_setCoinbase", t, func() { + api := NewAPI(rpchost) + ret, err := api.MinerSetCoinbase(systemaccount, []string{systemprivkey}) + So(err, ShouldBeNil) + So(ret, ShouldBeTrue) + }) +} +func TestMinerSetExtraTooLong(t *testing.T) { + Convey("miner_setExtra", t, func() { + api := NewAPI(rpchost) + ret, err := api.MinerSetExtra([]byte("testextratestextratestextratestextratestextratestextra")) + So(err, ShouldNotBeNil) + So(ret, ShouldBeTrue) + }) +} diff --git a/wallet/keystore/keyjson_test.go b/sdk/utils.go similarity index 53% rename from wallet/keystore/keyjson_test.go rename to sdk/utils.go index 423e5154..a8947c28 100644 --- a/wallet/keystore/keyjson_test.go +++ b/sdk/utils.go @@ -14,39 +14,35 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package keystore +package sdk import ( "crypto/ecdsa" - crand "crypto/rand" - "testing" + "math/rand" + "time" + "github.com/fractalplatform/fractal/common" "github.com/fractalplatform/fractal/crypto" - "github.com/stretchr/testify/assert" ) -func TestEncryptAndDecryptKey(t *testing.T) { - keyj := new(keyJSON) - - privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), crand.Reader) - if err != nil { - t.Fatal(err) - } - - key := &Key{ - Addr: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey), - PrivateKey: privateKeyECDSA, - } - - err = keyj.encryptKey(key, "pwd", StandardScryptN, StandardScryptP) - if err != nil { - t.Fatal(err) - } +var ( + availableChars = "abcdefghijklmnopqrstuvwxyz0123456789" +) - newk, err := keyj.decryptKey("pwd") - if err != nil { - t.Fatal(err) +// GenerateAccountName generate account name +func GenerateAccountName(namePrefix string, addStrLen int) string { + newRandomName := namePrefix + size := len(availableChars) + rand.Seed(time.Now().UnixNano()) + for i := 0; i < addStrLen; i++ { + index := rand.Intn(10000) % size + newRandomName += string(availableChars[index]) } + return newRandomName +} - assert.Equal(t, newk, key) +// GenerateKey generate pubkey and privkey +func GenerateKey() (*ecdsa.PrivateKey, common.PubKey) { + prikey, _ := crypto.GenerateKey() + return prikey, common.BytesToPubKey(crypto.FromECDSAPub(&prikey.PublicKey)) } diff --git a/state/mtp/encoding.go b/state/mtp/encoding.go index 76aba61d..d121275b 100644 --- a/state/mtp/encoding.go +++ b/state/mtp/encoding.go @@ -25,14 +25,6 @@ package mtp // 'terminator' byte of value 0x10 which indicates whether or not the node at the key // contains a value. Hex key encoding is used for nodes loaded in memory because it's // convenient to access. -// -// COMPACT encoding is defined by the Ethereum Yellow Paper (it's called "hex prefix -// encoding" there) and contains the bytes of the key and a flag. The high nibble of the -// first byte contains the flag; the lowest bit encoding the oddness of the length and -// the second-lowest encoding whether the node at the key is a value node. The low nibble -// of the first byte is zero in the case of an even number of nibbles and the first nibble -// in the case of an odd number. All remaining nibbles (now an even number) fit properly -// into the remaining bytes. Compact encoding is used for nodes stored on disk. func hexToCompact(hex []byte) []byte { terminator := byte(0) diff --git a/state/statedb.go b/state/statedb.go index 80a6ff88..cd604041 100644 --- a/state/statedb.go +++ b/state/statedb.go @@ -22,11 +22,12 @@ import ( "errors" "fmt" "sort" + "strings" "sync" - "time" "github.com/ethereum/go-ethereum/log" "github.com/fractalplatform/fractal/common" + "github.com/fractalplatform/fractal/event" "github.com/fractalplatform/fractal/rawdb" trie "github.com/fractalplatform/fractal/state/mtp" "github.com/fractalplatform/fractal/types" @@ -49,6 +50,9 @@ type StateDB struct { db Database trie Trie + iterator *trie.Iterator + triehd *trie.SecureTrie + readSet map[string][]byte // save old/unmodified data writeSet map[string][]byte // last modify data dirtySet map[string]struct{} // writeSet which key is modified @@ -90,6 +94,8 @@ func New(root common.Hash, db Database) (*StateDB, error) { return &StateDB{ db: db, trie: tr, + iterator: nil, + triehd: nil, readSet: make(map[string][]byte), writeSet: make(map[string][]byte), dirtySet: make(map[string]struct{}), @@ -117,6 +123,8 @@ func (s *StateDB) Reset(root common.Hash) error { } s.trie = tr + s.iterator = nil + s.triehd = nil s.readSet = make(map[string][]byte) s.writeSet = make(map[string][]byte) s.dirtySet = make(map[string]struct{}) @@ -279,6 +287,8 @@ func (s *StateDB) Copy() *StateDB { state := &StateDB{ db: s.db, trie: s.trie, + iterator: nil, + triehd: nil, readSet: make(map[string][]byte, len(s.writeSet)), writeSet: make(map[string][]byte, len(s.writeSet)), dirtySet: make(map[string]struct{}, len(s.dirtySet)), @@ -482,131 +492,184 @@ func TraceNew(blockHash common.Hash, cache Database) (*StateDB, error) { return stateDb, nil } -func SnapShotblk(db fdb.Database, Ticktime, snapshotTime uint64) { // snapshotTime uint : second +const second = 1000000000 - var curSnapshotTime uint64 +type SnapshotSt struct { + db fdb.Database + tickTime uint64 + snapshotTime uint64 + intervalTime uint64 + stop chan struct{} +} + +func NewSnapshot(db fdb.Database, sptime uint64) *SnapshotSt { + snapshot := &SnapshotSt{ + db: db, + snapshotTime: sptime, + stop: make(chan struct{}), + intervalTime: (sptime * second), + } + + return snapshot +} + +func (sn *SnapshotSt) Start() { + log.Info("Snapshot start", "snapshot interval=", sn.snapshotTime) + go sn.SnapShotblk() +} + +func (sn *SnapshotSt) Stop() { + close(sn.stop) + log.Info("Snapshot stopped") +} + +func (sn *SnapshotSt) checkInterrupt() bool { + select { + case <-sn.stop: + return true + default: + return false + } +} + +func (sn *SnapshotSt) getBlcok(blockNum uint64) (uint64, *types.Header) { + if sn.checkInterrupt() { + return 0, nil + } + hash := rawdb.ReadCanonicalHash(sn.db, blockNum) + head := rawdb.ReadHeader(sn.db, hash, blockNum) + time := head.Time.Uint64() + return time, head +} + +func (sn *SnapshotSt) getlastBlcok() (uint64, uint64) { + if sn.checkInterrupt() { + return 0, 0 + } + hash := rawdb.ReadHeadBlockHash(sn.db) + number := rawdb.ReadHeaderNumber(sn.db, hash) + head := rawdb.ReadHeader(sn.db, hash, *number) + time := head.Time.Uint64() + return time, *number +} + +func (sn *SnapshotSt) writeSnapshot(snapshottime, prevsnptime, number uint64, root common.Hash) error { + if sn.checkInterrupt() { + return fmt.Errorf("interrupt") + } + + snapshotmsg := types.SnapshotMsg{ + Time: prevsnptime, + Number: number, + Root: root, + } + + batch := sn.db.NewBatch() + rawdb.WriteBlockSnapshotTime(batch, snapshottime, snapshotmsg) + rawdb.WriteBlockSnapshotLast(batch, snapshottime) + if err := batch.Write(); err != nil { + return err + } + return nil +} + +func (sn *SnapshotSt) lookupBlock(blockNum, prevBlockTime, lastBlockTime, lastBlockNum uint64) (uint64, *types.Header) { + var nextHead *types.Header + var nextTimeHour, nextTime uint64 + prevTimeHour := (prevBlockTime / sn.intervalTime) * sn.intervalTime + + for { + nextTime, nextHead = sn.getBlcok(blockNum) + if nextHead == nil { + return 0, nil + } + + nextTimeHour = (nextTime / sn.intervalTime) * sn.intervalTime + if prevTimeHour == nextTimeHour { + blockNum = blockNum + 1 + if blockNum < lastBlockNum { + continue + } else { + return 0, nil + } + } else { + if lastBlockTime-nextTime >= sn.intervalTime { + break + } else { + return 0, nil + } + } + } + return nextTimeHour, nextHead +} + +func (sn *SnapshotSt) snapshotRecord(block *types.Block) bool { var blockNum uint64 - var snapshotInfo types.SnapshotMsg + var curSnapshotTime uint64 + var prevTime uint64 + var head *types.Header - var prevHash, nextHash common.Hash - var prevTimeHour, nextTimeHour uint64 - log.Info("Start snapshot", "tick=", Ticktime, "interval=", snapshotTime) + snapshotTimeLast := rawdb.ReadSnapshotLast(sn.db) + if len(snapshotTimeLast) == 0 { + blockNum = 0 + prevTime, head = sn.getBlcok(blockNum) + if head == nil { + return false + } + curSnapshotTime = (prevTime / sn.intervalTime) * sn.intervalTime + } else { + curSnapshotTime = binary.BigEndian.Uint64(snapshotTimeLast) + snapshotInfo := rawdb.ReadSnapshotTime(sn.db, curSnapshotTime) + blockNum = snapshotInfo.Number + prevTime, head = sn.getBlcok(blockNum) + if head == nil { + return false + } + } - futureTimer := time.NewTicker(time.Duration(Ticktime) * time.Second) - defer futureTimer.Stop() + curBlockTime, lastBlockNum := block.Head.Time.Uint64(), block.Head.Number.Uint64() + if blockNum >= lastBlockNum { + return false + } + + if curBlockTime-curSnapshotTime > 2*sn.intervalTime { + blockNum = blockNum + 1 + nextTimeHour, nextHead := sn.lookupBlock(blockNum, prevTime, curBlockTime, lastBlockNum) + if nextHead != nil { + err := sn.writeSnapshot(nextTimeHour, curSnapshotTime, nextHead.Number.Uint64(), nextHead.Root) + if err != nil { + log.Error("Failed to write snapshot to disk", "err", err) + } else { + log.Debug("Sanpshot time", "time", nextTimeHour, "number", nextHead.Number.Uint64(), "hash", nextHead.Root.String()) + } + } else { + return false + } + } + + return false +} + +const ( + // chainHeadChanSize is the size of channel listening to ChainHeadEvent. + chainHeadChanSize = 10 +) + +func (sn *SnapshotSt) SnapShotblk() { + chainHeadCh := make(chan *event.Event, chainHeadChanSize) + chainHeadSub := event.Subscribe(nil, chainHeadCh, event.ChainHeadEv, &types.Block{}) + defer chainHeadSub.Unsubscribe() for { select { - case <-futureTimer.C: - for { - snapshotTimeLast := rawdb.ReadSnapshotLast(db) - if len(snapshotTimeLast) == 0 { - blockNum = 0 - prevHash = rawdb.ReadCanonicalHash(db, blockNum) - prevHead := rawdb.ReadHeader(db, prevHash, blockNum) - prevTime := prevHead.Time.Uint64() - curSnapshotTime = (prevTime / (snapshotTime * 1000000000)) * (snapshotTime * 1000000000) - - curBlockHash := rawdb.ReadHeadBlockHash(db) - number := rawdb.ReadHeaderNumber(db, curBlockHash) - curBlockHeader := rawdb.ReadHeader(db, curBlockHash, *number) - curBlockTime := curBlockHeader.Time.Uint64() - - if curBlockTime-curSnapshotTime > 2*(snapshotTime*1000000000) { - for { - blockNum = blockNum + 1 - - nextHash = rawdb.ReadCanonicalHash(db, blockNum) - nextHead := rawdb.ReadHeader(db, nextHash, blockNum) - - prevTimeHour = (prevTime / (snapshotTime * 1000000000)) * (snapshotTime * 1000000000) - nextTime := nextHead.Time.Uint64() - nextTimeHour = (nextTime / (snapshotTime * 1000000000)) * (snapshotTime * 1000000000) - - if prevTimeHour == nextTimeHour { - prevTime = nextTime - continue - } else { - if curBlockTime-nextTimeHour < (snapshotTime*1000000000) || curBlockTime-nextTime < (snapshotTime*1000000000) { - break - } - - snapshotInfo.Time = 0 - snapshotInfo.Number = nextHead.Number.Uint64() - snapshotInfo.Root = nextHead.Root - - batch := db.NewBatch() - rawdb.WriteBlockSnapshotTime(batch, nextTimeHour, snapshotInfo) - rawdb.WriteBlockSnapshotLast(batch, nextTimeHour) - if err := batch.Write(); err != nil { - log.Error("Failed to write snapshot to disk", "err", err) - } - log.Debug("Sanpshot moment", "time=", nextTimeHour) - break - } - } - - } - } else { - curSnapshotTime = binary.BigEndian.Uint64(snapshotTimeLast) - snapshotInfo := rawdb.ReadSnapshotTime(db, curSnapshotTime) - blockNum = snapshotInfo.Number - - curBlockHash := rawdb.ReadHeadBlockHash(db) - number := rawdb.ReadHeaderNumber(db, curBlockHash) - curBlockHeader := rawdb.ReadHeader(db, curBlockHash, *number) - curBlockTime := curBlockHeader.Time.Uint64() - - if curBlockTime-curSnapshotTime > 2*(snapshotTime*1000000000) { - blockNum = blockNum + 1 - - prevHash = rawdb.ReadCanonicalHash(db, blockNum) - prevHead := rawdb.ReadHeader(db, prevHash, blockNum) - prevTime := prevHead.Time.Uint64() - - for { - blockNum = blockNum + 1 - - nextHash = rawdb.ReadCanonicalHash(db, blockNum) - nextHead := rawdb.ReadHeader(db, nextHash, blockNum) - - prevTimeHour = (prevTime / (snapshotTime * 1000000000)) * (snapshotTime * 1000000000) - nextTime := nextHead.Time.Uint64() - nextTimeHour = (nextTime / (snapshotTime * 1000000000)) * (snapshotTime * 1000000000) - - if prevTimeHour == nextTimeHour { - prevTime = nextTime - continue - } else { - if curBlockTime-nextTimeHour < (snapshotTime * 1000000000) { - break - } - - snapshotInfo.Time = curSnapshotTime - snapshotInfo.Number = nextHead.Number.Uint64() - snapshotInfo.Root = nextHead.Root - batch := db.NewBatch() - rawdb.WriteBlockSnapshotTime(batch, nextTimeHour, *snapshotInfo) - rawdb.WriteBlockSnapshotLast(batch, nextTimeHour) - if err := batch.Write(); err != nil { - log.Error("Failed to write snapshot to disk", "err", err) - } - log.Debug("Sanpshot moment", "time=", nextTimeHour) - break - } - - } - - } - - if curBlockTime-nextTimeHour > 2*(snapshotTime*1000000000) { - continue - } else { - break - } - - } + case ev := <-chainHeadCh: + // Handle ChainHeadEvent + if sn.checkInterrupt() { + return } + + blk := ev.Data.(*types.Block) + sn.snapshotRecord(blk) } } } @@ -658,3 +721,75 @@ func (s *StateDB) GetSnapshotPrev(time uint64) (uint64, error) { } return snapshotInfo.Time, nil } + +func (s *StateDB) StartGetAccountInfo(time uint64) error { + var err error + if time == 0 { + return fmt.Errorf("Not snapshot info") + } + + // optKey := acctDataPrefix + linkSymbol + account + linkSymbol + key + db := s.db.GetDB() + snapshotInfo := rawdb.ReadSnapshotTime(db, time) + if snapshotInfo == nil { + return fmt.Errorf("Not snapshot info") + } + + triedb := trie.NewDatabase(db) + s.triehd, err = trie.NewSecure(snapshotInfo.Root, triedb, 1) + if err != nil { + return err + } + + s.iterator = trie.NewIterator(s.triehd.NodeIterator(nil)) + return nil + +} + +func (s *StateDB) LookupAccountInfo() ([]types.AccountInfo, bool) { + var endFlag bool + var count uint64 = 1000 + var accountInfo []types.AccountInfo + + if s.iterator == nil { + return nil, false + } + + for !endFlag { + flag := s.iterator.Next() + if flag == false { + return accountInfo, false + } + acckey := parseAcckey(string(s.triehd.GetKey(s.iterator.Key))) + if len(acckey) == 0 { + continue + } + count = count - 1 + accountInfo = append(accountInfo, types.AccountInfo{ + Name: acckey[0], + Key: acckey[1], + Value: s.iterator.Value}) + + if count == 0 { + endFlag = true + } + + } + return accountInfo, true + +} + +func (s *StateDB) StopGetAccountInfo() error { + s.iterator = nil + s.triehd = nil + return nil +} + +func parseAcckey(s string) []string { + if !strings.HasPrefix(s, acctDataPrefix+linkSymbol) { + return nil + } + tps := strings.TrimPrefix(s, acctDataPrefix+linkSymbol) + acckey := strings.SplitN(tps, linkSymbol, 2) + return acckey +} diff --git a/state/statedb_test.go b/state/statedb_test.go index 34b28fb1..9e629791 100644 --- a/state/statedb_test.go +++ b/state/statedb_test.go @@ -213,6 +213,10 @@ func TestSnapshot(t *testing.T) { Time: big.NewInt(1548582552502000000), } + block := &types.Block{ + Head: head, + } + hash := head.Hash() root1, err := state.Commit(batch, hash, 0) @@ -227,9 +231,12 @@ func TestSnapshot(t *testing.T) { rawdb.WriteHeader(batch, head) rawdb.WriteHeadBlockHash(batch, hash) rawdb.WriteCanonicalHash(batch, hash, 0) + // rawdb.WriteBlock(batch, block) batch.Write() - go SnapShotblk(db, 3, 10) + snapshot := NewSnapshot(db, 10) + + snapshot.snapshotRecord(block) for i := 1; i < 10; i++ { @@ -243,6 +250,10 @@ func TestSnapshot(t *testing.T) { head.Number = big.NewInt(int64(i)) head.Time = big.NewInt(1548582552502000000 + int64(i*5000000000)) + block := &types.Block{ + Head: head, + } + hash = head.Hash() root, err = state.Commit(batch, hash, 0) @@ -259,9 +270,9 @@ func TestSnapshot(t *testing.T) { rawdb.WriteHeadBlockHash(batch, hash) rawdb.WriteCanonicalHash(batch, hash, uint64(i)) batch.Write() - } - time.Sleep(time.Duration(10) * time.Second) + snapshot.snapshotRecord(block) + } time, err := state.GetSnapshotLast() pretime, _ := state.GetSnapshotPrev(time) @@ -275,4 +286,52 @@ func TestSnapshot(t *testing.T) { if bytes.Equal(value2, []byte(strconv.Itoa(100*4))) == false { t.Error("Test snapshot failed") } + + // + err = state.StartGetAccountInfo(time) + if err != nil { + t.Error("Test snapshot get account failed") + } + + var flag bool = true + var accountInfo []types.AccountInfo + for flag { + accountInfo, flag = state.LookupAccountInfo() + } + + if bytes.Equal(accountInfo[0].Value, []byte(strconv.Itoa(100*6))) == false { + t.Error("Test snapshot get account failed") + } + + err = state.StopGetAccountInfo() + if err != nil { + t.Error("Test snapshot get account failed") + } + + err = state.StartGetAccountInfo(time) + if err != nil { + t.Error("Test snapshot get account failed") + } + + // + err = state.StartGetAccountInfo(pretime) + if err != nil { + t.Error("Test snapshot get account failed") + } + + flag = true + accountInfo = accountInfo[:0] + for flag { + accountInfo, flag = state.LookupAccountInfo() + } + + if bytes.Equal(accountInfo[0].Value, []byte(strconv.Itoa(100*4))) == false { + t.Error("Test snapshot get account failed") + } + + err = state.StopGetAccountInfo() + if err != nil { + t.Error("Test snapshot get account failed") + } + } diff --git a/test/common/dpos.go b/test/common/dpos.go index c215ebb3..f3a34b37 100644 --- a/test/common/dpos.go +++ b/test/common/dpos.go @@ -66,7 +66,8 @@ func (acc *Account) CreateAccount(to common.Name, value *big.Int, id uint64, gas } tx := types.NewTransaction(acc.feeid, big.NewInt(1e10), []*types.Action{action}...) - if err := types.SignAction(action, tx, signer, acc.priv); err != nil { + key := types.MakeKeyPair(acc.priv, []uint64{0}) + if err := types.SignActionWithMultiKey(action, tx, signer, []*types.KeyPair{key}); err != nil { panic(err) } rawtx, err := rlp.EncodeToBytes(tx) @@ -87,7 +88,8 @@ func (acc *Account) Transfer(to common.Name, value *big.Int, id uint64, gas uint } tx := types.NewTransaction(acc.feeid, big.NewInt(1e10), []*types.Action{action}...) - if err := types.SignAction(action, tx, signer, acc.priv); err != nil { + key := types.MakeKeyPair(acc.priv, []uint64{0}) + if err := types.SignActionWithMultiKey(action, tx, signer, []*types.KeyPair{key}); err != nil { panic(err) } rawtx, err := rlp.EncodeToBytes(tx) @@ -97,9 +99,9 @@ func (acc *Account) Transfer(to common.Name, value *big.Int, id uint64, gas uint return rawtx } -// RegProducer -func (acc *Account) RegProducer(to common.Name, value *big.Int, id uint64, gas uint64, url string, state *big.Int) []byte { - arg := &args.RegisterProducer{ +// RegCadidate +func (acc *Account) RegCadidate(to common.Name, value *big.Int, id uint64, gas uint64, url string, state *big.Int) []byte { + arg := &args.RegisterCadidate{ Url: url, Stake: state, } @@ -110,13 +112,14 @@ func (acc *Account) RegProducer(to common.Name, value *big.Int, id uint64, gas u if acc.getnonce != nil { acc.nonce = acc.getnonce(acc.name) } - action := types.NewAction(types.RegProducer, acc.name, to, acc.nonce, id, gas, value, payload) + action := types.NewAction(types.RegCadidate, acc.name, to, acc.nonce, id, gas, value, payload) if acc.getnonce == nil { acc.nonce++ } tx := types.NewTransaction(acc.feeid, big.NewInt(1e10), []*types.Action{action}...) - if err := types.SignAction(action, tx, signer, acc.priv); err != nil { + key := types.MakeKeyPair(acc.priv, []uint64{0}) + if err := types.SignActionWithMultiKey(action, tx, signer, []*types.KeyPair{key}); err != nil { panic(err) } rawtx, err := rlp.EncodeToBytes(tx) @@ -126,9 +129,9 @@ func (acc *Account) RegProducer(to common.Name, value *big.Int, id uint64, gas u return rawtx } -// UpdateProducer -func (acc *Account) UpdateProducer(to common.Name, value *big.Int, id uint64, gas uint64, url string, state *big.Int) []byte { - arg := &args.UpdateProducer{ +// UpdateCadidate +func (acc *Account) UpdateCadidate(to common.Name, value *big.Int, id uint64, gas uint64, url string, state *big.Int) []byte { + arg := &args.UpdateCadidate{ Url: url, Stake: state, } @@ -139,13 +142,14 @@ func (acc *Account) UpdateProducer(to common.Name, value *big.Int, id uint64, ga if acc.getnonce != nil { acc.nonce = acc.getnonce(acc.name) } - action := types.NewAction(types.UpdateProducer, acc.name, to, acc.nonce, id, gas, value, payload) + action := types.NewAction(types.UpdateCadidate, acc.name, to, acc.nonce, id, gas, value, payload) if acc.getnonce == nil { acc.nonce++ } tx := types.NewTransaction(acc.feeid, big.NewInt(1e10), []*types.Action{action}...) - if err := types.SignAction(action, tx, signer, acc.priv); err != nil { + key := types.MakeKeyPair(acc.priv, []uint64{0}) + if err := types.SignActionWithMultiKey(action, tx, signer, []*types.KeyPair{key}); err != nil { panic(err) } rawtx, err := rlp.EncodeToBytes(tx) @@ -155,18 +159,20 @@ func (acc *Account) UpdateProducer(to common.Name, value *big.Int, id uint64, ga return rawtx } -// UnRegProducer -func (acc *Account) UnRegProducer(to common.Name, value *big.Int, id uint64, gas uint64) []byte { +// UnRegCadidate +func (acc *Account) UnRegCadidate(to common.Name, value *big.Int, id uint64, gas uint64) []byte { if acc.getnonce != nil { acc.nonce = acc.getnonce(acc.name) } - action := types.NewAction(types.UnregProducer, acc.name, to, acc.nonce, id, gas, value, nil) + action := types.NewAction(types.UnregCadidate, acc.name, to, acc.nonce, id, gas, value, nil) if acc.getnonce == nil { acc.nonce++ } tx := types.NewTransaction(acc.feeid, big.NewInt(1e10), []*types.Action{action}...) - if err := types.SignAction(action, tx, signer, acc.priv); err != nil { + key := types.MakeKeyPair(acc.priv, []uint64{0}) + + if err := types.SignActionWithMultiKey(action, tx, signer, []*types.KeyPair{key}); err != nil { panic(err) } rawtx, err := rlp.EncodeToBytes(tx) @@ -176,10 +182,10 @@ func (acc *Account) UnRegProducer(to common.Name, value *big.Int, id uint64, gas return rawtx } -// VoteProducer -func (acc *Account) VoteProducer(to common.Name, value *big.Int, id uint64, gas uint64, producer string, state *big.Int) []byte { - arg := &args.VoteProducer{ - Producer: producer, +// VoteCadidate +func (acc *Account) VoteCadidate(to common.Name, value *big.Int, id uint64, gas uint64, cadidate string, state *big.Int) []byte { + arg := &args.VoteCadidate{ + Cadidate: cadidate, Stake: state, } payload, err := rlp.EncodeToBytes(arg) @@ -189,13 +195,15 @@ func (acc *Account) VoteProducer(to common.Name, value *big.Int, id uint64, gas if acc.getnonce != nil { acc.nonce = acc.getnonce(acc.name) } - action := types.NewAction(types.VoteProducer, acc.name, to, acc.nonce, id, gas, value, payload) + action := types.NewAction(types.VoteCadidate, acc.name, to, acc.nonce, id, gas, value, payload) if acc.getnonce == nil { acc.nonce++ } tx := types.NewTransaction(acc.feeid, big.NewInt(1e10), []*types.Action{action}...) - if err := types.SignAction(action, tx, signer, acc.priv); err != nil { + key := types.MakeKeyPair(acc.priv, []uint64{0}) + + if err := types.SignActionWithMultiKey(action, tx, signer, []*types.KeyPair{key}); err != nil { panic(err) } rawtx, err := rlp.EncodeToBytes(tx) @@ -205,10 +213,10 @@ func (acc *Account) VoteProducer(to common.Name, value *big.Int, id uint64, gas return rawtx } -// ChangeProducer -func (acc *Account) ChangeProducer(to common.Name, value *big.Int, id uint64, gas uint64, producer string) []byte { - arg := &args.ChangeProducer{ - Producer: producer, +// ChangeCadidate +func (acc *Account) ChangeCadidate(to common.Name, value *big.Int, id uint64, gas uint64, cadidate string) []byte { + arg := &args.ChangeCadidate{ + Cadidate: cadidate, } payload, err := rlp.EncodeToBytes(arg) if err != nil { @@ -217,33 +225,15 @@ func (acc *Account) ChangeProducer(to common.Name, value *big.Int, id uint64, ga if acc.getnonce != nil { acc.nonce = acc.getnonce(acc.name) } - action := types.NewAction(types.ChangeProducer, acc.name, to, acc.nonce, id, gas, value, payload) + action := types.NewAction(types.ChangeCadidate, acc.name, to, acc.nonce, id, gas, value, payload) if acc.getnonce == nil { acc.nonce++ } tx := types.NewTransaction(acc.feeid, big.NewInt(1e10), []*types.Action{action}...) - if err := types.SignAction(action, tx, signer, acc.priv); err != nil { - panic(err) - } - rawtx, err := rlp.EncodeToBytes(tx) - if err != nil { - panic(err) - } - return rawtx -} - -func (acc *Account) UnvoteProducer(to common.Name, value *big.Int, id uint64, gas uint64) []byte { - if acc.getnonce != nil { - acc.nonce = acc.getnonce(acc.name) - } - action := types.NewAction(types.UnvoteProducer, acc.name, to, acc.nonce, id, gas, value, nil) - if acc.getnonce == nil { - acc.nonce++ - } + key := types.MakeKeyPair(acc.priv, []uint64{0}) - tx := types.NewTransaction(acc.feeid, big.NewInt(1e10), []*types.Action{action}...) - if err := types.SignAction(action, tx, signer, acc.priv); err != nil { + if err := types.SignActionWithMultiKey(action, tx, signer, []*types.KeyPair{key}); err != nil { panic(err) } rawtx, err := rlp.EncodeToBytes(tx) @@ -253,25 +243,19 @@ func (acc *Account) UnvoteProducer(to common.Name, value *big.Int, id uint64, ga return rawtx } -// UnvoteVoter -func (acc *Account) UnvoteVoter(to common.Name, value *big.Int, id uint64, gas uint64, voter string) []byte { - arg := &args.RemoveVoter{ - Voter: voter, - } - payload, err := rlp.EncodeToBytes(arg) - if err != nil { - panic(err) - } +func (acc *Account) UnvoteCadidate(to common.Name, value *big.Int, id uint64, gas uint64) []byte { if acc.getnonce != nil { acc.nonce = acc.getnonce(acc.name) } - action := types.NewAction(types.RemoveVoter, acc.name, to, acc.nonce, id, gas, value, payload) + action := types.NewAction(types.UnvoteCadidate, acc.name, to, acc.nonce, id, gas, value, nil) if acc.getnonce == nil { acc.nonce++ } tx := types.NewTransaction(acc.feeid, big.NewInt(1e10), []*types.Action{action}...) - if err := types.SignAction(action, tx, signer, acc.priv); err != nil { + key := types.MakeKeyPair(acc.priv, []uint64{0}) + + if err := types.SignActionWithMultiKey(action, tx, signer, []*types.KeyPair{key}); err != nil { panic(err) } rawtx, err := rlp.EncodeToBytes(tx) diff --git a/test/common/utils.go b/test/common/utils.go index ccb1c992..7f45e662 100644 --- a/test/common/utils.go +++ b/test/common/utils.go @@ -31,6 +31,7 @@ import ( "github.com/fractalplatform/fractal/asset" "github.com/fractalplatform/fractal/common" "github.com/fractalplatform/fractal/rpc" + "github.com/fractalplatform/fractal/types" jww "github.com/spf13/jwalterweatherman" ) @@ -86,7 +87,7 @@ func GetNonce(accountname common.Name) (uint64, error) { // GetAccountBalanceByID get balance by address ,assetID and number. func GetAccountBalanceByID(accountName common.Name, assetID uint64) (*big.Int, error) { balance := big.NewInt(0) - err := ClientCall("account_getAccountBalanceByID", balance, accountName, assetID) + err := ClientCall("account_getAccountBalanceByID", balance, accountName, assetID, 1) return balance, err } @@ -130,6 +131,12 @@ func GetDposAccount(name common.Name) (map[string]interface{}, error) { return fields, err } +func GetBlockAndResult(blockNr rpc.BlockNumber) (*types.BlockAndResult, error) { + result := &types.BlockAndResult{} + err := ClientCall("ft_getBlockAndResultByNumber", result, blockNr) + return result, err +} + // defaultDataDir is the default data directory to use for the databases and other // persistence requirements. func defaultDataDir() string { diff --git a/test/contract/MultiAsset/MultiAsset.abi b/test/contract/MultiAsset/MultiAsset.abi index b50960a3..6f81e3de 100644 --- a/test/contract/MultiAsset/MultiAsset.abi +++ b/test/contract/MultiAsset/MultiAsset.abi @@ -1 +1 @@ -[{"constant":false,"inputs":[{"name":"desc","type":"string"}],"name":"reg","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"t","type":"uint256"},{"name":"time","type":"uint256"}],"name":"getSnapshotTime","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"assetId","type":"address"}],"name":"getBalanceEx","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"assetId","type":"uint256"},{"name":"time","type":"uint256"}],"name":"getAssetAmount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"assetId","type":"address"},{"name":"value","type":"uint256"}],"name":"transAsset","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"},{"name":"assetId","type":"address"}],"name":"changeOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"assetId","type":"uint256"},{"name":"time","type":"uint256"}],"name":"getSnapBalance","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"assetId","type":"address"},{"name":"value","type":"uint256"}],"name":"add","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"}] +[{"constant":false,"inputs":[{"name":"desc","type":"string"}],"name":"reg","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"a","type":"uint256"}],"name":"balances","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"},{"name":"assetId","type":"uint256"}],"name":"changeOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"assetId","type":"uint256"},{"name":"value","type":"uint256"}],"name":"transAsset","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"t","type":"uint256"},{"name":"time","type":"uint256"}],"name":"getSnapshotTime","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"assetId","type":"uint256"}],"name":"getBalanceEx","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"assetId","type":"uint256"},{"name":"value","type":"uint256"}],"name":"setdestroyasset","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"assetId","type":"uint256"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"add","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"user","type":"address"},{"name":"t","type":"uint256"}],"name":"getdg","outputs":[{"name":"a","type":"uint256"},{"name":"b","type":"uint256"},{"name":"c","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"assetId","type":"uint256"},{"name":"time","type":"uint256"},{"name":"typeId","type":"uint256"}],"name":"getSnapBalance","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"assetId","type":"uint256"},{"name":"time","type":"uint256"}],"name":"getAssetAmount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":true,"stateMutability":"payable","type":"constructor"}] \ No newline at end of file diff --git a/test/contract/MultiAsset/MultiAsset.bin b/test/contract/MultiAsset/MultiAsset.bin index a1dc2032..b1c22269 100644 --- a/test/contract/MultiAsset/MultiAsset.bin +++ b/test/contract/MultiAsset/MultiAsset.bin @@ -1 +1 @@ -608060405234801561001057600080fd5b506105ce806100206000396000f30060806040526004361061008e576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680634708293e146100905780637a727a41146100f9578063925dc8b314610130578063d639ec4214610193578063ef9ba08c146101ca578063f00d4b5d1461022a578063f208415e1461028d578063f5d82b6b146102e4575b005b34801561009c57600080fd5b506100f7600480360381019080803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050610331565b005b34801561010557600080fd5b5061012e6004803603810190808035906020019092919080359060200190929190505050610352565b005b34801561013c57600080fd5b50610191600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506103a0565b005b34801561019f57600080fd5b506101c86004803603810190808035906020019092919080359060200190929190505050610414565b005b610228600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610462565b005b34801561023657600080fd5b5061028b600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506104c8565b005b34801561029957600080fd5b506102e2600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019092919050505061050d565b005b3480156102f057600080fd5b5061032f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610573565b005b8060200160405181900390c015801561034e573d6000803e3d6000fd5b5050565b60008282c690507f676574536e617073686f7454696d6500000000000000000000000000000000008160010260405180826000191660001916815260200191505060405180910390a1505050565b7f67657462616c616e6365657800000000000000000000000000000000000000008273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16c360010260405180826000191660001916815260200191505060405180910390a15050565b60008282c590507f6765746173736574616d6f756e740000000000000000000000000000000000008160010260405180826000191660001916815260200191505060405180910390a1505050565b8273ffffffffffffffffffffffffffffffffffffffff166108fc8373ffffffffffffffffffffffffffffffffffffffff168391821502919060405160006040518083038186868a8ac49450505050501580156104c2573d6000803e3d6000fd5b50505050565b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16c2158015610508573d6000803e3d6000fd5b505050565b60008373ffffffffffffffffffffffffffffffffffffffff168383c790507f676574536e617042616c616e63650000000000000000000000000000000000008160010260405180826000191660001916815260200191505060405180910390a150505050565b8173ffffffffffffffffffffffffffffffffffffffff1681c115801561059d573d6000803e3d6000fd5b5050505600a165627a7a723058203eac0d54e785a391b33757afdb79bfc0adc40010d764de55bb544bf6139cac3e0029 +608060405261075f806100136000396000f3006080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680634708293e146100b45780634903b0d11461011057806356df3db11461015157806368669bc81461019e5780637a727a41146101e85780637d3d999b1461021f5780639b0fd0291461026c578063b9955e39146102b7578063bbd6ad6f1461030e578063cf4908761461037d578063d639ec42146103de575b600080fd5b61010e600480360381019080803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050610415565b005b34801561011c57600080fd5b5061013b60048036038101908080359060200190929190505050610425565b6040518082815260200191505060405180910390f35b34801561015d57600080fd5b5061019c600480360381019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061042f565b005b6101e6600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291908035906020019092919050505061044d565b005b3480156101f457600080fd5b5061021d600480360381019080803590602001909291908035906020019092919050505061049d565b005b34801561022b57600080fd5b5061026a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506104eb565b005b34801561027857600080fd5b506102a16004803603810190808035906020019092919080359060200190929190505050610549565b6040518082815260200191505060405180910390f35b3480156102c357600080fd5b5061030c60048036038101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610556565b005b34801561031a57600080fd5b50610359600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610576565b60405180848152602001838152602001828152602001935050505060405180910390f35b34801561038957600080fd5b506103dc600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190803590602001909291908035906020019092919050505061067d565b005b3480156103ea57600080fd5b5061041360048036038101908080359060200190929190803590602001909291905050506106e5565b005b8060200160405181900390c05050565b6000819050919050565b808273ffffffffffffffffffffffffffffffffffffffff16c2505050565b8273ffffffffffffffffffffffffffffffffffffffff166108fc838391821502919060405160006040518083038186868a8ac4945050505050158015610497573d6000803e3d6000fd5b50505050565b60008282c690507f676574536e617073686f7454696d6500000000000000000000000000000000008160010260405180826000191660001916815260200191505060405180910390a1505050565b7f67657462616c616e6365657800000000000000000000000000000000000000008273ffffffffffffffffffffffffffffffffffffffff1682c360010260405180826000191660001916815260200191505060405180910390a15050565b60008282c8905092915050565b828273ffffffffffffffffffffffffffffffffffffffff1682c150505050565b6000806000806000808773ffffffffffffffffffffffffffffffffffffffff1687ca8093508194508295505050507f64656c65676174654e756d0000000000000000000000000000000000000000008360010260405180826000191660001916815260200191505060405180910390a17f566f74654e756d000000000000000000000000000000000000000000000000008260010260405180826000191660001916815260200191505060405180910390a17f426c6f636b4e756d0000000000000000000000000000000000000000000000008160010260405180826000191660001916815260200191505060405180910390a18282829550955095505050509250925092565b60008473ffffffffffffffffffffffffffffffffffffffff16848484c790507f676574536e617042616c616e63650000000000000000000000000000000000008160010260405180826000191660001916815260200191505060405180910390a15050505050565b60008282c590507f6765746173736574616d6f756e740000000000000000000000000000000000008160010260405180826000191660001916815260200191505060405180910390a15050505600a165627a7a72305820c8cac49e526b2d6b1960c43cf1cb84561311d0a5aead78dcbc2fd25c2ecdcdb90029 \ No newline at end of file diff --git a/test/contract/MultiAsset/MultiAsset.sol b/test/contract/MultiAsset/MultiAsset.sol old mode 100644 new mode 100755 index 8a19ebfa..a8626efa --- a/test/contract/MultiAsset/MultiAsset.sol +++ b/test/contract/MultiAsset/MultiAsset.sol @@ -1,22 +1,23 @@ pragma solidity ^0.4.24; contract MultiAsset { - function() public payable { + constructor() public payable { } - function reg(string desc) public { + function reg(string desc) public payable{ issueasset(desc); } - function add(address assetId, uint256 value) public { - addasset(assetId,value); + function add(uint256 assetId, address to, uint256 value ) public { + addasset(assetId,to,value); } - function transAsset(address to, address assetId, uint256 value) public payable { + function transAsset(address to, uint256 assetId, uint256 value) public payable { to.transferex(assetId, value); } - function changeOwner(address newOwner, address assetId) public { + function changeOwner(address newOwner, uint256 assetId) public { setassetowner(assetId, newOwner); } - function getBalanceEx(address to,address assetId) public { + function getBalanceEx(address to,uint256 assetId) public { log1(bytes32(to.balanceex(assetId)),"getbalanceex"); + // return to.balanceex(assetId); } function getAssetAmount(uint256 assetId, uint256 time) public{ @@ -30,10 +31,30 @@ contract MultiAsset { x = snapshottime(t,time); log1(bytes32(x),"getSnapshotTime" ); - } - function getSnapBalance(address to,uint256 assetId,uint256 time) public { + } + + function getSnapBalance(address to,uint256 assetId, uint256 time, uint256 typeId) public { uint256 x ; - x = to.snapbalance(assetId,time); + x = to.snapbalance(assetId,time,typeId); log1(bytes32(x),"getSnapBalance"); } + + function balances(uint256 a) public pure returns (uint256 balance) { + balance = a; + } + + function getdg(address user, uint256 t) external returns (uint256 a, uint256 b, uint256 c) { + uint256 aa; + uint256 bb; + uint256 cc; + (aa,bb,cc) = getdelegate(user,t); + log1(bytes32(aa), "delegateNum"); + log1(bytes32(bb), "VoteNum"); + log1(bytes32(cc), "BlockNum"); + return (aa,bb,cc); + } + + function setdestroyasset(uint256 assetId, uint256 value) public returns (uint256) { + return destroyasset(assetId, value); + } } diff --git a/test/contract/MultiAsset/transaction_contract.go b/test/contract/MultiAsset/transaction_contract.go index f1630b65..bd8749d7 100644 --- a/test/contract/MultiAsset/transaction_contract.go +++ b/test/contract/MultiAsset/transaction_contract.go @@ -18,65 +18,46 @@ package main import ( "bytes" + "crypto/ecdsa" + "encoding/binary" "fmt" "io/ioutil" "math/big" "strings" "time" + jww "github.com/spf13/jwalterweatherman" + "github.com/fractalplatform/fractal/common" "github.com/fractalplatform/fractal/crypto" - testcommon "github.com/fractalplatform/fractal/test/common" + tc "github.com/fractalplatform/fractal/test/common" "github.com/fractalplatform/fractal/types" "github.com/fractalplatform/fractal/utils/abi" "github.com/fractalplatform/fractal/utils/rlp" - jww "github.com/spf13/jwalterweatherman" ) var ( - abifile = "MultiAsset.abi" - binfile = "MultiAsset.bin" - privateKey, _ = crypto.HexToECDSA("289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032") - from = common.Name("ftsystemio") - to = common.Name("toaddrname") - newFrom = common.Name("newfromname") - - contractAddr = common.Name("multiasset") - assetID = uint64(1) - - nonce = uint64(0) - - gasLimit = uint64(2000000) -) - -func generateAccount() { - nonce, _ = testcommon.GetNonce(from) - - newPrivateKey, _ := crypto.GenerateKey() - pubKey := common.BytesToPubKey(crypto.FromECDSAPub(&newPrivateKey.PublicKey)) + abifile = "./MultiAsset.abi" + binfile = "./MultiAsset.bin" + systemprikey, _ = crypto.HexToECDSA("289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032") + systemname = common.Name("ftsystemio") - balance, _ := testcommon.GetAccountBalanceByID(from, assetID) - balance.Div(balance, big.NewInt(10)) + contractAddr = common.Name("testtest11") + assetID = uint64(1) + gasLimit = uint64(2000000) - newFrom = common.Name(fmt.Sprintf("newfromname%d", nonce)) - contractAddr = common.Name(fmt.Sprintf("multiasset%d", nonce)) + pub1 = "0x0468cba7890aae10f3dde57d269cf7c4ba14cc0efc2afee86791b0a22b794820febdb2e5c6c56878a308e7f62ad2d75739de40313a72975c993dd76a5301a03d12" + pri1 = "357a2cbdd91686dcbe2c612e9bed85d4415f62446440839466bf7b2f1ab135b7" - sendTransferTx(types.CreateAccount, from, newFrom, nonce, assetID, balance, pubKey.Bytes()) - sendTransferTx(types.CreateAccount, from, to, nonce+1, assetID, big.NewInt(0), pubKey.Bytes()) + pub2 = "0x04fa0b2a9b2d0542bf2912c4c6500ba64a26652e302370ed5645b1c32df50fbe7a5f12da0b278638e1df6753a7c6ac09e68cb748cfe6d45102114f52e95e9ed652" + pri2 = "340cde826336f1adb8673ec945819d073af00cffb5c174542e35ff346445e213" - for { - time.Sleep(10 * time.Second) - fromexist, _ := testcommon.CheckAccountIsExist(newFrom) - toexist, _ := testcommon.CheckAccountIsExist(to) - if fromexist && toexist { - break - } - } + pubkey1 = common.HexToPubKey(pub1) + prikey1, _ = crypto.HexToECDSA(pri1) - from = newFrom - fmt.Println("from ", from) - privateKey = newPrivateKey -} + pubkey2 = common.HexToPubKey(pub2) + prikey2, _ = crypto.HexToECDSA(pri2) +) func input(abifile string, method string, params ...interface{}) (string, error) { var abicode string @@ -128,8 +109,24 @@ func formIssueAssetInput(abifile string, desc string) ([]byte, error) { } return common.Hex2Bytes(issueAssetInput), nil } +func formIssueAssetInput1(abifile string, assetId *big.Int, to common.Address, value *big.Int) ([]byte, error) { + issueAssetInput, err := input(abifile, "add", assetId, to, value) + if err != nil { + jww.INFO.Println("createInput error ", err) + return nil, err + } + return common.Hex2Bytes(issueAssetInput), nil +} +func formSetAssetOwner(abifile string, newOwner common.Address, assetId *big.Int) ([]byte, error) { + issueAssetInput, err := input(abifile, "changeOwner", newOwner, assetId) + if err != nil { + jww.INFO.Println("createInput error ", err) + return nil, err + } + return common.Hex2Bytes(issueAssetInput), nil +} -func formTransferAssetInput(abifile string, toAddr common.Address, assetId common.Address, value *big.Int) ([]byte, error) { +func formTransferAssetInput(abifile string, toAddr common.Address, assetId *big.Int, value *big.Int) ([]byte, error) { transferAssetInput, err := input(abifile, "transAsset", toAddr, assetId, value) if err != nil { jww.INFO.Println("transferAssetInput error ", err) @@ -138,29 +135,19 @@ func formTransferAssetInput(abifile string, toAddr common.Address, assetId commo return common.Hex2Bytes(transferAssetInput), nil } +func formGetDelgateInput(abifile string, user common.Address, time *big.Int) ([]byte, error) { + issueAssetInput, err := input(abifile, "getdg", user, time) + if err != nil { + jww.INFO.Println("createInput error ", err) + return nil, err + } + return common.Hex2Bytes(issueAssetInput), nil +} + func init() { jww.SetLogThreshold(jww.LevelTrace) jww.SetStdoutThreshold(jww.LevelInfo) - generateAccount() -} - -func main() { - jww.INFO.Println("test send sundry transaction...") - sendDeployContractTransaction() - sendIssueTransaction() - for { - time.Sleep(10 * time.Second) - assetName := "eth" + from.String() - asset, _ := testcommon.GetAssetInfoByName(assetName) - if asset.AssetId != 0 { - assetID = asset.AssetId - fmt.Println("assetID ", assetID) - break - } - } - sendFulfillContractTransaction() - sendTransferTransaction() } func sendDeployContractTransaction() { @@ -170,49 +157,87 @@ func sendDeployContractTransaction() { jww.INFO.Println("sendDeployContractTransaction formCreateContractInput error ... ", err) return } - nonce, _ = testcommon.GetNonce(from) - sendTransferTx(types.CreateContract, from, contractAddr, nonce, assetID, big.NewInt(0), input) + nonce, _ := tc.GetNonce(systemname) + sendTx(types.CreateContract, systemname, contractAddr, nonce, assetID, big.NewInt(100000000000), input, systemprikey) } func sendIssueTransaction() { jww.INFO.Println("test sendIssueTransaction... ") - issueStr := "eth" + from.String() + ",ethereum,10000000000,10," + from.String() + issueStr := "ft" + contractAddr.String() + ",ft,10000000000,10," + contractAddr.String() + ",9000000000000000," + contractAddr.String() + //issueStr := "eth4" + contractAddr.String() + ",ft,10000000000,10," + contractAddr.String() + ",9000000000000000," + contractAddr.String() //25560 input, err := formIssueAssetInput(abifile, issueStr) if err != nil { jww.INFO.Println("sendIssueTransaction formIssueAssetInput error ... ", err) return } - nonce++ - sendTransferTx(types.Transfer, from, contractAddr, nonce, assetID, big.NewInt(0), input) + nonce, _ := tc.GetNonce(contractAddr) + sendTx(types.CallContract, contractAddr, contractAddr, nonce, assetID, big.NewInt(0), input, prikey1) } -func sendFulfillContractTransaction() { - jww.INFO.Println("test sendFulfillContractTransaction... ") - nonce++ - sendTransferTx(types.Transfer, from, contractAddr, nonce, assetID, big.NewInt(1000000000), nil) +func sendIncreaseIssueTransaction() { + jww.INFO.Println("test sendIssueTransaction... ") + input, err := formIssueAssetInput1(abifile, big.NewInt(3), common.BytesToAddress([]byte("testtest12")), big.NewInt(100000)) //21976 21848 + + if err != nil { + jww.INFO.Println("sendIssueTransaction formIssueAssetInput error ... ", err) + return + } + nonce, _ := tc.GetNonce(contractAddr) + sendTx(types.CallContract, contractAddr, contractAddr, nonce, assetID, big.NewInt(0), input, prikey1) +} + +func sendSetOwnerIssueTransaction() { + jww.INFO.Println("test sendIssueTransaction... ") + input, err := formSetAssetOwner(abifile, common.BytesToAddress([]byte("testtest12")), big.NewInt(3)) //22168 + + if err != nil { + jww.INFO.Println("sendIssueTransaction formIssueAssetInput error ... ", err) + return + } + + nonce, _ := tc.GetNonce(contractAddr) + sendTx(types.CallContract, contractAddr, contractAddr, nonce, assetID, big.NewInt(0), input, prikey1) +} + +func sendTransferToContractTransaction() { + nonce, _ := tc.GetNonce(systemname) + sendTx(types.Transfer, systemname, contractAddr, nonce, 1, big.NewInt(100), nil, systemprikey) } func sendTransferTransaction() { jww.INFO.Println("test sendTransferTransaction... ") - input, err := formTransferAssetInput(abifile, common.BytesToAddress([]byte(to.String())), common.BigToAddress(big.NewInt(int64(assetID))), big.NewInt(1)) + input, err := formTransferAssetInput(abifile, common.BytesToAddress([]byte("testtest12")), big.NewInt(1), big.NewInt(10)) if err != nil { jww.INFO.Println("sendDeployContractTransaction formCreateContractInput error ... ", err) return } + nonce, _ := tc.GetNonce(systemname) + sendTx(types.CallContract, systemname, contractAddr, nonce, assetID, big.NewInt(0), input, systemprikey) +} + +func sendGetDelgateContractTransaction() { + jww.INFO.Println("test sendGetDelgateContractTransaction... ") - for { - nonce++ - sendTransferTx(types.Transfer, from, contractAddr, nonce, assetID, big.NewInt(0), input) + b := make([]byte, 8) + binary.BigEndian.PutUint64(b, 4099) + + input, err := formGetDelgateInput(abifile, common.BytesToAddress(b), big.NewInt(0)) + if err != nil { + jww.INFO.Println("sendGetDelgateContractTransaction formGetDelgateInput error ... ", err) + return } + nonce, _ := tc.GetNonce(systemname) + sendTx(types.CallContract, systemname, contractAddr, nonce, assetID, big.NewInt(0), input, systemprikey) } -func sendTransferTx(txType types.ActionType, from, to common.Name, nonce, assetID uint64, value *big.Int, input []byte) { +func sendTx(txType types.ActionType, from, to common.Name, nonce, assetID uint64, value *big.Int, input []byte, prikey *ecdsa.PrivateKey) { action := types.NewAction(txType, from, to, nonce, assetID, gasLimit, value, input) - gasprice, _ := testcommon.GasPrice() + gasprice, _ := tc.GasPrice() tx := types.NewTransaction(1, gasprice, action) signer := types.MakeSigner(big.NewInt(1)) - err := types.SignAction(action, tx, signer, privateKey) + keypair := types.MakeKeyPair(prikey, []uint64{0}) + err := types.SignActionWithMultiKey(action, tx, signer, []*types.KeyPair{keypair}) if err != nil { jww.ERROR.Fatalln(err) } @@ -221,7 +246,25 @@ func sendTransferTx(txType types.ActionType, from, to common.Name, nonce, assetI if err != nil { jww.ERROR.Fatalln(err) } - - hash, _ := testcommon.SendRawTx(rawtx) + hash, _ := tc.SendRawTx(rawtx) jww.INFO.Println("result hash: ", hash.Hex()) } + +func main() { + sendDeployContractTransaction() + time.Sleep(time.Duration(3) * time.Second) + sendIssueTransaction() + time.Sleep(time.Duration(3) * time.Second) + asset, _ := tc.GetAssetInfoByName("ft" + contractAddr.String()) + fmt.Println(asset) + sendIncreaseIssueTransaction() + time.Sleep(time.Duration(3) * time.Second) + sendTransferToContractTransaction() + time.Sleep(time.Duration(3) * time.Second) + sendTransferTransaction() + time.Sleep(time.Duration(3) * time.Second) + sendSetOwnerIssueTransaction() + time.Sleep(time.Duration(3) * time.Second) + sendGetDelgateContractTransaction() + time.Sleep(time.Duration(3) * time.Second) +} diff --git a/test/contract/VEN/transaction_contract.go b/test/contract/VEN/transaction_contract.go index 543420c7..786d6dc9 100644 --- a/test/contract/VEN/transaction_contract.go +++ b/test/contract/VEN/transaction_contract.go @@ -190,7 +190,8 @@ func sendTransferTx(txType types.ActionType, from, to common.Name, nonce, assetI tx := types.NewTransaction(assetID, gasprice, action) signer := types.MakeSigner(big.NewInt(1)) - err := types.SignAction(action, tx, signer, privateKey) + keypair := types.MakeKeyPair(privateKey, []uint64{0}) + err := types.SignActionWithMultiKey(action, tx, signer, []*types.KeyPair{keypair}) if err != nil { jww.ERROR.Fatalln(err) } diff --git a/test/dpos/dpostx.go b/test/dpos/dpostx.go deleted file mode 100644 index 919eec2f..00000000 --- a/test/dpos/dpostx.go +++ /dev/null @@ -1,295 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package main - -import ( - //"bytes" - //"io/ioutil" - "math/big" - //"strings" - "fmt" - "time" - - "github.com/fractalplatform/fractal/common" - "github.com/fractalplatform/fractal/consensus/dpos" - "github.com/fractalplatform/fractal/crypto" - testcommon "github.com/fractalplatform/fractal/test/common" - "github.com/fractalplatform/fractal/types" - "github.com/fractalplatform/fractal/utils/rlp" - jww "github.com/spf13/jwalterweatherman" -) - -var ( - minproducerstake = big.NewInt(0).Mul(dpos.DefaultConfig.UnitStake, dpos.DefaultConfig.ProducerMinQuantity) - minvoterstake = big.NewInt(0).Mul(dpos.DefaultConfig.UnitStake, dpos.DefaultConfig.VoterMinQuantity) - big1 = big.NewInt(1) - - privateKey, _ = crypto.HexToECDSA("289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032") - from = common.Name("ftsystemio") - p1 = common.Name("producer1") - p2 = common.Name("producer2") - newFrom = common.Name("newfromname") - noNeedUser = common.Name("") - noNeedNum = big.NewInt(0) - - assetID = uint64(1) - - nonce = uint64(0) - - gasLimit = uint64(2000000) -) - -func generateAccount() { - nonce, _ = testcommon.GetNonce(from) - - newPrivateKey, _ := crypto.GenerateKey() - pubKey := common.BytesToPubKey(crypto.FromECDSAPub(&newPrivateKey.PublicKey)) - - balance, _ := testcommon.GetAccountBalanceByID(from, assetID) - balance.Div(balance, big.NewInt(10)) - - newFrom = common.Name(fmt.Sprintf("newfromname%d", nonce)) - //contractAddr = common.Name(fmt.Sprintf("multiasset%d", nonce)) - - sendTransferTx(types.CreateAccount, from, newFrom, nonce, assetID, balance, pubKey.Bytes()) - nonce++ - sendTransferTx(types.CreateAccount, from, p1, nonce, assetID, balance, pubKey.Bytes()) - nonce++ - sendTransferTx(types.CreateAccount, from, p2, nonce, assetID, balance, pubKey.Bytes()) - - for { - time.Sleep(3 * time.Second) - bnf, _ := testcommon.CheckAccountIsExist(newFrom) - bp1, _ := testcommon.CheckAccountIsExist(p1) - bp2, _ := testcommon.CheckAccountIsExist(p2) - if bnf && bp1 && bp2 { - jww.INFO.Println("account created... ") - break - } - } - - from = newFrom - privateKey = newPrivateKey -} - -func init() { - jww.SetLogThreshold(jww.LevelTrace) - jww.SetStdoutThreshold(jww.LevelInfo) - - generateAccount() -} - -func main() { - jww.INFO.Println("Test dpos votes transactions...") - - commonUnregProducer() - - sendRegProducer() - sendUpdateProducer() - sendVoteProducer() - - for { - // check result - time.Sleep(3 * time.Second) - - p1_fields, err := testcommon.GetDposAccount(p1) - p2_fields, _ := testcommon.GetDposAccount(p2) - if true { - jww.INFO.Println("p1:", p1_fields) - jww.INFO.Println("p2:", p2_fields) - jww.INFO.Println("err", err) - break - } - } - sendChangeProducer() - // - sendUnvoteProducer() - // - for { - // check result - time.Sleep(3 * time.Second) - - p1_fields, _ := testcommon.GetDposAccount(p1) - p2_fields, _ := testcommon.GetDposAccount(p2) - if true { - jww.INFO.Println("p1:", p1_fields) - jww.INFO.Println("p2:", p2_fields) - break - } - } - sendVoteProducer() - sendRemoveVoter() - // - for { - // check result - time.Sleep(3 * time.Second) - - p1_fields, _ := testcommon.GetDposAccount(p1) - p2_fields, _ := testcommon.GetDposAccount(p2) - if true { - jww.INFO.Println("p1:", p1_fields) - jww.INFO.Println("p2:", p2_fields) - break - } - } - sendUnregProducer() -} - -func sendRegProducer() { - jww.INFO.Println("test sendRegProducer... ") - - rp := dpos.RegisterProducer{ - Url: "https://www.test_url.xxx/", - Stake: minproducerstake, - } - - rawdata, err := rlp.EncodeToBytes(rp) - if err != nil { - jww.ERROR.Fatalln(err) - } - - nonce++ - sendTransferTx(types.RegProducer, p1, noNeedUser, nonce, assetID, noNeedNum, rawdata) - nonce++ - sendTransferTx(types.RegProducer, p2, noNeedUser, nonce, assetID, noNeedNum, rawdata) - jww.INFO.Println("test sendRegProducer... done ") -} - -func sendUnregProducer() { - jww.INFO.Println("test sendUnregProducer... ") - - nonce++ - sendTransferTx(types.UnregProducer, p1, noNeedUser, nonce, assetID, noNeedNum, nil) - nonce++ - sendTransferTx(types.UnregProducer, p2, noNeedUser, nonce, assetID, noNeedNum, nil) - jww.INFO.Println("test sendUnregProducer... done ") -} - -func sendUpdateProducer() { - jww.INFO.Println("test sendUpdateProducer... ") - - rp := dpos.UpdateProducer{ - Url: "https://www.test_url_p1.xxx/", - Stake: minproducerstake, - } - - rawdata, err := rlp.EncodeToBytes(rp) - if err != nil { - jww.ERROR.Fatalln(err) - } - nonce++ - sendTransferTx(types.UpdateProducer, p1, noNeedUser, nonce, assetID, noNeedNum, rawdata) - jww.INFO.Println("test sendUpdateProducer... done ") -} - -func sendRemoveVoter() { - jww.INFO.Println("test sendRemoveVoter... ") - - rp := dpos.RemoveVoter{ - Voter: from.String(), - } - - rawdata, err := rlp.EncodeToBytes(rp) - if err != nil { - jww.ERROR.Fatalln(err) - } - - nonce++ - sendTransferTx(types.RemoveVoter, p1, noNeedUser, nonce, assetID, noNeedNum, rawdata) - jww.INFO.Println("test sendRemoveVoter... done ") -} - -func sendVoteProducer() { - jww.INFO.Println("test sendVoteProducer... ") - - rp := dpos.VoteProducer{ - Producer: p1.String(), - Stake: minvoterstake, - } - - rawdata, err := rlp.EncodeToBytes(rp) - if err != nil { - jww.ERROR.Fatalln(err) - } - nonce++ - sendTransferTx(types.VoteProducer, from, noNeedUser, nonce, assetID, noNeedNum, rawdata) - jww.INFO.Println("test sendVoteProducer... done ") -} - -func sendChangeProducer() { - jww.INFO.Println("test sendChangeProducer... ") - - rp := dpos.ChangeProducer{ - Producer: p2.String(), - } - - rawdata, err := rlp.EncodeToBytes(rp) - if err != nil { - jww.ERROR.Fatalln(err) - } - nonce++ - sendTransferTx(types.ChangeProducer, from, noNeedUser, nonce, assetID, noNeedNum, rawdata) - jww.INFO.Println("test sendChangeProducer... done") -} - -func sendUnvoteProducer() { - jww.INFO.Println("test sendUnvoteProducer... ") - - nonce++ - sendTransferTx(types.UnvoteProducer, from, noNeedUser, nonce, assetID, noNeedNum, nil) - jww.INFO.Println("test sendUnvoteProducer... done ") -} - -// sendTransferTx -func sendTransferTx(txType types.ActionType, from, to common.Name, nonce, assetID uint64, value *big.Int, input []byte) { - action := types.NewAction(txType, from, to, nonce, assetID, gasLimit, value, input) - gp, _ := testcommon.GasPrice() - tx := types.NewTransaction(assetID, gp, action) - - signer := types.MakeSigner(big.NewInt(1)) - err := types.SignAction(action, tx, signer, privateKey) - if err != nil { - jww.ERROR.Fatalln(err) - } - - rawtx, err := rlp.EncodeToBytes(tx) - if err != nil { - jww.ERROR.Fatalln(err) - } - - if hash, err := testcommon.SendRawTx(rawtx); err != nil { - jww.ERROR.Fatalln("send failed: ", err) - } else { - jww.INFO.Println("result hash: ", hash.Hex()) - } -} - -func commonUnregProducer() { - jww.INFO.Println("test commonUnregProducer... ") - - acct := testcommon.NewAccount(p1, privateKey, 1, 0, nil) - - rawtx := acct.UnRegProducer(p1, big1, 1, gasLimit) - - if hash, err := testcommon.SendRawTx(rawtx); err != nil { - jww.ERROR.Fatalln("send failed: h", err, hash.Hex()) - } else { - jww.INFO.Println("result hash: ", hash.Hex()) - } - - jww.INFO.Println("test commonUnregProducer... done ") -} diff --git a/test/dposstart/main.go b/test/dposstart/main.go new file mode 100644 index 00000000..a6dc8fb5 --- /dev/null +++ b/test/dposstart/main.go @@ -0,0 +1,129 @@ +package main + +import ( + "flag" + "fmt" + "math" + "math/big" + "strings" + + "github.com/fractalplatform/fractal/accountmanager" + "github.com/fractalplatform/fractal/common" + args "github.com/fractalplatform/fractal/consensus/dpos" + "github.com/fractalplatform/fractal/crypto" + "github.com/fractalplatform/fractal/sdk" +) + +var ( + gasLimit = uint64(210000) + gasPrice = big.NewInt(10000000000) +) + +func main() { + rpcHost := flag.String("u", "http://localhost:8545", "RPC host地址") + issueHex := flag.String("s", "ftsystemio:289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032", "系统账户私钥") + systoken := flag.String("systoken", "ftoken", "系统代币名字") + value := flag.Int64("n", 10000000, "生成者代币质押数量(最小单位),1000万") + chainID := flag.Int64("chainid", 1, "chain id") + flag.Parse() + + cadidates := flag.Args() + if len(cadidates) == 0 { + cadidates = append(cadidates, "ftcadidate1:289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032") + cadidates = append(cadidates, "ftcadidate2:9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658") + cadidates = append(cadidates, "ftcadidate3:8605cf6e76c9fc8ac079d0f841bd5e99bd3ad40fdd56af067993ed14fc5bfca8") + } + + fmt.Println("RPC:", rpcHost) + fmt.Println("系统账户:", *issueHex) + fmt.Println("系统代币:", *systoken) + fmt.Println("质押数量:", *value) + fmt.Println("生成者数量:", len(cadidates)) + fmt.Println("生成者列表:", cadidates) + api := sdk.NewAPI(*rpcHost) + + sysasset, err := api.AssetInfoByName(*systoken) + if err != nil { + panic(fmt.Sprintf("get asset info %v err %v", *systoken, err)) + } + systokenid := sysasset.AssetId + systokendecimals := big.NewInt(1) + for i := uint64(0); i < sysasset.Decimals; i++ { + systokendecimals = new(big.Int).Mul(systokendecimals, big.NewInt(10)) + } + + // issuer + splits := strings.Split(*issueHex, ":") + if len(splits) != 2 { + println("系统账户出错啦~~~", *issueHex) + return + } + if !common.IsValidAccountName(splits[0]) { + println("系统账户非法啦~~~", splits[0]) + return + } + issuerName := common.StrToName(splits[0]) + issuerPriv, err := crypto.HexToECDSA(splits[1]) + if err != nil { + println("系统账户私钥出错啦~~~", issueHex) + return + } + + if len(cadidates) < 3 { + println("生产者个数不能小于3~~~", len(cadidates)) + return + } + issuerAcct := sdk.NewAccount(api, issuerName, issuerPriv, systokenid, math.MaxUint64, true, big.NewInt(*chainID)) + + prods := map[common.Name]*sdk.Account{} + for _, privHex := range cadidates { + splits := strings.Split(privHex, ":") + if len(splits) != 2 { + println("生产者账户出错啦~~~", privHex) + return + } + if !common.IsValidAccountName(splits[0]) { + println("生产者账户非法啦~~~", splits[0]) + return + } + name := common.StrToName(splits[0]) + priv, err := crypto.HexToECDSA(splits[1]) + if err != nil { + println("生产者账户私钥出错啦~~~", privHex) + return + } + prods[name] = sdk.NewAccount(api, name, priv, systokenid, math.MaxUint64, true, big.NewInt(*chainID)) + } + + delegateValue := new(big.Int).Mul(big.NewInt(*value), systokendecimals) + issueValue := new(big.Int).Mul(big.NewInt(*value+10), systokendecimals) + + fmt.Println("转账金额/人:", issueValue) + fmt.Println("质押金额/人:", delegateValue) + + for to, acct := range prods { + existed, _ := api.AccountIsExist(to.String()) + if !existed { + hash, err := issuerAcct.CreateAccount(issuerName, issueValue, systokenid, gasLimit, &accountmanager.AccountAction{ + AccountName: to, + PublicKey: acct.Pubkey(), + }) + fmt.Println(hash.String(), ":", issuerName, "create & transfer", issueValue, fmt.Sprintf("(%v)", systokenid), "to", to, "error", err) + } else { + hash, err := issuerAcct.Transfer(to, issueValue, systokenid, gasLimit) + fmt.Println(hash.String(), ":", issuerName, "transfer", issueValue, fmt.Sprintf("(%v)", systokenid), "to", to, "error", err) + } + } + + // reg cadidates + for name, acct := range prods { + value := big.NewInt(1e5) + hash, err := acct.RegCadidate(name, big.NewInt(0), systokenid, gasLimit, &args.RegisterCadidate{ + Url: "www." + name.String() + ".io", + Stake: delegateValue, + }) + fmt.Println(hash.String(), ":", issuerName, "reg", delegateValue, "& transfer", value, "(", systokenid, ")", "to ", name, "error", err) + } + + fmt.Println("Dpos启动完成.") +} diff --git a/test/multisig/transaction_contract.go b/test/multisig/transaction_contract.go new file mode 100644 index 00000000..492f86ae --- /dev/null +++ b/test/multisig/transaction_contract.go @@ -0,0 +1,276 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package main + +import ( + "crypto/ecdsa" + "encoding/hex" + "fmt" + "math/big" + "time" + + "github.com/fractalplatform/fractal/accountmanager" + "github.com/fractalplatform/fractal/common" + "github.com/fractalplatform/fractal/crypto" + testcommon "github.com/fractalplatform/fractal/test/common" + "github.com/fractalplatform/fractal/types" + "github.com/fractalplatform/fractal/utils/rlp" + jww "github.com/spf13/jwalterweatherman" +) + +var ( + privateKey, _ = crypto.HexToECDSA("289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032") + from = common.Name("ftsystemio") + aca = common.Name("accounta") + acb = common.Name("accountb") + acc = common.Name("accountc") + + a_author_0_priv *ecdsa.PrivateKey + a_author_2_priv *ecdsa.PrivateKey + a_author_3_priv *ecdsa.PrivateKey + b_author_0_priv *ecdsa.PrivateKey + b_author_2_priv *ecdsa.PrivateKey + c_author_0_priv *ecdsa.PrivateKey + c_author_1_priv *ecdsa.PrivateKey + c_author_2_priv *ecdsa.PrivateKey + + newPrivateKey_a *ecdsa.PrivateKey + newPrivateKey_b *ecdsa.PrivateKey + newPrivateKey_c *ecdsa.PrivateKey + pubKey_a common.PubKey + pubKey_b common.PubKey + pubKey_c common.PubKey + + aNonce = uint64(0) + bNonce = uint64(0) + cNonce = uint64(0) + + assetID = uint64(1) + nonce = uint64(0) + gasLimit = uint64(2000000) +) + +func generateAccount() { + nonce, _ = testcommon.GetNonce(from) + + newPrivateKey_a, _ = crypto.GenerateKey() + pubKey_a = common.BytesToPubKey(crypto.FromECDSAPub(&newPrivateKey_a.PublicKey)) + a_author_0_priv = newPrivateKey_a + fmt.Println("priv_a ", hex.EncodeToString(crypto.FromECDSA(newPrivateKey_a)), " pubKey_a ", pubKey_a.String()) + + newPrivateKey_b, _ = crypto.GenerateKey() + pubKey_b = common.BytesToPubKey(crypto.FromECDSAPub(&newPrivateKey_b.PublicKey)) + b_author_0_priv = newPrivateKey_b + fmt.Println("priv_b ", hex.EncodeToString(crypto.FromECDSA(newPrivateKey_b)), " pubKey_b ", pubKey_b.String()) + + newPrivateKey_c, _ = crypto.GenerateKey() + pubKey_c = common.BytesToPubKey(crypto.FromECDSAPub(&newPrivateKey_c.PublicKey)) + c_author_0_priv = newPrivateKey_c + fmt.Println("priv_c ", hex.EncodeToString(crypto.FromECDSA(newPrivateKey_c)), " pubKey_c ", pubKey_c.String()) + + balance, _ := testcommon.GetAccountBalanceByID(from, assetID) + balance.Div(balance, big.NewInt(10)) + + aca = common.Name(fmt.Sprintf("accounta%d", nonce)) + acb = common.Name(fmt.Sprintf("accountb%d", nonce)) + acc = common.Name(fmt.Sprintf("accountc%d", nonce)) + + key := types.MakeKeyPair(privateKey, []uint64{0}) + acct := &accountmanager.AccountAction{ + AccountName: aca, + Founder: aca, + PublicKey: pubKey_a, + } + b, _ := rlp.EncodeToBytes(acct) + sendTransferTx(types.CreateAccount, from, from, nonce, assetID, balance, b, []*types.KeyPair{key}) + + acct = &accountmanager.AccountAction{ + AccountName: acb, + Founder: acb, + PublicKey: pubKey_b, + } + b, _ = rlp.EncodeToBytes(acct) + sendTransferTx(types.CreateAccount, from, from, nonce+1, assetID, balance, b, []*types.KeyPair{key}) + + acct = &accountmanager.AccountAction{ + AccountName: acc, + Founder: acc, + PublicKey: pubKey_c, + } + b, _ = rlp.EncodeToBytes(acct) + sendTransferTx(types.CreateAccount, from, from, nonce+2, assetID, balance, b, []*types.KeyPair{key}) + + for { + time.Sleep(10 * time.Second) + aexist, _ := testcommon.CheckAccountIsExist(aca) + bexist, _ := testcommon.CheckAccountIsExist(acb) + cexist, _ := testcommon.CheckAccountIsExist(acc) + if aexist && bexist && cexist { + break + } + } + + fmt.Println("aca ", aca, " acb ", acb, " acc ", acc) +} + +func init() { + jww.SetLogThreshold(jww.LevelTrace) + jww.SetStdoutThreshold(jww.LevelInfo) + + generateAccount() +} + +func addAuthorsForAca() { + a_author_0 := common.NewAuthor(pubKey_a, 500) + a_authorAct_0 := &accountmanager.AuthorAction{1, a_author_0} + + a_author_1 := common.NewAuthor(acb, 400) + a_authorAct_1 := &accountmanager.AuthorAction{0, a_author_1} + + a_author_2_priv, _ = crypto.GenerateKey() + a_author_2_addr := crypto.PubkeyToAddress(a_author_2_priv.PublicKey) + a_author_2 := common.NewAuthor(a_author_2_addr, 400) + a_authorAct_2 := &accountmanager.AuthorAction{0, a_author_2} + + a_author_3_priv, _ = crypto.GenerateKey() + a_author_3_pub := common.BytesToPubKey(crypto.FromECDSAPub(&a_author_3_priv.PublicKey)) + a_author_3 := common.NewAuthor(a_author_3_pub, 400) + a_authorAct_3 := &accountmanager.AuthorAction{0, a_author_3} + + action := &accountmanager.AccountAuthorAction{1000, 0, []*accountmanager.AuthorAction{a_authorAct_0, a_authorAct_1, a_authorAct_2, a_authorAct_3}} + input, err := rlp.EncodeToBytes(action) + if err != nil { + jww.INFO.Println("addAuthors for accounta error ... ", err) + return + } + key := types.MakeKeyPair(newPrivateKey_a, []uint64{0}) + sendTransferTx(types.UpdateAccountAuthor, aca, aca, aNonce, assetID, big.NewInt(0), input, []*types.KeyPair{key}) +} + +func addAuthorsForAcb() { + b_author_0 := common.NewAuthor(pubKey_b, 50) + b_authorAct_0 := &accountmanager.AuthorAction{1, b_author_0} + + b_author_1 := common.NewAuthor(acc, 40) + b_authorAct_1 := &accountmanager.AuthorAction{0, b_author_1} + + b_author_2_priv, _ = crypto.GenerateKey() + b_author_2_addr := crypto.PubkeyToAddress(b_author_2_priv.PublicKey) + b_author_2 := common.NewAuthor(b_author_2_addr, 40) + b_authorAct_2 := &accountmanager.AuthorAction{0, b_author_2} + + action := &accountmanager.AccountAuthorAction{100, 0, []*accountmanager.AuthorAction{b_authorAct_0, b_authorAct_1, b_authorAct_2}} + input, err := rlp.EncodeToBytes(action) + if err != nil { + jww.INFO.Println("addAuthors for accountb error ... ", err) + return + } + key := types.MakeKeyPair(newPrivateKey_b, []uint64{0}) + sendTransferTx(types.UpdateAccountAuthor, acb, acb, bNonce, assetID, big.NewInt(0), input, []*types.KeyPair{key}) +} + +func addAuthorsForAcc() { + c_author_0 := common.NewAuthor(pubKey_c, 5) + c_authorAct_0 := &accountmanager.AuthorAction{1, c_author_0} + + c_author_1_priv, _ = crypto.GenerateKey() + c_author_1_addr := crypto.PubkeyToAddress(c_author_1_priv.PublicKey) + c_author_1 := common.NewAuthor(c_author_1_addr, 4) + c_authorAct_1 := &accountmanager.AuthorAction{0, c_author_1} + + c_author_2_priv, _ = crypto.GenerateKey() + c_author_2_pub := common.BytesToPubKey(crypto.FromECDSAPub(&c_author_2_priv.PublicKey)) + c_author_2 := common.NewAuthor(c_author_2_pub, 4) + c_authorAct_2 := &accountmanager.AuthorAction{0, c_author_2} + + action := &accountmanager.AccountAuthorAction{10, 0, []*accountmanager.AuthorAction{c_authorAct_0, c_authorAct_1, c_authorAct_2}} + input, err := rlp.EncodeToBytes(action) + if err != nil { + jww.INFO.Println("addAuthors for accountc error ... ", err) + return + } + key := types.MakeKeyPair(newPrivateKey_c, []uint64{0}) + sendTransferTx(types.UpdateAccountAuthor, acc, acc, cNonce, assetID, big.NewInt(0), input, []*types.KeyPair{key}) +} + +func transferFromA2B() { + key_1_0 := types.MakeKeyPair(b_author_0_priv, []uint64{1, 0}) + key_1_1_0 := types.MakeKeyPair(c_author_0_priv, []uint64{1, 1, 0}) + key_1_1_1 := types.MakeKeyPair(c_author_1_priv, []uint64{1, 1, 1}) + key_1_1_2 := types.MakeKeyPair(c_author_2_priv, []uint64{1, 1, 2}) + key_2 := types.MakeKeyPair(a_author_2_priv, []uint64{2}) + key_3 := types.MakeKeyPair(a_author_3_priv, []uint64{3}) + key_1_2 := types.MakeKeyPair(b_author_2_priv, []uint64{1, 2}) + + aNonce++ + sendTransferTx(types.Transfer, aca, acb, aNonce, assetID, big.NewInt(1), nil, []*types.KeyPair{key_1_0, key_1_1_0, key_1_1_1, key_1_1_2, key_2, key_3, key_1_2}) +} + +func modifyAUpdateAUthorThreshold() { + key_2 := types.MakeKeyPair(a_author_2_priv, []uint64{2}) + action := &accountmanager.AccountAuthorAction{0, 2, []*accountmanager.AuthorAction{}} + input, err := rlp.EncodeToBytes(action) + if err != nil { + jww.INFO.Println("addAuthors for accountc error ... ", err) + return + } + + aNonce++ + sendTransferTx(types.UpdateAccountAuthor, aca, aca, aNonce, assetID, big.NewInt(0), input, []*types.KeyPair{key_2}) +} + +func main() { + jww.INFO.Println("test send sundry transaction...") + + // d_author_1_priv, _ := crypto.GenerateKey() + // d_author_1_pub := common.BytesToPubKey(crypto.FromECDSAPub(&d_author_1_priv.PublicKey)) + // d_author_1_pub_addr := common.BytesToAddress(crypto.Keccak256(d_author_1_pub.Bytes()[1:])[12:]) + // d_author_1_addr := crypto.PubkeyToAddress(d_author_1_priv.PublicKey) + // fmt.Println(d_author_1_pub_addr.String(), d_author_1_addr.String()) + + addAuthorsForAca() + addAuthorsForAcb() + addAuthorsForAcc() + time.Sleep(10 * time.Second) + + transferFromA2B() + modifyAUpdateAUthorThreshold() +} + +func sendTransferTx(txType types.ActionType, from, to common.Name, nonce, assetID uint64, value *big.Int, input []byte, keys []*types.KeyPair) { + action := types.NewAction(txType, from, to, nonce, assetID, gasLimit, value, input) + gasprice, _ := testcommon.GasPrice() + tx := types.NewTransaction(1, gasprice, action) + + signer := types.MakeSigner(big.NewInt(1)) + err := types.SignActionWithMultiKey(action, tx, signer, keys) + if err != nil { + jww.ERROR.Fatalln(err) + } + + rawtx, err := rlp.EncodeToBytes(tx) + if err != nil { + jww.ERROR.Fatalln(err) + } + + hash, err := testcommon.SendRawTx(rawtx) + if err != nil { + jww.INFO.Println("result err: ", err) + + } + jww.INFO.Println("result hash: ", hash.Hex()) +} diff --git a/test/systemtestcase/account_test.go b/test/systemtestcase/account_test.go deleted file mode 100644 index 40f27af9..00000000 --- a/test/systemtestcase/account_test.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package main - -import ( - "crypto/ecdsa" - "testing" - - . "github.com/fractalplatform/fractal/test/systemtestcase/rpc" - . "github.com/smartystreets/goconvey/convey" -) - -func createNewAccount(fromAccount string, fromPriKey *ecdsa.PrivateKey, newAccountName string) error { - result, _, _ := CreateNewAccountWithName(fromAccount, fromPriKey, newAccountName) - return result -} - -func TestCreateNormalAccount_111(t *testing.T) { - Convey("用系统账户创建新账户", t, func() { - accountName, err := GenerateValidAccountName("111", 8) - So(err, ShouldBeNil) - So(createNewAccount(SystemAccount, SystemAccountPriKey, accountName), ShouldBeNil) - }) -} - -func TestCreateDuplicateAccount_112(t *testing.T) { - Convey("用系统账户重复创建账户失败", t, func() { - accountName, err := GenerateValidAccountName("112", 8) - So(err, ShouldBeNil) - So(createNewAccount(SystemAccount, SystemAccountPriKey, accountName), ShouldBeNil) - So(createNewAccount(SystemAccount, SystemAccountPriKey, accountName), ShouldNotBeNil) - }) -} - -func TestCreateErrAccount_113(t *testing.T) { - Convey("用系统账户创建新账户失败(账户名不符合规范)", t, func() { - So(createNewAccount(SystemAccount, SystemAccountPriKey, "&!@123456"), ShouldNotBeNil) - }) -} - -func TestGetAccountInfo_131(t *testing.T) { - Convey("获取系统账户信息", t, func() { - So(GetAccountInfo(SystemAccount), ShouldBeTrue) - }) - - Convey("获取已创建的普通账户信息", t, func() { - accountName, err := GenerateValidAccountName("131", 8) - So(err, ShouldBeNil) - So(createNewAccount(SystemAccount, SystemAccountPriKey, accountName), ShouldBeNil) - So(GetAccountInfo(accountName), ShouldBeTrue) - }) -} - -func TestGetAccountInfo_132(t *testing.T) { - Convey("获取不存在的账户信息失败", t, func() { - accountName := GenerateAccountName("132", 8) - So(GetAccountInfo(accountName), ShouldBeFalse) - }) -} - -func TestAccountExist_133(t *testing.T) { - Convey("确认已创建的账户存在", t, func() { - accountName, err := GenerateValidAccountName("133", 8) - So(err, ShouldBeNil) - - So(createNewAccount(SystemAccount, SystemAccountPriKey, accountName), ShouldBeNil) - - bExist, err := AccountIsExist(accountName) - So(err, ShouldBeNil) - So(bExist, ShouldBeTrue) - }) -} - -func TestAccountExist_134(t *testing.T) { - Convey("确认未创建的账户不存在", t, func() { - accountName := GenerateAccountName("134", 8) - bExist, err := AccountIsExist(accountName) - So(err, ShouldBeNil) - So(bExist, ShouldBeFalse) - }) -} - -//func TestDeleteAccount_121(t *testing.T) { -// Convey("删除已存在账号", t, func() { -// accountName := GenerateAccountName("121", 8) -// So(deleteAccount(SystemAccount, SystemAccountPriKey, accountName), ShouldBeTrue) -// }) -//} -// -//func TestDeleteAccount_122(t *testing.T) { -// Convey("删除不存在账号", t, func() { -// accountName := GenerateAccountName("122", 8) -// _, prikey := GeneratePubKey() -// So(deleteNotExistAcount(accountName, prikey), ShouldBeFalse) -// }) -//} - -//func deleteAccount(fromname string, fromprikey *ecdsa.PrivateKey, newname string) bool { -// result, _, prikey := CreateNewAccountWithName(fromname, fromprikey, newname) -// if !result { -// return false -// } -// return DeleteAcountWithResult(common.Name(newname), prikey) -//} -// -//func deleteNotExistAcount(fromname string, fromprikey *ecdsa.PrivateKey) bool { -// return DeleteAcountWithResult(common.Name(fromname), fromprikey) -//} diff --git a/test/systemtestcase/asset_test.go b/test/systemtestcase/asset_test.go deleted file mode 100644 index 25bdd4f6..00000000 --- a/test/systemtestcase/asset_test.go +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package main - -import ( - "math/big" - "testing" - - . "github.com/fractalplatform/fractal/test/systemtestcase/rpc" - . "github.com/smartystreets/goconvey/convey" -) - -func TestIssueAsset_211_1(t *testing.T) { - amount := new(big.Int).SetUint64(1000000000) - decimals := uint64(18) - - Convey("系统账户创建资产,无收款方", t, func() { - assetName := GenerateAssetName("211", 8) - symbol := assetName - fromAccount := SystemAccount - ownerAccount := SystemAccount - fromPriKey := SystemAccountPriKey - So(IssueAsset(fromAccount, ownerAccount, fromPriKey, "", assetName, symbol, amount, decimals), ShouldBeNil) - }) -} - -func TestIssueAsset_211_2(t *testing.T) { - - Convey("普通账户创建资产,无收款方", t, func() { - newAccount, err := GenerateValidAccountName("211", 8) - So(err, ShouldBeNil) - - assetName := GenerateAssetName("211", 8) - symbol := assetName - amount := new(big.Int).SetUint64(1000000000) - decimals := uint64(18) - _, err = IssueAssetWithValidAccount(newAccount, newAccount, "", assetName, symbol, amount, decimals) - So(err, ShouldBeNil) - }) -} - -func TestIssueAsset_212(t *testing.T) { - Convey("系统账户创建资产,带收款方", t, func() { - assetName := GenerateAssetName("211", 8) - symbol := assetName - amount := new(big.Int).SetUint64(1000000000) - decimals := uint64(18) - So(IssueAsset(SystemAccount, SystemAccount, SystemAccountPriKey, "toaccount", assetName, symbol, amount, decimals), ShouldNotBeNil) - }) -} - -func TestIssueAsset_213(t *testing.T) { - Convey("系统账户重复创建资产,无收款方", t, func() { - assetName := GenerateAssetName("213", 8) - symbol := assetName - amount := new(big.Int).SetUint64(1000000000) - decimals := uint64(18) - So(IssueAsset(SystemAccount, SystemAccount, SystemAccountPriKey, "", assetName, symbol, amount, decimals), ShouldBeNil) - So(IssueAsset(SystemAccount, SystemAccount, SystemAccountPriKey, "", assetName, symbol, amount, decimals), ShouldNotBeNil) - }) -} - -func TestIncreaseAsset_221(t *testing.T) { - Convey("普通账户创建资产后,增发一定数量的资产", t, func() { - newAccount, err := GenerateValidAccountName("221", 8) - So(err, ShouldBeNil) - - assetName := GenerateAssetName("221", 8) - symbol := assetName - amount := new(big.Int).SetUint64(1000000000) - decimals := uint64(18) - newPriKey, err := IssueAssetWithValidAccount(newAccount, newAccount, "", assetName, symbol, amount, decimals) - So(err, ShouldBeNil) - So(IncreaseAsset(newAccount, newPriKey, assetName, amount), ShouldBeNil) - }) -} - -func TestIncreaseAsset_222(t *testing.T) { - -} - -func TestModifyAssetOwner_231(t *testing.T) { - Convey("普通账户创建资产后,将资产Owner设置为新的账户", t, func() { - accountName, err := GenerateValidAccountName("231", 8) - So(err, ShouldBeNil) - - assetName := GenerateAssetName("231", 8) - symbol := assetName - amount := new(big.Int).SetUint64(1000000000) - decimals := uint64(18) - newPriKey, err := IssueAssetWithValidAccount(accountName, accountName, "", assetName, symbol, amount, decimals) - So(err, ShouldBeNil) - - newAccountName, err := GenerateValidAccountName("231", 8) - So(err, ShouldBeNil) - So(createNewAccount(SystemAccount, SystemAccountPriKey, newAccountName), ShouldBeNil) - - So(SetAssetNewOwner(accountName, assetName, newAccountName, newPriKey), ShouldBeNil) - }) -} - -func TestModifyAssetOwner_232(t *testing.T) { - Convey("普通账户创建资产后,将资产Owner设置为新的账户", t, func() { - accountName, err := GenerateValidAccountName("231", 8) - So(err, ShouldBeNil) - - assetName := GenerateAssetName("231", 8) - symbol := assetName - amount := new(big.Int).SetUint64(1000000000) - decimals := uint64(18) - newPriKey, err := IssueAssetWithValidAccount(accountName, accountName, "", assetName, symbol, amount, decimals) - So(err, ShouldBeNil) - - newAccountName := GenerateAccountName("231", 8) - - So(SetAssetNewOwner(accountName, assetName, newAccountName, newPriKey), ShouldNotBeNil) - }) -} - -func TestTransferAsset_241(t *testing.T) { - Convey("系统账户向普通账户转其已有的资产,", t, func() { - newAccountName, err := GenerateValidAccountName("241", 8) - So(err, ShouldBeNil) - So(createNewAccount(SystemAccount, SystemAccountPriKey, newAccountName), ShouldBeNil) - - So(TransferAsset(SystemAccount, newAccountName, 1, 10000000, SystemAccountPriKey), ShouldBeNil) - So(TransferAsset(SystemAccount, newAccountName, 1, 10000000, SystemAccountPriKey), ShouldBeNil) - }) -} - -func TestTransferAsset_242(t *testing.T) { - Convey("系统账户向普通账户转其未曾有过的的资产,", t, func() { - newAccountName, err := GenerateValidAccountName("241", 8) - So(err, ShouldBeNil) - So(createNewAccount(SystemAccount, SystemAccountPriKey, newAccountName), ShouldBeNil) - - So(TransferAsset(SystemAccount, newAccountName, 1, 10000000, SystemAccountPriKey), ShouldBeNil) - }) -} - -func TestTransferAsset_243(t *testing.T) { - Convey("系统账户向不存在的账户转资产,", t, func() { - newAccountName, err := GenerateValidAccountName("242", 8) - So(err, ShouldBeNil) - - So(TransferAsset(SystemAccount, newAccountName, 1, 10000000, SystemAccountPriKey), ShouldNotBeNil) - }) -} - -func TestTransferAsset_244(t *testing.T) { - Convey("系统账户向普通账户转未曾创建过的的资产,", t, func() { - newAccountName, err := GenerateValidAccountName("241", 8) - So(err, ShouldBeNil) - So(createNewAccount(SystemAccount, SystemAccountPriKey, newAccountName), ShouldBeNil) - - notExistAssetId := GetNextAssetIdFrom(1) - So(TransferAsset(SystemAccount, newAccountName, notExistAssetId, 10000000, SystemAccountPriKey), ShouldNotBeNil) - }) -} - -func TestTransferAsset_245(t *testing.T) { - Convey("系统账户将其未拥有过的资产转给其它账号", t, func() { - newAccountName, err := GenerateValidAccountName("241", 8) - So(err, ShouldBeNil) - So(createNewAccount(SystemAccount, SystemAccountPriKey, newAccountName), ShouldBeNil) - notOwnedAssetId := GetNextAssetIdFrom(1) - 1 - - for notOwnedAssetId > 0 { - balance, _ := GetAssetBalanceByID(SystemAccount, notOwnedAssetId) - if balance.Cmp(big.NewInt(0)) == 0 { - break - } - notOwnedAssetId-- - } - - So(TransferAsset(SystemAccount, newAccountName, notOwnedAssetId, 1, SystemAccountPriKey), ShouldNotBeNil) - }) -} - -func TestTransferAsset_246(t *testing.T) { - Convey("系统账户向普通账户转账金额超过其余额", t, func() { - newAccountName, err := GenerateValidAccountName("241", 8) - So(err, ShouldBeNil) - So(createNewAccount(SystemAccount, SystemAccountPriKey, newAccountName), ShouldBeNil) - - balance, _ := GetAssetBalanceByID(SystemAccount, 1) - So(TransferAsset(SystemAccount, newAccountName, 1, balance.Int64()+1, SystemAccountPriKey), ShouldNotBeNil) - }) -} - -func TestTransferAsset_247(t *testing.T) { - Convey("给自己转账", t, func() { - - }) -} - -func TestTransferAsset_25_1to5(t *testing.T) { - - Convey("查询资产相关信息", t, func() { - - amount := new(big.Int).SetUint64(1000000000) - decimals := uint64(18) - assetName := GenerateAssetName("211", 8) - symbol := assetName - assetId := uint64(0) - oldAccount := GetAccountByName(SystemAccount) - So(len(oldAccount.Balances) > 0, ShouldBeTrue) - - So(IssueAsset(SystemAccount, SystemAccount, SystemAccountPriKey, "", assetName, symbol, amount, decimals), ShouldBeNil) - Convey("1:通过资产名称查询资产id", func() { - assetInfo, err := GetAssetInfoByName(assetName) - So(err, ShouldBeNil) - assetId = assetInfo.AssetId - So(assetId > 0, ShouldBeTrue) - - Convey("2:通过资产ID查询资产对象", func() { - assetInfo, err := GetAssetInfoById(assetId) - So(err, ShouldBeNil) - So(assetInfo.AssetId == assetId, ShouldBeTrue) - So(assetInfo.AssetName == assetName, ShouldBeTrue) - }) - Convey("3:查询某账户拥有的资产数量", func() { - newAccount := GetAccountByName(SystemAccount) - So(len(newAccount.Balances)-len(oldAccount.Balances) == 1, ShouldBeTrue) - }) - Convey("4:查询所有资产数量", func() { - maxAssetId := GetNextAssetIdFrom(1) - 1 - So(assetId == maxAssetId, ShouldBeTrue) - }) - Convey("5:通过资产名称查询资产对象", func() { - assetInfo, err := GetAssetInfoByName(assetName) - So(err, ShouldBeNil) - So(assetInfo.AssetId == assetId, ShouldBeTrue) - So(assetInfo.AssetName == assetName, ShouldBeTrue) - }) - }) - }) -} diff --git a/test/systemtestcase/dpos_test.go b/test/systemtestcase/dpos_test.go deleted file mode 100644 index a735128b..00000000 --- a/test/systemtestcase/dpos_test.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package main - -import ( - "testing" - - . "github.com/smartystreets/goconvey/convey" -) - -func TestRegProducer(t *testing.T) { - Convey("注册生成者", t, func() { - Convey("生产者", func() { - Convey("已是投票者", func() { - - }) - Convey("已是生产者", func() { - - }) - }) - Convey("URL长度", func() { - Convey("不满足", func() { - - }) - Convey("满足", func() { - - }) - }) - Convey("最低数量要求", func() { - Convey("不满足", func() { - - }) - Convey("满足", func() { - - }) - }) - }) -} - -func TestUpdateProducer(t *testing.T) { - Convey("更新生成者", t, func() { - Convey("生产者", func() { - Convey("已是投票者", func() { - - }) - Convey("不是生产者", func() { - - }) - }) - Convey("URL长度", func() { - Convey("不满足", func() { - - }) - Convey("满足", func() { - - }) - }) - Convey("最低数量要求", func() { - Convey("不满足", func() { - - }) - Convey("满足", func() { - - }) - }) - }) -} - -func TestUnRegProducer(t *testing.T) { - Convey("注销生成者", t, func() { - Convey("生产者", func() { - Convey("已是投票者", func() { - - }) - Convey("不是生产者", func() { - - }) - }) - Convey("URL长度", func() { - Convey("不满足", func() { - - }) - Convey("满足", func() { - - }) - }) - Convey("最低数量要求", func() { - Convey("不满足", func() { - - }) - Convey("满足", func() { - - }) - }) - }) -} diff --git a/test/systemtestcase/rpc/accountAPI.go b/test/systemtestcase/rpc/accountAPI.go deleted file mode 100644 index 8017d958..00000000 --- a/test/systemtestcase/rpc/accountAPI.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package rpc - -import ( - "crypto/ecdsa" - "errors" - "strconv" - - "github.com/fractalplatform/fractal/accountmanager" - "github.com/fractalplatform/fractal/common" - "github.com/fractalplatform/fractal/types" -) - -func createAccount(fromAccount string, fromPriKey *ecdsa.PrivateKey, newAccountName string) (common.Hash, common.PubKey, *ecdsa.PrivateKey, error) { - accountName := common.Name(fromAccount) - nonce, err := GetNonce(accountName) - if err != nil { - return common.Hash{}, common.PubKey{}, nil, err - } - createdAccountName := common.Name(newAccountName) - pubkey, priKey := GeneratePubKey() - gc := NewGeAction(types.CreateAccount, accountName, createdAccountName, nonce, 1, Gaslimit, nil, pubkey[:], fromPriKey) - var gcs []*GenAction - gcs = append(gcs, gc) - txHash, err := SendTxTest(gcs) - return txHash, pubkey, priKey, err -} - -func AccountIsExist(accountName string) (bool, error) { - isExist := new(bool) - err := ClientCall("account_accountIsExist", isExist, accountName) - if err != nil { - return false, err - } - return *isExist, nil -} - -func GetAccountInfo(accountName string) bool { - account := &accountmanager.Account{} - ClientCall("account_getAccountByName", account, accountName) - return len(account.AcctName.String()) > 0 -} - -func GetAccountByName(accountName string) accountmanager.Account { - account := &accountmanager.Account{} - ClientCall("account_getAccountByName", account, accountName) - return *account -} - -func CreateNewAccountWithName(fromAccount string, fromPriKey *ecdsa.PrivateKey, newAccountName string) (error, common.PubKey, *ecdsa.PrivateKey) { - txHash, pubKey, priKey, err := createAccount(fromAccount, fromPriKey, newAccountName) - if err != nil { - return errors.New("创建账户交易失败:" + err.Error()), pubKey, priKey - } - maxTime := uint(60) - receipt, outOfTime, err := DelayGetReceiptByTxHash(txHash, maxTime) - if err != nil { - return errors.New("获取交易receipt失败:" + err.Error()), pubKey, priKey - } - if outOfTime { - return errors.New("无法在" + strconv.Itoa(int(maxTime)) + "秒内获取交易receipt"), pubKey, priKey - } - if len(receipt.ActionResults) == 0 || receipt.ActionResults[0].Status == 0 { - return errors.New("创建账户失败"), pubKey, priKey - } - - bExist, err := AccountIsExist(newAccountName) - if err != nil { - return errors.New("无法查询到创建的账户:" + err.Error()), pubKey, priKey - } - if !bExist { - return errors.New("无法查询到创建的账户"), pubKey, priKey - } - - return nil, pubKey, priKey - -} - -// -//func deleteAcount(fromName common.Name, fromprikey *ecdsa.PrivateKey) (common.Hash, error) { -// nonce, err := GetNonce(fromName) -// if err != nil { -// return common.Hash{}, err -// } -// gc := NewGeAction(types.DeleteAccount, fromName, "", nonce, 1, Gaslimit, nil, nil, fromprikey) -// var gcs []*GenAction -// gcs = append(gcs, gc) -// hash, err := SendTxTest(gcs) -// if err != nil { -// return common.Hash{}, err -// } -// return hash, nil -//} - -//func DeleteAcountWithResult(fromName common.Name, fromprikey *ecdsa.PrivateKey) bool { -// txHash, err := deleteAcount(fromName, fromprikey) -// if err != nil { -// return false -// } -// receipt, outOfTime := DelayGetReceiptByTxHash(txHash, 60) -// -// if outOfTime || len(receipt.ActionResults) == 0 || receipt.ActionResults[0].Status == 0 { -// return false -// } -// return AccountIsExist(string(fromName)) -//} - -func GenerateAccountName(namePrefix string, addStrLen int) string { - return GenerateRandomName(namePrefix, addStrLen) -} - -func GenerateValidAccountName(namePrefix string, suffixStrLen int) (string, error) { - maxTime := 10 - for maxTime > 0 { - newAccountName := GenerateAccountName(namePrefix, suffixStrLen) - bExist, err := AccountIsExist(newAccountName) - if err != nil { - return "", errors.New("判断账号是否存在的RPC接口调用失败:" + err.Error()) - } - if !bExist { - return newAccountName, nil - } - maxTime-- - } - return "", errors.New("难以获得有效的账户名") -} - -func GenerateValidAccountNameAndKey(namePrefix string, suffixStrLen int) (string, common.PubKey, *ecdsa.PrivateKey, error) { - pubKey, priKey := GeneratePubKey() - accountName, err := GenerateValidAccountName(namePrefix, suffixStrLen) - return accountName, pubKey, priKey, err -} diff --git a/test/systemtestcase/rpc/assetAPI.go b/test/systemtestcase/rpc/assetAPI.go deleted file mode 100644 index 00912909..00000000 --- a/test/systemtestcase/rpc/assetAPI.go +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package rpc - -import ( - "crypto/ecdsa" - "errors" - "github.com/fractalplatform/fractal/utils/rlp" - "math/big" - "strconv" - - "github.com/fractalplatform/fractal/asset" - "github.com/fractalplatform/fractal/common" - "github.com/fractalplatform/fractal/types" -) - -func transfer(from, to string, assetId uint64, amount *big.Int, prikey *ecdsa.PrivateKey) (common.Hash, error) { - nonce, err := GetNonce(common.Name(from)) - if err != nil { - return common.Hash{}, err - } - gc := NewGeAction(types.Transfer, common.Name(from), common.Name(to), nonce, assetId, Gaslimit, amount, nil, prikey) - var gcs []*GenAction - gcs = append(gcs, gc) - return SendTxTest(gcs) -} - -// GetAccountBalanceByID get balance by address ,assetID and number. -func GetAssetBalanceByID(accountName string, assetID uint64) (*big.Int, error) { - balance := big.NewInt(0) - err := ClientCall("account_getAccountBalanceByID", balance, common.Name(accountName), assetID) - return balance, err -} - -//GetAssetInfoByName get assetINfo by accountName -func GetAssetInfoByName(assetName string) (*asset.AssetObject, error) { - assetInfo := &asset.AssetObject{} - err := ClientCall("account_getAssetInfoByName", assetInfo, assetName) - return assetInfo, err -} - -func GetAssetInfoById(assetID uint64) (*asset.AssetObject, error) { - assetInfo := &asset.AssetObject{} - err := ClientCall("account_getAssetInfoByID", assetInfo, assetID) - return assetInfo, err -} - -func IsAssetExist(assetName string) (bool, error) { - assetObj, err := GetAssetInfoByName(assetName) - return assetObj.AssetName == assetName, err -} - -func TransferAsset(fromAccountName, toAccountName string, assetId uint64, amount int64, prikey *ecdsa.PrivateKey) error { - oldAssetAmount, _ := GetAssetBalanceByID(toAccountName, assetId) - - hash, err := transfer(fromAccountName, toAccountName, assetId, big.NewInt(amount), prikey) - if err != nil { - return errors.New("转账交易失败(未进txpool):" + err.Error()) - } - maxTime := uint(60) - receipt, outOfTime, err := DelayGetReceiptByTxHash(hash, maxTime) - if err != nil { - return errors.New("获取交易receipt失败:" + err.Error()) - } - if outOfTime { - return errors.New("无法在" + strconv.Itoa(int(maxTime)) + "秒内获取交易receipt") - } - if len(receipt.ActionResults) == 0 || receipt.ActionResults[0].Status == 0 { - return errors.New("转账失败") - } - //str, _ := json.Marshal(receipt) - //fmt.Println(string(str)) - - newAssetAmount, err := GetAssetBalanceByID(toAccountName, assetId) - if err != nil { - return errors.New("转账后无法获取目标账户的资产余额:" + err.Error()) - } - - if newAssetAmount.Sub(newAssetAmount, oldAssetAmount).Cmp(big.NewInt(amount)) != 0 { - return errors.New("转账后资产余额差不等于转账金额") - } - return nil -} - -func IssueAssetWithValidAccount(fromAccount string, owner string, toAccount string, assetName string, symbol string, amount *big.Int, decimals uint64) (*ecdsa.PrivateKey, error) { - err, _, priKey := CreateNewAccountWithName(SystemAccount, SystemAccountPriKey, fromAccount) - if err != nil { - return nil, errors.New("创建新账号失败:" + err.Error()) - } - err = TransferAsset(SystemAccount, fromAccount, 1, 1000000000000000, SystemAccountPriKey) - if err != nil { - return nil, errors.New("创建新账号成功,但用系统账户给新账号转账出错:" + err.Error()) - } - - err = IssueAsset(fromAccount, owner, priKey, toAccount, assetName, symbol, amount, decimals) - if err != nil { - return nil, errors.New("有足够余额的新账号无法创建资产:" + err.Error()) - } - return priKey, nil -} - -func IssueAsset(fromAccount string, owner string, fromPrikey *ecdsa.PrivateKey, toAccount string, assetName string, symbol string, amount *big.Int, decimals uint64) error { - nonce, err := GetNonce(common.Name(fromAccount)) - if err != nil { - return errors.New("获取nonce失败:" + err.Error()) - } - - txHash, err := issueAsset(common.Name(fromAccount), common.Name(owner), amount, assetName, symbol, nonce, decimals, fromPrikey) - if err != nil { - return errors.New("发布资产的交易失败:" + err.Error()) - } - - maxTime := uint(60) - receipt, outOfTime, err := DelayGetReceiptByTxHash(txHash, maxTime) - if err != nil { - return errors.New("获取交易receipt失败:" + err.Error()) - } - if outOfTime { - return errors.New("无法在" + strconv.Itoa(int(maxTime)) + "秒内获取交易receipt") - } - if len(receipt.ActionResults) == 0 || receipt.ActionResults[0].Status == 0 { - return errors.New("发布资产失败") - } - - bExist, err := IsAssetExist(assetName) - if err != nil { - return errors.New("判断资产是否存在的RPC接口调用失败:" + err.Error()) - } - if bExist { - return nil - } else { - return errors.New("无法查到新发行的资产") - } -} - -func IncreaseAsset(fromAccount string, fromPrikey *ecdsa.PrivateKey, assetName string, increasedAmount *big.Int) error { - assetObj, err := GetAssetInfoByName(assetName) - if err != nil { - return errors.New("通过资产名获取资产信息失败(增发前):" + err.Error()) - } - - nonce, err := GetNonce(common.Name(fromAccount)) - if err != nil { - return errors.New("获取账户nonce失败:" + err.Error()) - } - - txhash, err := increaseAsset(common.Name(fromAccount), "", assetObj.AssetId, increasedAmount, nonce, fromPrikey) - if err != nil { - return errors.New("发送增发资产的交易失败:" + err.Error()) - } - - maxTime := uint(60) - receipt, outOfTime, err := DelayGetReceiptByTxHash(txhash, maxTime) - if err != nil { - return errors.New("获取交易receipt失败:" + err.Error()) - } - if outOfTime { - return errors.New("无法在" + strconv.Itoa(int(maxTime)) + "秒内获取交易receipt") - } - if len(receipt.ActionResults) == 0 || receipt.ActionResults[0].Status == 0 { - return errors.New("增发资产的交易执行失败") - } - - newAssetObj, err := GetAssetInfoByName(assetName) - if err != nil { - return errors.New("通过资产名获取资产信息失败(增发成功后):" + err.Error()) - } - if new(big.Int).Sub(newAssetObj.Amount, assetObj.Amount).Cmp(increasedAmount) != 0 { - return errors.New("增发的资产数额不对") - } - return nil -} - -// just send a tx -func issueAsset(from, owner common.Name, amount *big.Int, assetName string, symbol string, nonce uint64, decimals uint64, prikey *ecdsa.PrivateKey) (common.Hash, error) { - asset := &asset.AssetObject{ - AssetName: assetName, - Symbol: symbol, - Amount: amount, - Decimals: decimals, - Owner: owner, - } - payload, err := rlp.EncodeToBytes(asset) - if err != nil { - return common.Hash{}, err - } - gc := NewGeAction(types.IssueAsset, from, "", nonce, 1, Gaslimit, nil, payload, prikey) - var gcs []*GenAction - gcs = append(gcs, gc) - return SendTxTest(gcs) -} - -// just send a tx -func increaseAsset(from common.Name, to common.Name, assetId uint64, increasedAmount *big.Int, nonce uint64, prikey *ecdsa.PrivateKey) (common.Hash, error) { - ast := &asset.AssetObject{ - AssetId: assetId, - AssetName: "", - Symbol: "", - Amount: increasedAmount, - } - payload, err := rlp.EncodeToBytes(ast) - if err != nil { - return common.Hash{}, err - } - gc := NewGeAction(types.IncreaseAsset, from, to, nonce, assetId, Gaslimit, nil, payload, prikey) - var gcs []*GenAction - gcs = append(gcs, gc) - return SendTxTest(gcs) -} - -func SetAssetNewOwner(fromAccount string, assetName string, newOwner string, priKey *ecdsa.PrivateKey) error { - assetObj, err := GetAssetInfoByName(assetName) - if err != nil { - return errors.New("无法通过资产名获取资产信息:" + err.Error()) - } - txHash, err := setAssetOwner(common.Name(fromAccount), common.Name(newOwner), assetObj.AssetId, priKey) - if err != nil { - return errors.New("发送修改资产Owner的交易失败:" + err.Error()) - } - - maxTime := uint(60) - receipt, outOfTime, err := DelayGetReceiptByTxHash(txHash, maxTime) - if err != nil { - return errors.New("获取交易receipt失败:" + err.Error()) - } - if outOfTime { - return errors.New("无法在" + strconv.Itoa(int(maxTime)) + "秒内获取交易receipt") - } - if len(receipt.ActionResults) == 0 || receipt.ActionResults[0].Status == 0 { - return errors.New("发送修改资产Owner的交易执行失败") - } - - newAssetObj, err := GetAssetInfoByName(assetName) - if err != nil { - return errors.New("通过资产名获取资产信息失败(修改Owner成功后):" + err.Error()) - } - if newAssetObj.Owner.String() != newOwner { - return errors.New("资产Owner跟预期设置的Owner不一致") - } - return nil -} - -func setAssetOwner(from, newOwner common.Name, assetId uint64, prikey *ecdsa.PrivateKey) (common.Hash, error) { - ast := &asset.AssetObject{ - AssetId: assetId, - Owner: newOwner, - } - payload, err := rlp.EncodeToBytes(ast) - if err != nil { - return common.Hash{}, err - } - nonce, err := GetNonce(common.Name(from)) - if err != nil { - return common.Hash{}, err - } - - gc := NewGeAction(types.SetAssetOwner, from, "", nonce, assetId, Gaslimit, nil, payload, prikey) - var gcs []*GenAction - gcs = append(gcs, gc) - return SendTxTest(gcs) -} - -func GenerateAssetName(namePrefix string, addStrLen int) string { - return GenerateRandomName(namePrefix, addStrLen) -} - -func GetNextAssetIdFrom(fromAssetId uint64) uint64 { - if fromAssetId == 0 { - fromAssetId = 1 - } - for { - assetInfo, err := GetAssetInfoById(fromAssetId) - if err != nil { - return 0 - } - if assetInfo.AssetId > 0 { - fromAssetId++ - } else { - return fromAssetId - } - } -} - -func GenerateValidAssetName(namePrefix string, suffixStrLen int) (string, error) { - maxTime := 10 - for maxTime > 0 { - newAssetName := GenerateAssetName(namePrefix, suffixStrLen) - bExist, err := IsAssetExist(newAssetName) - if err != nil { - return "", errors.New("判断资产是否存在的RPC接口调用失败:" + err.Error()) - } - if !bExist { - return newAssetName, nil - } - maxTime-- - } - return "", errors.New("难以获得有效的资产名") - -} diff --git a/test/systemtestcase/rpc/transactionAPI.go b/test/systemtestcase/rpc/transactionAPI.go deleted file mode 100644 index cd562fec..00000000 --- a/test/systemtestcase/rpc/transactionAPI.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package rpc - -import ( - "fmt" - "time" - - "github.com/fractalplatform/fractal/common" - "github.com/fractalplatform/fractal/types" -) - -func GetReceiptByTxHash(hash common.Hash) (*types.RPCReceipt, error) { - receipt := &types.RPCReceipt{} - err := ClientCall("ft_getTransactionReceipt", receipt, hash.Hex()) - - return receipt, err -} - -func DelayGetReceiptByTxHash(txHash common.Hash, maxTime uint) (*types.RPCReceipt, bool, error) { - for maxTime > 0 { - time.Sleep(time.Duration(1) * time.Second) - receipt, err := GetReceiptByTxHash(txHash) - if err != nil { - fmt.Println("DelayGetReceiptByTxHash:" + err.Error()) - return nil, false, err - } - - //json, _ := json.Marshal(receipt) - //fmt.Println("DelayGetReceiptByTxHash:" + string(json)) - if receipt.BlockNumber > 0 { - return receipt, false, nil - } - maxTime-- - } - return &types.RPCReceipt{}, maxTime == 0, nil -} - -func GetTxpoolStatus() (int, int) { - result := map[string]int{} - ClientCall("txpool_status", result) - return result["pending"], result["queue"] -} - -func IsTxpoolFull() bool { - pendingTxNum, _ := GetTxpoolStatus() - return pendingTxNum >= MaxTxNumInTxpool -} diff --git a/test/systemtestcase/rpc/utils.go b/test/systemtestcase/rpc/utils.go deleted file mode 100644 index 67166ec4..00000000 --- a/test/systemtestcase/rpc/utils.go +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package rpc - -import ( - "context" - "crypto/ecdsa" - "fmt" - "math/big" - "math/rand" - "os" - "os/user" - "path/filepath" - "runtime" - "sync" - "time" - - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/fractalplatform/fractal/common" - "github.com/fractalplatform/fractal/crypto" - "github.com/fractalplatform/fractal/params" - "github.com/fractalplatform/fractal/types" - "github.com/fractalplatform/fractal/utils/rlp" -) - -import ( - "github.com/fractalplatform/fractal/rpc" -) - -var ( - once sync.Once - clientInstance *rpc.Client - hostIp = "192.168.2.13" // "127.0.0.1" // - port = 8090 // 8545 // - gasPrice = big.NewInt(2000000) -) - -type GenAction struct { - *types.Action - PrivateKey *ecdsa.PrivateKey -} - -// DefultURL default rpc url -func DefultURL() string { - return fmt.Sprintf("http://%s:%d", hostIp, port) -} - -func GeneratePubKey() (common.PubKey, *ecdsa.PrivateKey) { - prikey, _ := crypto.GenerateKey() - return common.BytesToPubKey(crypto.FromECDSAPub(&prikey.PublicKey)), prikey -} - -func NewGeAction(at types.ActionType, from, to common.Name, nonce uint64, assetid uint64, gaslimit uint64, amount *big.Int, payload []byte, prikey *ecdsa.PrivateKey) *GenAction { - action := types.NewAction(at, from, to, nonce, assetid, gaslimit, amount, payload) - return &GenAction{ - Action: action, - PrivateKey: prikey, - } -} -func SendTxTest(gcs []*GenAction) (common.Hash, error) { - //nonce := GetNonce(sendaddr, "latest") - signer := types.NewSigner(params.DefaultChainconfig.ChainID) - var actions []*types.Action - for _, v := range gcs { - actions = append(actions, v.Action) - } - tx := types.NewTransaction(uint64(1), gasPrice, actions...) - for _, v := range gcs { - err := types.SignAction(v.Action, tx, signer, v.PrivateKey) - if err != nil { - return common.Hash{}, err - } - } - rawtx, _ := rlp.EncodeToBytes(tx) - hash, err := SendRawTx(rawtx) - return hash, err -} - -//SendRawTx send raw transaction -func SendRawTx(rawTx []byte) (common.Hash, error) { - hash := new(common.Hash) - err := ClientCall("ft_sendRawTransaction", hash, hexutil.Bytes(rawTx)) - return *hash, err -} - -// MustRPCClient Wraper rpc's client -func MustRPCClient() (*rpc.Client, error) { - once.Do(func() { - client, err := rpc.DialHTTP(DefultURL()) - if err != nil { - return - } - clientInstance = client - }) - - return clientInstance, nil -} - -// ClientCall Wrapper rpc call api. -func ClientCall(method string, result interface{}, args ...interface{}) error { - client, err := MustRPCClient() - if err != nil { - return err - } - err = client.CallContext(context.Background(), result, method, args...) - if err != nil { - return err - } - return nil -} - -// GasPrice suggest gas price -func GasPrice() (*big.Int, error) { - gp := big.NewInt(0) - err := ClientCall("ft_gasPrice", gp) - return gp, err -} - -// GetNonce get nonce by address and block number. -func GetNonce(accountname common.Name) (uint64, error) { - nonce := new(uint64) - err := ClientCall("account_getNonce", nonce, accountname) - return *nonce, err -} - -// defaultDataDir is the default data directory to use for the databases and other -// persistence requirements. -func DefaultDataDir() string { - // Try to place the data folder in the user's home dir - home := HomeDir() - if home != "" { - if runtime.GOOS == "darwin" { - return filepath.Join(home, "Library", "pi_ledger") - } else if runtime.GOOS == "windows" { - return filepath.Join(home, "AppData", "Roaming", "pi_ledger") - } else { - return filepath.Join(home, ".pi_ledger") - } - } - // As we cannot guess a stable location, return empty and handle later - return "" -} - -func HomeDir() string { - if home := os.Getenv("HOME"); home != "" { - return home - } - if usr, err := user.Current(); err == nil { - return usr.HomeDir - } - return "" -} - -func GenerateRandomName(namePrefix string, addStrLen int) string { - newRandomName := namePrefix - var str string = "abcdefghijklmnopqrstuvwxyz0123456789" - size := len(str) - rand.Seed(time.Now().Unix()) - for i := 0; i < addStrLen; i++ { - index := rand.Intn(10000) % size - newRandomName += string(str[index]) - } - return newRandomName -} diff --git a/test/systemtestcase/transfer_test.go b/test/systemtestcase/transfer_test.go deleted file mode 100644 index 06ab7d0f..00000000 --- a/test/systemtestcase/transfer_test.go +++ /dev/null @@ -1 +0,0 @@ -package main diff --git a/test/transaction/sendtransaction.go b/test/transaction/sendtransaction.go new file mode 100644 index 00000000..5daf9b97 --- /dev/null +++ b/test/transaction/sendtransaction.go @@ -0,0 +1,267 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package main + +import ( + "crypto/ecdsa" + "fmt" + "math/big" + "time" + + "github.com/fractalplatform/fractal/accountmanager" + "github.com/fractalplatform/fractal/asset" + "github.com/fractalplatform/fractal/common" + "github.com/fractalplatform/fractal/crypto" + "github.com/fractalplatform/fractal/types" + "github.com/fractalplatform/fractal/utils/rlp" + jww "github.com/spf13/jwalterweatherman" + + tc "github.com/fractalplatform/fractal/test/common" +) + +var ( + minerprikey, _ = crypto.HexToECDSA("289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032") + minerpubkey = common.HexToPubKey("0x047db227d7094ce215c3a0f57e1bcc732551fe351f94249471934567e0f5dc1bf795962b8cccb87a2eb56b29fbe37d614e2f4c3c45b789ae4f1f51f4cb21972ffd") + newPrivateKey, _ = crypto.HexToECDSA("8ee847ae5974a13ce9df66083e453ea1e0f7995379ed027a98e827aa8b6bc211") + gaslimit = uint64(2000000) + minername = common.Name("ftsystemio") + toname = common.Name("testtest11") + issueAmount = new(big.Int).Mul(big.NewInt(10), big.NewInt(1e10)) + inCreateAmount = big.NewInt(100000000) + indexstr = "abcdefghijklmnopqrstuvwxyz0123456789" + basefrom = "newnamefrom%s" + baseto = "newnameto%s" + testbase = "testtest" + testname1 = "" +) + +type GenAction struct { + *types.Action + PrivateKey *ecdsa.PrivateKey +} + +func init() { + jww.SetLogThreshold(jww.LevelTrace) + jww.SetStdoutThreshold(jww.LevelInfo) +} + +func GeneragePubKey() (common.PubKey, *ecdsa.PrivateKey) { + prikey, _ := crypto.GenerateKey() + return common.BytesToPubKey(crypto.FromECDSAPub(&prikey.PublicKey)), prikey +} + +func createAccount(accountName common.Name, founder common.Name, from, newname common.Name, nonce uint64, publickey common.PubKey, prikey *ecdsa.PrivateKey) { + account := &accountmanager.AccountAction{ + AccountName: accountName, + Founder: founder, + ChargeRatio: 80, + PublicKey: publickey, + } + payload, err := rlp.EncodeToBytes(account) + if err != nil { + panic("rlp payload err") + } + to := newname + gc := newGeAction(types.CreateAccount, from, to, nonce, 1, gaslimit, nil, payload, prikey) + var gcs []*GenAction + gcs = append(gcs, gc) + sendTxTest(gcs) +} + +func updateAccount(from, founder common.Name, nonce uint64, privatekey *ecdsa.PrivateKey) { + account := &accountmanager.AccountAction{ + AccountName: from, + Founder: founder, + ChargeRatio: 80, + } + payload, err := rlp.EncodeToBytes(account) + if err != nil { + panic("rlp payload err") + } + gc := newGeAction(types.UpdateAccount, from, "", nonce, 1, gaslimit, nil, payload, privatekey) + var gcs []*GenAction + gcs = append(gcs, gc) + sendTxTest(gcs) +} + +func updateAccountAuthor(from common.Name, oldpubkey, newpubkey common.PubKey, nonce uint64, privatekey *ecdsa.PrivateKey) { + oldauthor := &common.Author{ + Owner: oldpubkey, + } + oldauthoraction := &accountmanager.AuthorAction{accountmanager.DeleteAuthor, oldauthor} + newauthor := &common.Author{ + Owner: newpubkey, + Weight: 1, + } + newauthoraction := &accountmanager.AuthorAction{accountmanager.AddAuthor, newauthor} + accountauthor := &accountmanager.AccountAuthorAction{ + AuthorActions: []*accountmanager.AuthorAction{oldauthoraction, newauthoraction}, + } + payload, err := rlp.EncodeToBytes(accountauthor) + if err != nil { + panic("rlp payload err") + } + gc := newGeAction(types.UpdateAccountAuthor, from, "", nonce, 1, gaslimit, nil, payload, privatekey) + var gcs []*GenAction + gcs = append(gcs, gc) + sendTxTest(gcs) +} + +func issueAsset(atype types.ActionType, assetid uint64, from, Owner, founder common.Name, amount *big.Int, assetname string, nonce uint64, prikey *ecdsa.PrivateKey) { + ast := &asset.AssetObject{ + AssetId: assetid, + AssetName: assetname, + Symbol: fmt.Sprintf("symbol%d", nonce), + Amount: amount, + Decimals: 2, + Founder: founder, + AddIssue: nil, + Owner: Owner, + UpperLimit: big.NewInt(100000000000000000), + } + payload, err := rlp.EncodeToBytes(ast) + if err != nil { + panic("rlp payload err") + } + gc := newGeAction(atype, from, "", nonce, 1, gaslimit, nil, payload, prikey) + var gcs []*GenAction + gcs = append(gcs, gc) + sendTxTest(gcs) +} + +func increaseAsset(from, to common.Name, assetid uint64, nonce uint64, prikey *ecdsa.PrivateKey) { + ast := &accountmanager.IncAsset{ + AssetId: assetid, + To: to, + Amount: inCreateAmount, + } + payload, err := rlp.EncodeToBytes(ast) + if err != nil { + panic("rlp payload err") + } + gc := newGeAction(types.IncreaseAsset, from, "", nonce, 1, gaslimit, nil, payload, prikey) + var gcs []*GenAction + gcs = append(gcs, gc) + sendTxTest(gcs) +} + +func transfer(from, to common.Name, amount *big.Int, nonce uint64, prikey *ecdsa.PrivateKey) { + gc := newGeAction(types.Transfer, from, to, nonce, 1, gaslimit, amount, nil, prikey) + var gcs []*GenAction + gcs = append(gcs, gc) + sendTxTest(gcs) +} + +func newGeAction(at types.ActionType, from, to common.Name, nonce uint64, assetid uint64, gaslimit uint64, amount *big.Int, payload []byte, prikey *ecdsa.PrivateKey) *GenAction { + action := types.NewAction(at, from, to, nonce, assetid, gaslimit, amount, payload) + + return &GenAction{ + Action: action, + PrivateKey: prikey, + } +} + +func sendTxTest(gcs []*GenAction) { + signer := types.NewSigner(big.NewInt(1)) + var actions []*types.Action + for _, v := range gcs { + actions = append(actions, v.Action) + } + tx := types.NewTransaction(uint64(1), big.NewInt(1), actions...) + for _, v := range gcs { + keypair := types.MakeKeyPair(v.PrivateKey, []uint64{0}) + err := types.SignActionWithMultiKey(v.Action, tx, signer, []*types.KeyPair{keypair}) + if err != nil { + panic(fmt.Sprintf("SignAction err %v", err)) + } + + } + rawtx, err := rlp.EncodeToBytes(tx) + if err != nil { + jww.ERROR.Fatalln(err) + } + hash, err := tc.SendRawTx(rawtx) + if err != nil { + panic(err) + } + jww.INFO.Printf("hash: %x", hash) +} + +var ( + pub1 = "0x0468cba7890aae10f3dde57d269cf7c4ba14cc0efc2afee86791b0a22b794820febdb2e5c6c56878a308e7f62ad2d75739de40313a72975c993dd76a5301a03d12" + pri1 = "357a2cbdd91686dcbe2c612e9bed85d4415f62446440839466bf7b2f1ab135b7" + + pub2 = "0x04fa0b2a9b2d0542bf2912c4c6500ba64a26652e302370ed5645b1c32df50fbe7a5f12da0b278638e1df6753a7c6ac09e68cb748cfe6d45102114f52e95e9ed652" + pri2 = "340cde826336f1adb8673ec945819d073af00cffb5c174542e35ff346445e213" + + pubkey1 = common.HexToPubKey(pub1) + prikey1, _ = crypto.HexToECDSA(pri1) + + pubkey2 = common.HexToPubKey(pub2) + prikey2, _ = crypto.HexToECDSA(pri2) +) + +func main() { + nonce, _ := tc.GetNonce(minername) + //pub, pri := GeneragePubKey() + createAccount(toname, "", minername, toname, nonce, pubkey1, minerprikey) + nonce++ + transfer(minername, toname, issueAmount, nonce, minerprikey) + nonce++ + + newname2 := common.Name("testtest12") + //pub1, _ := GeneragePubKey() + createAccount(newname2, "", minername, newname2, nonce, pubkey2, minerprikey) + nonce++ + + transfer(minername, newname2, issueAmount, nonce, minerprikey) + nonce++ + + pubkey3, prikey3 := GeneragePubKey() + newname3 := common.Name("testtest13") + createAccount(newname3, "", minername, newname3, nonce, pubkey3, minerprikey) + nonce++ + + transfer(minername, newname3, issueAmount, nonce, minerprikey) + nonce++ + + time.Sleep(time.Duration(3) * time.Second) + + t3nonce, _ := tc.GetNonce(newname3) + updateAccount(newname3, newname2, t3nonce, prikey3) + time.Sleep(time.Duration(3) * time.Second) + + t3nonce, _ = tc.GetNonce(newname3) + updateAccountAuthor(newname3, pubkey3, pubkey2, t3nonce, prikey3) + time.Sleep(time.Duration(3) * time.Second) + + t3nonce, _ = tc.GetNonce(newname3) + issueAsset(types.IssueAsset, 0, newname3, newname3, newname3, big.NewInt(10000000000000), "testnewasset", t3nonce, prikey2) + time.Sleep(time.Duration(3) * time.Second) + + t3nonce++ + increaseAsset(newname3, newname3, 2, t3nonce, prikey2) + time.Sleep(time.Duration(3) * time.Second) + + t3nonce++ + issueAsset(types.SetAssetOwner, 2, newname3, toname, "", big.NewInt(100000), "testnewasset", t3nonce, prikey2) + time.Sleep(time.Duration(3) * time.Second) + + t3nonce++ + issueAsset(types.DestroyAsset, 2, toname, "", "", big.NewInt(100000), "testnewasset", t3nonce, prikey1) + +} diff --git a/test/tx/main.go b/test/tx/main.go deleted file mode 100644 index 150833ad..00000000 --- a/test/tx/main.go +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package main - -import ( - "crypto/ecdsa" - "fmt" - "math/big" - "time" - - "os" - "strconv" - "sync" - - "github.com/fractalplatform/fractal/asset" - "github.com/fractalplatform/fractal/common" - "github.com/fractalplatform/fractal/crypto" - "github.com/fractalplatform/fractal/params" - tc "github.com/fractalplatform/fractal/test/common" - "github.com/fractalplatform/fractal/types" - "github.com/fractalplatform/fractal/utils/rlp" - jww "github.com/spf13/jwalterweatherman" -) - -var ( - minerprikey []*ecdsa.PrivateKey - minername []common.Name - ipc []string - index int - - basefrom []string - baseto []string - - ftproducer1key, _ = crypto.HexToECDSA("289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032") - ftproducer1name = common.Name("ftproducer1") - - ftproducer2key, _ = crypto.HexToECDSA("9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658") - ftproducer2name = common.Name("ftproducer2") - - ftproducer3key, _ = crypto.HexToECDSA("8605cf6e76c9fc8ac079d0f841bd5e99bd3ad40fdd56af067993ed14fc5bfca8") - ftproducer3name = common.Name("ftproducer3") - - gaslimit = uint64(200000) - issueAmount = big.NewInt(100000000000000) - inCreateAmount = big.NewInt(100000000) - indexstr = "abcdefghijklmnopqrstuvwxyz0123456789" -) - -type GenAction struct { - *types.Action - PrivateKey *ecdsa.PrivateKey -} - -func init() { - jww.SetLogThreshold(jww.LevelTrace) - jww.SetStdoutThreshold(jww.LevelInfo) - - sysprikey, _ := crypto.HexToECDSA("289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032") - //syspubkey := common.HexToPubKey("0x047db227d7094ce215c3a0f57e1bcc732551fe351f94249471934567e0f5dc1bf795962b8cccb87a2eb56b29fbe37d614e2f4c3c45b789ae4f1f51f4cb21972ffd") - minerprikey = append(minerprikey, sysprikey) - minername = append(minername, params.DefaultChainconfig.SysName) - minerprikey = append(minerprikey, ftproducer1key, ftproducer2key, ftproducer3key) - minername = append(minername, ftproducer1name, ftproducer2name, ftproducer3name) - ipc = append(ipc, "/home/Fractal/piTest/node_01/data/ft.ipc", "/home/Fractal/piTest/node_02/data/ft.ipc", "/home/Fractal/piTest/node_03/data/ft.ipc") - - basefrom = append(basefrom, "newnamefrom1%s", "newnamefrom2%s", "newnamefrom3%s") - baseto = append(baseto, "newnameto1%s", "newnameto2%s", "newnameto3%s") -} - -func GeneragePubKey() (common.PubKey, *ecdsa.PrivateKey) { - prikey, _ := crypto.GenerateKey() - return common.BytesToPubKey(crypto.FromECDSAPub(&prikey.PublicKey)), prikey -} - -func createAccount(from, newname common.Name, nonce uint64, prikey *ecdsa.PrivateKey, pubkey common.PubKey) (common.Hash, error) { - to := newname - gc := newGeAction(types.CreateAccount, from, to, nonce, 1, gaslimit, nil, pubkey[:], prikey) - var gcs []*GenAction - gcs = append(gcs, gc) - return sendTxTest(gcs) -} - -func issueAsset(from, Owner common.Name, amount *big.Int, assetname string, nonce uint64, prikey *ecdsa.PrivateKey) (common.Hash, error) { - ast := &asset.AssetObject{ - AssetName: assetname, - Symbol: fmt.Sprintf("symbol%d", nonce), - Amount: amount, - Decimals: 2, - Owner: Owner, - Founder: from, - UpperLimit: big.NewInt(500000000000000000), - } - payload, err := rlp.EncodeToBytes(ast) - if err != nil { - panic("rlp payload err") - } - gc := newGeAction(types.IssueAsset, from, "", nonce, 1, gaslimit, nil, payload, prikey) - var gcs []*GenAction - gcs = append(gcs, gc) - return sendTxTest(gcs) -} - -func transfer(from, to common.Name, amount *big.Int, nonce uint64, prikey *ecdsa.PrivateKey) (common.Hash, error) { - gc := newGeAction(types.Transfer, from, to, nonce, 1, gaslimit, amount, nil, prikey) - var gcs []*GenAction - gcs = append(gcs, gc) - return sendTxTest(gcs) -} - -func newGeAction(at types.ActionType, from, to common.Name, nonce uint64, assetid uint64, gaslimit uint64, amount *big.Int, payload []byte, prikey *ecdsa.PrivateKey) *GenAction { - action := types.NewAction(at, from, to, nonce, assetid, gaslimit, amount, payload) - return &GenAction{ - Action: action, - PrivateKey: prikey, - } -} - -func sendTxTest(gcs []*GenAction) (common.Hash, error) { - //nonce := GetNonce(sendaddr, "latest") - signer := types.NewSigner(params.DefaultChainconfig.ChainID) - var actions []*types.Action - for _, v := range gcs { - actions = append(actions, v.Action) - } - tx := types.NewTransaction(uint64(1), big.NewInt(2), actions...) - for _, v := range gcs { - err := types.SignAction(v.Action, tx, signer, v.PrivateKey) - if err != nil { - panic(fmt.Sprintf("SignAction err %v", err)) - } - } - rawtx, err := rlp.EncodeToBytes(tx) - if err != nil { - jww.ERROR.Fatalln(err) - } - return tc.SendRawTx(rawtx) -} - -func stressTest() { - if len(os.Args) < 3 { - panic("argument not enough") - } - threadsize, err := strconv.Atoi(os.Args[1]) - if err != nil { - panic("argument err") - } - - index, err = strconv.Atoi(os.Args[2]) - if err != nil { - panic("argument err") - } - - if threadsize >= len(indexstr) { - panic("first argument over") - } - type UserInfo struct { - name common.Name - pubkey common.PubKey - prikey *ecdsa.PrivateKey - } - - //tc.SetDefultURL(ipc[index]) - newusersfrom := make([]UserInfo, threadsize) - newusersto := make([]UserInfo, threadsize) - for i := 0; i < threadsize; i++ { - newusersfrom[i].name = common.Name(fmt.Sprintf(basefrom[index], string(indexstr[i]))) - newusersfrom[i].pubkey, newusersfrom[i].prikey = GeneragePubKey() - newusersto[i].name = common.Name(fmt.Sprintf(baseto[index], string(indexstr[i]))) - newusersto[i].pubkey, newusersto[i].prikey = GeneragePubKey() - } - nonce, _ := tc.GetNonce(minername[index]) - for i := 0; i < threadsize; i++ { - h, err := createAccount(minername[index], newusersfrom[i].name, nonce, minerprikey[index], newusersfrom[i].pubkey) - fmt.Println("create account from :", h.String(), "err:", err) - nonce += 1 - h, err = createAccount(minername[index], newusersto[i].name, nonce, minerprikey[index], newusersto[i].pubkey) - fmt.Println("create account to :", h.String(), "err:", err) - nonce += 1 - } - flag := true - for { - flag = true - jww.INFO.Printf("check createAccount \n") - time.Sleep(time.Duration(7) * time.Second) - for i := 0; i < threadsize; i++ { - act, _ := tc.GetAccountByName(newusersfrom[i].name) - jww.INFO.Println("name", act.AcctName, "check createAccount 1", newusersfrom[i].name) - if act.AcctName != newusersfrom[i].name { - flag = false - } - act, _ = tc.GetAccountByName(newusersto[i].name) - jww.INFO.Println("name", act.AcctName, "check createAccount 2", newusersto[i].name) - if act.AcctName != newusersto[i].name { - flag = false - } - } - if flag { - break - } - } - jww.INFO.Printf("all account create success \n") - nonce, _ = tc.GetNonce(minername[index]) - for i := 0; i < threadsize; i++ { - h, err := transfer(minername[index], newusersfrom[i].name, big.NewInt(10000000000000), nonce, minerprikey[index]) - fmt.Println("transfer to prodcer :", h.String(), "err:", err) - nonce += 1 - } - for { - flag = true - jww.INFO.Printf("check transfer \n") - time.Sleep(time.Duration(7) * time.Second) - for i := 0; i < len(newusersfrom); i++ { - balance, _ := tc.GetAccountBalanceByID(newusersfrom[i].name, 1) - fmt.Println("name", newusersfrom[i].name, "balance:", balance.Int64()) - if balance.Int64() < 10000000000000 { - flag = false - } - } - if flag { - break - } - } - jww.INFO.Printf("all account transfer success \n") - if len(os.Args) < 2 { - panic("argument not enough") - } - type tmpasset struct { - from common.Name - to common.Name - assetid uint64 - } - ch := make(chan tmpasset, 100) - txsnumber := make([]*uint64, threadsize) - for i := 0; i < threadsize; i++ { - txsnumber[i] = new(uint64) - } - var wg sync.WaitGroup - for i := 0; i < threadsize; i++ { - wg.Add(1) - go func(from, to common.Name, prikey *ecdsa.PrivateKey, c chan tmpasset, txnumber *uint64) { - defer wg.Add(-1) - assetname := "na" + from.String() - innonce, _ := tc.GetNonce(from) - h, err := issueAsset(from, from, big.NewInt(100000000000000), assetname, innonce, prikey) - fmt.Println("issueAsset :", h.String(), "err:", err) - innonce += 1 - var astInfo *asset.AssetObject - for { - jww.INFO.Printf("check issueAsset %v\n", from) - time.Sleep(time.Duration(7) * time.Second) - astInfo, err = tc.GetAssetInfoByName(assetname) - jww.INFO.Println(astInfo, err) - fmt.Printf("astInfo.AssetName:[%s] assetname:[%s]\n", astInfo.AssetName, assetname) - if astInfo != nil && astInfo.AssetName == assetname { - //jww.INFO.Println("issueasset success ", assetname, astInfo.AssetId) - jww.INFO.Println("assetid ", astInfo.AssetId) - break - } - } - jww.INFO.Println("start sent transfer forevery \n") - flag := true - for { - gc := newGeAction(types.Transfer, from, to, innonce, astInfo.AssetId, gaslimit, big.NewInt(1), nil, prikey) - var gcs []*GenAction - gcs = append(gcs, gc) - sendTxTest(gcs) - innonce += 1 - if flag { - for { - jww.INFO.Printf("check balance %v\n", to) - time.Sleep(time.Duration(7) * time.Second) - balance, _ := tc.GetAccountBalanceByID(to, astInfo.AssetId) - if balance.Int64() >= 1 { - jww.INFO.Printf("Transfer to %v success\n", to) - flag = false - c <- tmpasset{from, to, astInfo.AssetId} - break - } - } - } - *txnumber += 1 - } - }(newusersfrom[i].name, newusersto[i].name, newusersfrom[i].prikey, ch, txsnumber[i]) - } - total := 0 - var toassets []tmpasset - for { - tmp := <-ch - toassets = append(toassets, tmp) - total += 1 - if total >= threadsize { - break - } - } - type calculatetps struct { - tm int64 - balance int64 - } - caltps := make(map[common.Name]*calculatetps, len(toassets)) - tm := time.Now().Unix() - for i := 0; i < len(toassets); i++ { - caltps[toassets[i].to] = &calculatetps{tm, 0} - } - - go func() { - defer wg.Add(-1) - nonce, _ := tc.GetNonce(minername[index]) - for { - time.Sleep(time.Duration(20) * time.Second) - for i := 0; i < threadsize; i++ { - h, err := transfer(minername[index], toassets[i].from, big.NewInt(100000), nonce, minerprikey[index]) - jww.INFO.Println("transfer to custom :", h.String(), "err:", err) - nonce += 1 - } - } - }() - lastnumber := uint64(0) - lasttime := time.Now().Unix() - for { - time.Sleep(time.Duration(10) * time.Second) - tmpnumber := uint64(0) - actualnumber := uint64(0) - tmptime := time.Now().Unix() - for i := 0; i < threadsize; i++ { - tmpnumber += *txsnumber[i] - balance, _ := tc.GetAccountBalanceByID(toassets[i].to, toassets[i].assetid) - actualnumber += balance.Uint64() - } - jww.INFO.Printf(" send tx number: %d\t succes num: %d\t tps: %d/s", - tmpnumber, actualnumber, (actualnumber-lastnumber)/uint64(tmptime-lasttime)) - lasttime = tmptime - lastnumber = actualnumber - } - - wg.Wait() -} - -func main() { - stressTest() -} diff --git a/txpool/config.go b/txpool/config.go index dc597173..2fda4145 100644 --- a/txpool/config.go +++ b/txpool/config.go @@ -16,29 +16,80 @@ package txpool -import "time" +import ( + "time" + + "github.com/ethereum/go-ethereum/log" +) // Config are the configuration parameters of the transaction pool. type Config struct { - NoLocals bool `mapstructure:"txpool-nolocals"` // Whether local transaction handling should be disabled - Journal string `mapstructure:"txpool-journal"` // Journal of local transactions to survive node restarts - Rejournal time.Duration `mapstructure:"txpool-rejournal"` // Time interval to regenerate the local transaction journal + NoLocals bool `mapstructure:"nolocals"` // Whether local transaction handling should be disabled + Journal string `mapstructure:"journal"` // Journal of local transactions to survive node restarts + Rejournal time.Duration `mapstructure:"rejournal"` // Time interval to regenerate the local transaction journal - PriceLimit uint64 `mapstructure:"txpool-pricelimit"` // Minimum gas price to enforce for acceptance into the pool - PriceBump uint64 `mapstructure:"txpool-pricebump"` // Minimum price bump percentage to replace an already existing transaction (nonce) + PriceLimit uint64 `mapstructure:"pricelimit"` // Minimum gas price to enforce for acceptance into the pool + PriceBump uint64 `mapstructure:"pricebump"` // Minimum price bump percentage to replace an already existing transaction (nonce) - AccountSlots uint64 `mapstructure:"txpool-accountslots"` // Minimum number of executable transaction slots guaranteed per account - GlobalSlots uint64 `mapstructure:"txpool-globalslots"` // Maximum number of executable transaction slots for all accounts - AccountQueue uint64 `mapstructure:"txpool-accountqueue"` // Maximum number of non-executable transaction slots permitted per account - GlobalQueue uint64 `mapstructure:"txpool-globalqueue"` // Maximum number of non-executable transaction slots for all accounts + AccountSlots uint64 `mapstructure:"accountslots"` // Minimum number of executable transaction slots guaranteed per account + GlobalSlots uint64 `mapstructure:"globalslots"` // Maximum number of executable transaction slots for all accounts + AccountQueue uint64 `mapstructure:"accountqueue"` // Maximum number of non-executable transaction slots permitted per account + GlobalQueue uint64 `mapstructure:"globalqueue"` // Maximum number of non-executable transaction slots for all accounts - Lifetime time.Duration `mapstructure:"txpool-lifetime"` // Maximum amount of time non-executable transaction are queued + Lifetime time.Duration `mapstructure:"lifetime"` // Maximum amount of time non-executable transaction are queued GasAssetID uint64 } -func (c *Config) check() Config { - conf := *c - //todo check config +// DefaultTxPoolConfig default txpool config +var DefaultTxPoolConfig = &Config{ + Journal: "transactions.rlp", + Rejournal: time.Hour, + PriceLimit: 1, + PriceBump: 10, + AccountSlots: 128, + GlobalSlots: 4096, + AccountQueue: 1280, + GlobalQueue: 4096, + Lifetime: 3 * time.Hour, + GasAssetID: 1, +} + +// check checks the provided user configurations and changes anything that's +// unreasonable or unworkable. +func (config *Config) check() Config { + conf := *config + if conf.Rejournal < time.Second { + log.Warn("Sanitizing invalid txpool journal time", "provided", conf.Rejournal, "updated", time.Second) + conf.Rejournal = time.Second + } + if conf.PriceLimit < 1 { + log.Warn("Sanitizing invalid txpool price limit", "provided", conf.PriceLimit, "updated", DefaultTxPoolConfig.PriceLimit) + conf.PriceLimit = DefaultTxPoolConfig.PriceLimit + } + if conf.PriceBump < 1 { + log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", DefaultTxPoolConfig.PriceBump) + conf.PriceBump = DefaultTxPoolConfig.PriceBump + } + if conf.AccountSlots < 1 { + log.Warn("Sanitizing invalid txpool account slots", "provided", conf.AccountSlots, "updated", DefaultTxPoolConfig.AccountSlots) + conf.AccountSlots = DefaultTxPoolConfig.AccountSlots + } + if conf.GlobalSlots < 1 { + log.Warn("Sanitizing invalid txpool global slots", "provided", conf.GlobalSlots, "updated", DefaultTxPoolConfig.GlobalSlots) + conf.GlobalSlots = DefaultTxPoolConfig.GlobalSlots + } + if conf.AccountQueue < 1 { + log.Warn("Sanitizing invalid txpool account queue", "provided", conf.AccountQueue, "updated", DefaultTxPoolConfig.AccountQueue) + conf.AccountQueue = DefaultTxPoolConfig.AccountQueue + } + if conf.GlobalQueue < 1 { + log.Warn("Sanitizing invalid txpool global queue", "provided", conf.GlobalQueue, "updated", DefaultTxPoolConfig.GlobalQueue) + conf.GlobalQueue = DefaultTxPoolConfig.GlobalQueue + } + if conf.Lifetime < 1 { + log.Warn("Sanitizing invalid txpool lifetime", "provided", conf.Lifetime, "updated", DefaultTxPoolConfig.Lifetime) + conf.Lifetime = DefaultTxPoolConfig.Lifetime + } return conf } diff --git a/txpool/error.go b/txpool/error.go index 2f0405da..be8bbdb9 100644 --- a/txpool/error.go +++ b/txpool/error.go @@ -60,4 +60,10 @@ var ( // than some meaningful limit a user might use. This is not a consensus error // making the transaction invalid, rather a DOS protection. ErrOversizedData = errors.New("oversized data") + + // ErrEmptyActions transaction no actions + ErrEmptyActions = errors.New("transaction no actions") + + // ErrInvalidValue action value invalid + ErrInvalidValue = errors.New("action value invalid") ) diff --git a/txpool/list.go b/txpool/list.go index cbb22716..78989269 100644 --- a/txpool/list.go +++ b/txpool/list.go @@ -93,7 +93,7 @@ func (l *txList) Forward(threshold uint64) []*types.Transaction { // than the provided thresholds. Every removed transaction is returned for any // post-removal maintenance. Strict-mode invalidated transactions are also // returned. -func (l *txList) Filter(costLimit *big.Int, gasLimit uint64, getBalance func(name common.Name, assetID uint64) (*big.Int, error)) ([]*types.Transaction, []*types.Transaction) { +func (l *txList) Filter(costLimit *big.Int, gasLimit uint64, getBalance func(name common.Name, assetID uint64, typeID uint64) (*big.Int, error)) ([]*types.Transaction, []*types.Transaction) { // If all transactions are below the threshold, short circuit if l.gascostcap.Cmp(costLimit) > 0 { l.gascostcap = new(big.Int).Set(costLimit) // Lower the caps to the thresholds @@ -105,7 +105,7 @@ func (l *txList) Filter(costLimit *big.Int, gasLimit uint64, getBalance func(nam // Filter out all the transactions above the account's funds removed := l.txs.Filter(func(tx *types.Transaction) bool { act := tx.GetActions()[0] - balance, _ := getBalance(act.Sender(), act.AssetID()) + balance, _ := getBalance(act.Sender(), act.AssetID(), 0) // todo change action return act.Value().Cmp(balance) > 0 || tx.Cost().Cmp(costLimit) > 0 || act.Gas() > gasLimit }) diff --git a/txpool/list_test.go b/txpool/list_test.go index 8e6a6974..12be3276 100644 --- a/txpool/list_test.go +++ b/txpool/list_test.go @@ -31,7 +31,7 @@ func TestStrictTxListAdd(t *testing.T) { txs := make([]*types.Transaction, 1024) for i := 0; i < len(txs); i++ { - txs[i] = transaction(uint64(i), "from", "to", 0, key) + txs[i] = transaction(uint64(i), "fromtest", "tototest", 0, key) } // Insert the transactions in a random order @@ -39,6 +39,7 @@ func TestStrictTxListAdd(t *testing.T) { for _, v := range rand.Perm(len(txs)) { list.Add(txs[v], 10) } + // Verify internal state assert.Equal(t, len(list.txs.items), len(txs)) diff --git a/txpool/priceheap_test.go b/txpool/priceheap_test.go index e644e173..9d8112ec 100644 --- a/txpool/priceheap_test.go +++ b/txpool/priceheap_test.go @@ -29,8 +29,8 @@ import ( func getPriceTx(price *big.Int, nonce uint64) *types.Transaction { return types.NewTransaction(0, price, types.NewAction( types.Transfer, - common.Name("from"), - common.Name("to"), + common.Name("fromtest"), + common.Name("tototest"), nonce, uint64(3), uint64(2000), diff --git a/txpool/sendercacher.go b/txpool/sendercacher.go index da9d6f85..5b8de46a 100644 --- a/txpool/sendercacher.go +++ b/txpool/sendercacher.go @@ -63,7 +63,7 @@ func (cacher *txSenderCacher) cache() { for task := range cacher.tasks { for i := 0; i < len(task.txs); i += task.inc { for _, a := range task.txs[i].GetActions() { - types.Recover(task.signer, a, task.txs[i]) + types.RecoverMultiKey(task.signer, a, task.txs[i]) } } } diff --git a/txpool/test_utils.go b/txpool/test_utils.go index 5eeb4956..63d99247 100644 --- a/txpool/test_utils.go +++ b/txpool/test_utils.go @@ -79,7 +79,8 @@ func transaction(nonce uint64, from, to common.Name, gaslimit uint64, key *ecdsa func pricedTransaction(nonce uint64, from, to common.Name, gaslimit uint64, gasprice *big.Int, key *ecdsa.PrivateKey) *types.Transaction { tx := newTx(gasprice, newAction(nonce, from, to, big.NewInt(100), gaslimit, nil)) - if err := types.SignAction(tx.GetActions()[0], tx, types.NewSigner(params.DefaultChainconfig.ChainID), key); err != nil { + keyPair := types.MakeKeyPair(key, []uint64{0}) + if err := types.SignActionWithMultiKey(tx.GetActions()[0], tx, types.NewSigner(params.DefaultChainconfig.ChainID), []*types.KeyPair{keyPair}); err != nil { panic(err) } return tx @@ -92,7 +93,7 @@ func generateAccount(t *testing.T, name common.Name, managers ...*am.AccountMana } pubkeyBytes := crypto.FromECDSAPub(&key.PublicKey) for _, m := range managers { - if err := m.CreateAccount(name, common.Name(""), 80, common.BytesToPubKey(pubkeyBytes)); err != nil { + if err := m.CreateAccount(name, common.Name(""), 0, 80, common.BytesToPubKey(pubkeyBytes)); err != nil { t.Fatal(err) } } @@ -103,7 +104,7 @@ func setupTxPool(assetOwner common.Name) (*TxPool, *am.AccountManager) { statedb, _ := state.New(common.Hash{}, state.NewDatabase(memdb.NewMemDatabase())) asset := asset.NewAsset(statedb) - asset.IssueAsset("ft", "zz", new(big.Int).SetUint64(params.Fractal), 10, assetOwner, assetOwner, big.NewInt(1000000)) + asset.IssueAsset("ft", 0, "zz", new(big.Int).SetUint64(params.Fractal), 10, assetOwner, assetOwner, big.NewInt(1000000), common.Name("")) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} manager, _ := am.NewAccountManager(statedb) return New(testTxPoolConfig, params.DefaultChainconfig, blockchain), manager @@ -173,18 +174,6 @@ func validateEvents(events chan *event.Event, count int) error { return nil } -func deriveSender(tx *types.Transaction) ([]common.PubKey, error) { - var pubkeys []common.PubKey - for _, a := range tx.GetActions() { - pubkey, err := types.Recover(types.NewSigner(params.DefaultChainconfig.ChainID), a, tx) - if err != nil { - return nil, err - } - pubkeys = append(pubkeys, pubkey) - } - return pubkeys, nil -} - type testChain struct { *testBlockChain diff --git a/txpool/txpool.go b/txpool/txpool.go index 0a04959e..451e413c 100644 --- a/txpool/txpool.go +++ b/txpool/txpool.go @@ -88,10 +88,12 @@ type TxPool struct { // New creates a new transaction pool to gather, sort and filter inbound // transactions from the network. func New(config Config, chainconfig *params.ChainConfig, bc blockChain) *TxPool { + // check the input to ensure no vulnerable gas prices are set + config = (&config).check() signer := types.NewSigner(chainconfig.ChainID) all := newTxLookup() tp := &TxPool{ - config: config.check(), + config: config, chain: bc, signer: signer, locals: newAccountSet(signer), @@ -190,7 +192,6 @@ func (tp *TxPool) loop() { } } tp.mu.Unlock() - // Handle local transaction journal rotation case <-journal.C: if tp.journal != nil { @@ -302,13 +303,11 @@ func (tp *TxPool) reset(oldHead, newHead *types.Header) { if err != am.ErrAccountIsDestroy { log.Error("Failed to pendingAccountManager SetNonce", "err", err) return - } else { - delete(tp.pending, name) - delete(tp.beats, name) - delete(tp.queue, name) - log.Debug("Remove all destory account ", "name", name) } - + delete(tp.pending, name) + delete(tp.beats, name) + delete(tp.queue, name) + log.Debug("Remove all destory account ", "name", name) } } // Check the queue and move transactions over to the pending if possible @@ -447,7 +446,7 @@ func (tp *TxPool) validateTx(tx *types.Transaction, local bool) error { } // Transactor should have enough funds to cover the gas costs - balance, err := tp.curAccountManager.GetAccountBalanceByID(from, tx.GasAssetID()) + balance, err := tp.curAccountManager.GetAccountBalanceByID(from, tx.GasAssetID(), 0) if err != nil { return err } @@ -458,7 +457,7 @@ func (tp *TxPool) validateTx(tx *types.Transaction, local bool) error { } // Transactor should have enough funds to cover the value costs - balance, err = tp.curAccountManager.GetAccountBalanceByID(from, action.AssetID()) + balance, err = tp.curAccountManager.GetAccountBalanceByID(from, action.AssetID(), 0) if err != nil { return err } @@ -472,6 +471,10 @@ func (tp *TxPool) validateTx(tx *types.Transaction, local bool) error { return ErrInsufficientFundsForValue } + if action.CheckValue() != true { + return ErrInvalidValue + } + intrGas, err := IntrinsicGas(action) if err != nil { return err @@ -641,7 +644,6 @@ func (tp *TxPool) promoteTx(name common.Name, hash common.Hash, tx *types.Transa // An older transaction was better, discard this tp.all.Remove(hash) tp.priced.Removed() - return false } // Otherwise discard any previous transaction and mark this @@ -657,7 +659,6 @@ func (tp *TxPool) promoteTx(name common.Name, hash common.Hash, tx *types.Transa } // Set the potentially new pending nonce and notify any subsystems of the new tx tp.beats[name] = time.Now() - // todo action tp.pendingAccountManager.SetNonce(name, tx.GetActions()[0].Nonce()+1) return true @@ -693,6 +694,18 @@ func (tp *TxPool) AddRemotes(txs []*types.Transaction) []error { // addTx enqueues a single transaction into the pool if it is valid. func (tp *TxPool) addTx(tx *types.Transaction, local bool) error { + if len(tx.GetActions()) == 0 { + return ErrEmptyActions + } + + // Cache senders in transactions before obtaining lock + for _, action := range tx.GetActions() { + _, err := types.RecoverMultiKey(tp.signer, action, tx) + if err != nil { + return err + } + } + tp.mu.Lock() defer tp.mu.Unlock() @@ -712,6 +725,29 @@ func (tp *TxPool) addTx(tx *types.Transaction, local bool) error { // addTxs attempts to queue a batch of transactions if they are valid. func (tp *TxPool) addTxs(txs []*types.Transaction, local bool) []error { + // Cache senders in transactions before obtaining lock + var ( + errs []error + isErr bool + ) + for _, tx := range txs { + if len(tx.GetActions()) == 0 { + errs = append(errs, fmt.Errorf(ErrEmptyActions.Error()+" hash %v", tx.Hash())) + isErr = true + continue + } + for _, action := range tx.GetActions() { + _, err := types.RecoverMultiKey(tp.signer, action, tx) + if err != nil { + log.Error("RecoverMultiKey reocver faild ", "err", err, "hash", tx.Hash()) + errs = append(errs, err) + isErr = true + } + } + } + if isErr { + return errs + } tp.mu.Lock() defer tp.mu.Unlock() @@ -855,7 +891,7 @@ func (tp *TxPool) promoteExecutables(accounts []common.Name) { } // Drop all transactions that are too costly (low balance or out of gas) // todo assetID - balance, err := tp.curAccountManager.GetAccountBalanceByID(addr, tp.config.GasAssetID) + balance, err := tp.curAccountManager.GetAccountBalanceByID(addr, tp.config.GasAssetID, 0) if err != nil { log.Error("promoteExecutables current account manager get balance err ", "name", addr, "assetID", tp.config.GasAssetID, "err", err) } @@ -1042,7 +1078,7 @@ func (tp *TxPool) demoteUnexecutables() { } // Drop all transactions that are too costly (low balance or out of gas), and queue any invalids back for later - gasBalance, err := tp.curAccountManager.GetAccountBalanceByID(addr, tp.config.GasAssetID) + gasBalance, err := tp.curAccountManager.GetAccountBalanceByID(addr, tp.config.GasAssetID, 0) if err != nil && err != am.ErrAccountNotExist { log.Error("promoteExecutables current account manager get balance err ", "name", addr, "assetID", tp.config.GasAssetID, "err", err) } diff --git a/txpool/txpool_test.go b/txpool/txpool_test.go index 3ebf9505..4d9c67e6 100644 --- a/txpool/txpool_test.go +++ b/txpool/txpool_test.go @@ -41,7 +41,6 @@ import ( // state reset and tests whether the pending state is in sync with the // block head event that initiated the resetState(). func TestStateChangeDuringTransactionPoolReset(t *testing.T) { - var ( statedb, _ = state.New(common.Hash{}, state.NewDatabase(mdb.NewMemDatabase())) manager, _ = am.NewAccountManager(statedb) @@ -54,7 +53,7 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) { ) // issue asset - if err := asset.IssueAsset("ft", "zz", new(big.Int).SetUint64(params.Fractal), 10, common.Name(""), fname, new(big.Int).SetUint64(params.Fractal)); err != nil { + if err := asset.IssueAsset("ft", 0, "zz", new(big.Int).SetUint64(params.Fractal), 10, common.Name(""), fname, new(big.Int).SetUint64(params.Fractal), common.Name("")); err != nil { t.Fatal(err) } @@ -216,27 +215,6 @@ func TestTransactionQueue(t *testing.T) { } } -func TestTransactionNegativeValue(t *testing.T) { - - var ( - fname = common.Name("fromname") - tname = common.Name("totestname") - ) - pool, manager := setupTxPool(fname) - defer pool.Stop() - fkey := generateAccount(t, fname, manager) - generateAccount(t, tname, manager) - - tx := newTx(big.NewInt(1), newAction(0, fname, tname, big.NewInt(-1), 100, nil)) - if err := types.SignAction(tx.GetActions()[0], tx, types.NewSigner(params.DefaultChainconfig.ChainID), fkey); err != nil { - panic(err) - } - - if err := pool.AddRemote(tx); err != ErrNegativeValue { - t.Fatal("expected", ErrNegativeValue, "got", err) - } -} - func TestTransactionChainFork(t *testing.T) { var ( @@ -253,15 +231,15 @@ func TestTransactionChainFork(t *testing.T) { statedb, _ := state.New(common.Hash{}, state.NewDatabase(mdb.NewMemDatabase())) newmanager, _ := am.NewAccountManager(statedb) - if err := newmanager.CreateAccount(fname, common.Name(""), 0, common.BytesToPubKey(crypto.FromECDSAPub(&fkey.PublicKey))); err != nil { + if err := newmanager.CreateAccount(fname, common.Name(""), 0, 0, common.BytesToPubKey(crypto.FromECDSAPub(&fkey.PublicKey))); err != nil { t.Fatal(err) } - if err := newmanager.CreateAccount(tname, common.Name(""), 0, common.BytesToPubKey(crypto.FromECDSAPub(&tkey.PublicKey))); err != nil { + if err := newmanager.CreateAccount(tname, common.Name(""), 0, 0, common.BytesToPubKey(crypto.FromECDSAPub(&tkey.PublicKey))); err != nil { t.Fatal(err) } asset := asset.NewAsset(statedb) - asset.IssueAsset("ft", "zz", new(big.Int).SetUint64(params.Fractal), 10, fname, fname, big.NewInt(1000000)) + asset.IssueAsset("ft", 0, "zz", new(big.Int).SetUint64(params.Fractal), 10, fname, fname, big.NewInt(1000000), common.Name("")) newmanager.AddAccountBalanceByID(fname, assetID, big.NewInt(100000000000000)) pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)} @@ -299,15 +277,15 @@ func TestTransactionDoubleNonce(t *testing.T) { statedb, _ := state.New(common.Hash{}, state.NewDatabase(mdb.NewMemDatabase())) newmanager, _ := am.NewAccountManager(statedb) - if err := newmanager.CreateAccount(fname, common.Name(""), 0, common.BytesToPubKey(crypto.FromECDSAPub(&fkey.PublicKey))); err != nil { + if err := newmanager.CreateAccount(fname, common.Name(""), 0, 0, common.BytesToPubKey(crypto.FromECDSAPub(&fkey.PublicKey))); err != nil { t.Fatal(err) } - if err := newmanager.CreateAccount(tname, common.Name(""), 0, common.BytesToPubKey(crypto.FromECDSAPub(&tkey.PublicKey))); err != nil { + if err := newmanager.CreateAccount(tname, common.Name(""), 0, 0, common.BytesToPubKey(crypto.FromECDSAPub(&tkey.PublicKey))); err != nil { t.Fatal(err) } asset := asset.NewAsset(statedb) - asset.IssueAsset("ft", "zz", new(big.Int).SetUint64(params.Fractal), 10, fname, fname, big.NewInt(1000000)) + asset.IssueAsset("ft", 0, "zz", new(big.Int).SetUint64(params.Fractal), 10, fname, fname, big.NewInt(1000000), common.Name("")) newmanager.AddAccountBalanceByID(fname, assetID, big.NewInt(100000000000000)) pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)} @@ -316,18 +294,19 @@ func TestTransactionDoubleNonce(t *testing.T) { } resetAsset() + keyPair := types.MakeKeyPair(fkey, []uint64{0}) tx1 := newTx(big.NewInt(1), newAction(0, fname, tname, big.NewInt(100), 100000, nil)) - if err := types.SignAction(tx1.GetActions()[0], tx1, types.NewSigner(params.DefaultChainconfig.ChainID), fkey); err != nil { + if err := types.SignActionWithMultiKey(tx1.GetActions()[0], tx1, types.NewSigner(params.DefaultChainconfig.ChainID), []*types.KeyPair{keyPair}); err != nil { panic(err) } tx2 := newTx(big.NewInt(2), newAction(0, fname, tname, big.NewInt(100), 1000000, nil)) - if err := types.SignAction(tx2.GetActions()[0], tx2, types.NewSigner(params.DefaultChainconfig.ChainID), fkey); err != nil { + if err := types.SignActionWithMultiKey(tx2.GetActions()[0], tx2, types.NewSigner(params.DefaultChainconfig.ChainID), []*types.KeyPair{keyPair}); err != nil { panic(err) } tx3 := newTx(big.NewInt(1), newAction(0, fname, tname, big.NewInt(100), 1000000, nil)) - if err := types.SignAction(tx3.GetActions()[0], tx3, types.NewSigner(params.DefaultChainconfig.ChainID), fkey); err != nil { + if err := types.SignActionWithMultiKey(tx3.GetActions()[0], tx3, types.NewSigner(params.DefaultChainconfig.ChainID), []*types.KeyPair{keyPair}); err != nil { panic(err) } @@ -1449,12 +1428,12 @@ func TestTransactionQueueLimitingEquivalency(t *testing.T) { testTransactionLi func TestTransactionPendingLimitingEquivalency(t *testing.T) { testTransactionLimitingEquivalency(t, 0) } func testTransactionLimitingEquivalency(t *testing.T, origin uint64) { - var ( fname = common.Name("fromname") tname = common.Name("totestname") assetID = uint64(1) ) + event.Reset() pool, manager := setupTxPool(fname) defer pool.Stop() fkey := generateAccount(t, fname, manager, pool.pendingAccountManager) @@ -1609,10 +1588,9 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) { blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} - config := testTxPoolConfig - config.GlobalSlots = 0 - - pool := New(config, params.DefaultChainconfig, blockchain) + event.Reset() + pool := New(testTxPoolConfig, params.DefaultChainconfig, blockchain) + pool.config.GlobalSlots = 0 defer pool.Stop() manager, _ := am.NewAccountManager(statedb) @@ -1637,7 +1615,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) { txs := []*types.Transaction{} for i, key := range keys { - for j := 0; j < int(config.AccountSlots)*2; j++ { + for j := 0; j < int(testTxPoolConfig.AccountSlots)*2; j++ { txs = append(txs, transaction(nonces[accs[i]], accs[i], tname, 100000, key)) nonces[accs[i]]++ } @@ -1645,9 +1623,9 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) { // Import the batch and verify that limits have been enforced pool.AddRemotes(txs) - for addr, list := range pool.pending { - if list.Len() != int(config.AccountSlots) { - t.Fatalf("addr %x: total pending transactions mismatch: have %d, want %d", addr, list.Len(), config.AccountSlots) + for name, list := range pool.pending { + if list.Len() != int(testTxPoolConfig.AccountSlots) { + t.Fatalf("addr %s: total pending transactions mismatch: have %d, want %d", name, list.Len(), testTxPoolConfig.AccountSlots) } } if err := validateTxPoolInternals(pool); err != nil { @@ -1661,7 +1639,6 @@ func TestTransactionJournaling(t *testing.T) { testTransactionJournaling func TestTransactionJournalingNoLocals(t *testing.T) { testTransactionJournaling(t, true) } func testTransactionJournaling(t *testing.T, nolocals bool) { - event.Reset() // Create a temporary file for the journal file, err := ioutil.TempFile("", "") if err != nil { @@ -1683,6 +1660,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) { config.Journal = journal config.Rejournal = time.Second + event.Reset() pool := New(config, params.DefaultChainconfig, blockchain) var ( @@ -1757,6 +1735,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) { manager.SetNonce(localName, 1) blockchain = &testBlockChain{statedb, 1000000, new(event.Feed)} + event.Reset() pool = New(config, params.DefaultChainconfig, blockchain) pending, queued = pool.Stats() @@ -1785,7 +1764,7 @@ func TestTransactionStatusCheck(t *testing.T) { // Create the pool to test the status retrievals with statedb, _ := state.New(common.Hash{}, state.NewDatabase(mdb.NewMemDatabase())) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} - + event.Reset() pool := New(testTxPoolConfig, params.DefaultChainconfig, blockchain) defer pool.Stop() @@ -1920,7 +1899,7 @@ func BenchmarkPoolInsert(b *testing.B) { fkey := generateAccount(nil, fname, manager) generateAccount(nil, tname, manager) - pool.curAccountManager.AddAccountBalanceByID(fname, assetID, big.NewInt(1000000)) + pool.curAccountManager.AddAccountBalanceByID(fname, assetID, big.NewInt(100000)) txs := make([]*types.Transaction, b.N) for i := 0; i < b.N; i++ { diff --git a/txpool/utils.go b/txpool/utils.go index 2f6bdb42..43312ba4 100644 --- a/txpool/utils.go +++ b/txpool/utils.go @@ -65,5 +65,9 @@ func IntrinsicGas(action *types.Action) (uint64, error) { } gas += dataGas + if signLen := len(action.GetSign()); signLen > 1 { + gas += (uint64(len(action.GetSign()) - 1)) * params.ActionGas + } + return gas, nil } diff --git a/cmd/ftkey/ftkey.go b/types/accountmanage.go similarity index 90% rename from cmd/ftkey/ftkey.go rename to types/accountmanage.go index d011754c..e1535bf8 100644 --- a/cmd/ftkey/ftkey.go +++ b/types/accountmanage.go @@ -14,8 +14,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package main +package types -func main() { - Execute() +type AccountManagerContext struct { + Action *Action + Number uint64 } diff --git a/types/action.go b/types/action.go index 1f3d7b35..abfa7251 100644 --- a/types/action.go +++ b/types/action.go @@ -34,19 +34,22 @@ var ErrInvalidSig = errors.New("invalid action v, r, s values") type ActionType uint64 const ( - // Transfer represents the ordinary and contract transfer action. - Transfer ActionType = iota + + // CallContract represents the call contract action. + CallContract ActionType = iota // CreateContract repesents the create contract action. CreateContract ) const ( - // CreateAccount repesents the create account. + //CreateAccount repesents the create account. CreateAccount ActionType = 0x100 + iota - // UpdateAccount repesents the update account action. + //UpdateAccount repesents update account. UpdateAccount // DeleteAccount repesents the delete account action. DeleteAccount + //UpdateAccountAuthor represents the update account author. + UpdateAccountAuthor ) const ( @@ -54,31 +57,48 @@ const ( IncreaseAsset ActionType = 0x200 + iota // IssueAsset repesents Issue asset action. IssueAsset - //destory asset - DestoryAsset + //DestroyAsset destroy asset + DestroyAsset // SetAssetOwner repesents set asset new owner action. SetAssetOwner - //set asset founder - SetAssetFounder + //SetAssetFounder set asset founder + //SetAssetFounder + UpdateAsset + //Transfer repesents transfer asset action. + Transfer ) const ( - // RegProducer repesents register producer action. - RegProducer ActionType = 0x300 + iota - // UpdateProducer repesents update producer action. - UpdateProducer - // UnregProducer repesents unregister producer action. - UnregProducer - // RemoveVoter repesents producer remove voter action. + // RegCadidate repesents register cadidate action. + RegCadidate ActionType = 0x300 + iota + // UpdateCadidate repesents update cadidate action. + UpdateCadidate + // UnregCadidate repesents unregister cadidate action. + UnregCadidate + // RemoveVoter repesents cadidate remove voter action. RemoveVoter - // VoteProducer repesents voter vote producer action. - VoteProducer - // ChangeProducer repesents voter change producer action. - ChangeProducer - // UnvoteProducer repesents voter cancel vote some producer action. - UnvoteProducer + // VoteCadidate repesents voter vote cadidate action. + VoteCadidate + // ChangeCadidate repesents voter change cadidate action. + ChangeCadidate + // UnvoteCadidate repesents voter cancel vote some cadidate action. + UnvoteCadidate +) + +const ( + // KickedCadidate + KickedCadidate ActionType = 0x400 + iota + // exit + ExitTakeOver ) +type SignData struct { + V *big.Int + R *big.Int + S *big.Int + Index []uint64 +} + type actionData struct { AType ActionType Nonce uint64 @@ -89,10 +109,7 @@ type actionData struct { Amount *big.Int Payload []byte - // Signature values - V *big.Int - R *big.Int - S *big.Int + Sign []*SignData } // Action represents an entire action in the transaction. @@ -101,6 +118,7 @@ type Action struct { // cache hash atomic.Value sender atomic.Value + author atomic.Value } // NewAction initialize transaction's action. @@ -117,9 +135,7 @@ func NewAction(actionType ActionType, from, to common.Name, nonce, assetID, gasL GasLimit: gasLimit, Amount: new(big.Int), Payload: payload, - V: new(big.Int), - R: new(big.Int), - S: new(big.Int), + Sign: make([]*SignData, 0), } if amount != nil { data.Amount.Set(amount) @@ -127,6 +143,38 @@ func NewAction(actionType ActionType, from, to common.Name, nonce, assetID, gasL return &Action{data: data} } +func (a *Action) GetSignIndex(i uint64) []uint64 { + return a.data.Sign[i].Index +} + +func (a *Action) GetSign() []*SignData { + return a.data.Sign +} + +//CheckValue check action type and value +func (a *Action) CheckValue() bool { + switch a.Type() { + case CreateContract: + fallthrough + case CallContract: + fallthrough + case Transfer: + fallthrough + case CreateAccount: + fallthrough + case DestroyAsset: + fallthrough + case RegCadidate: + fallthrough + case UpdateCadidate: + fallthrough + case VoteCadidate: + return true + default: + } + return a.Value().Cmp(big.NewInt(0)) == 0 +} + // Type returns action's type. func (a *Action) Type() ActionType { return a.data.AType } @@ -163,7 +211,7 @@ func (a *Action) DecodeRLP(s *rlp.Stream) error { // ChainID returns which chain id this action was signed for (if at all) func (a *Action) ChainID() *big.Int { - return deriveChainID(a.data.V) + return deriveChainID(a.data.Sign[0].V) } // Hash hashes the RLP encoding of action. @@ -177,20 +225,15 @@ func (a *Action) Hash() common.Hash { } // WithSignature returns a new transaction with the given signature. -func (a *Action) WithSignature(signer Signer, sig []byte) error { +func (a *Action) WithSignature(signer Signer, sig []byte, index []uint64) error { r, s, v, err := signer.SignatureValues(sig) if err != nil { return err } - a.data.R, a.data.S, a.data.V = r, s, v + a.data.Sign = append(a.data.Sign, &SignData{R: r, S: s, V: v, Index: index}) return nil } -// RawSignatureValues return raw signature values. -func (a *Action) RawSignatureValues() (*big.Int, *big.Int, *big.Int) { - return a.data.V, a.data.R, a.data.S -} - // RPCAction represents a action that will serialize to the RPC representation of a action. type RPCAction struct { Type uint64 `json:"type"` @@ -201,16 +244,12 @@ type RPCAction struct { GasLimit uint64 `json:"gas"` Amount *big.Int `json:"value"` Payload hexutil.Bytes `json:"payload"` - V *hexutil.Big `json:"v"` - R *hexutil.Big `json:"r"` - S *hexutil.Big `json:"s"` Hash common.Hash `json:"actionHash"` ActionIdex uint64 `json:"actionIndex"` } // NewRPCAction returns a action that will serialize to the RPC. func (a *Action) NewRPCAction(index uint64) *RPCAction { - v, r, s := a.RawSignatureValues() return &RPCAction{ Type: uint64(a.Type()), Nonce: a.Nonce(), @@ -221,9 +260,6 @@ func (a *Action) NewRPCAction(index uint64) *RPCAction { Amount: a.Value(), Payload: hexutil.Bytes(a.Data()), Hash: a.Hash(), - V: (*hexutil.Big)(v), - R: (*hexutil.Big)(r), - S: (*hexutil.Big)(s), ActionIdex: index, } } diff --git a/types/action_test.go b/types/action_test.go index af161624..f932109f 100644 --- a/types/action_test.go +++ b/types/action_test.go @@ -37,6 +37,28 @@ var ( big.NewInt(1000), []byte("test action"), ) + + testAction2 = NewAction( + UpdateAccount, + common.Name("fromname"), + common.Name("totoname"), + uint64(1), + uint64(3), + uint64(2000), + big.NewInt(1000), + []byte("test action"), + ) + + testAction3 = NewAction( + UpdateAccount, + common.Name("fromname"), + common.Name("totoname"), + uint64(1), + uint64(3), + uint64(2000), + big.NewInt(0), + []byte("test action"), + ) ) func TestActionEncodeAndDecode(t *testing.T) { @@ -50,5 +72,53 @@ func TestActionEncodeAndDecode(t *testing.T) { t.Fatal(err) } + t.Log(rlpHash(actAction).Hex()) + assert.Equal(t, testAction, actAction) } + +func TestAction_CheckValue(t *testing.T) { + actionBytes, err := rlp.EncodeToBytes(testAction) + if err != nil { + t.Fatal(err) + } + + actAction := &Action{} + if err := rlp.Decode(bytes.NewReader(actionBytes), &actAction); err != nil { + t.Fatal(err) + } + + if actAction.CheckValue() == false { + t.Errorf("TestAction_CheckValue err, wantErr %v", true) + } + + //test2 + actionBytes2, err := rlp.EncodeToBytes(testAction2) + if err != nil { + t.Fatal(err) + } + + actAction2 := &Action{} + if err := rlp.Decode(bytes.NewReader(actionBytes2), &actAction2); err != nil { + t.Fatal(err) + } + + if actAction2.CheckValue() == true { + t.Errorf("TestAction2_CheckValue err, wantErr %v", false) + } + + //test3 + actionBytes3, err := rlp.EncodeToBytes(testAction3) + if err != nil { + t.Fatal(err) + } + + actAction3 := &Action{} + if err := rlp.Decode(bytes.NewReader(actionBytes3), &actAction3); err != nil { + t.Fatal(err) + } + + if actAction3.CheckValue() == false { + t.Errorf("TestAction3_CheckValue err, wantErr %v", false) + } +} diff --git a/types/block.go b/types/block.go index 789a68a0..d4e323ff 100644 --- a/types/block.go +++ b/types/block.go @@ -20,6 +20,7 @@ package types import ( "bytes" "fmt" + "io" "math/big" "sort" "sync/atomic" @@ -37,24 +38,20 @@ type ForkID struct { // Header represents a block header in the blockchain. type Header struct { - ParentHash common.Hash `json:"parentHash"` - Coinbase common.Name `json:"miner"` - Root common.Hash `json:"stateRoot"` - TxsRoot common.Hash `json:"transactionsRoot"` - ReceiptsRoot common.Hash `json:"receiptsRoot"` - Bloom Bloom `json:"logsBloom"` - Difficulty *big.Int `json:"difficulty"` - Number *big.Int `json:"number"` - GasLimit uint64 `json:"gasLimit"` - GasUsed uint64 `json:"gasUsed"` - Time *big.Int `json:"timestamp"` - Extra []byte `json:"extraData"` - - // cache - forkID atomic.Value - - // additional fields (for forward compatibility). - AdditionalFields []rlp.RawValue `rlp:"tail"` + ParentHash common.Hash `json:"parentHash"` + Coinbase common.Name `json:"miner"` + ProposedIrreversible uint64 `json:"proposedIrreversible"` + Root common.Hash `json:"stateRoot"` + TxsRoot common.Hash `json:"transactionsRoot"` + ReceiptsRoot common.Hash `json:"receiptsRoot"` + Bloom Bloom `json:"logsBloom"` + Difficulty *big.Int `json:"difficulty"` + Number *big.Int `json:"number"` + GasLimit uint64 `json:"gasLimit"` + GasUsed uint64 `json:"gasUsed"` + Time *big.Int `json:"timestamp"` + Extra []byte `json:"extraData"` + ForkID ForkID `json:"forkID"` } // Hash returns the block hash of the header, which is simply the keccak256 hash of its @@ -63,29 +60,14 @@ func (h *Header) Hash() common.Hash { return rlpHash(h) } // WithForkID store fork id func (h *Header) WithForkID(cur, next uint64) { - forkID := ForkID{Cur: cur, Next: next} - bytes, _ := rlp.EncodeToBytes(forkID) - h.AdditionalFields = append(h.AdditionalFields, rlp.RawValue(bytes)) - h.forkID.Store(forkID) + h.ForkID = ForkID{Cur: cur, Next: next} } // CurForkID returns the header's current fork ID. -func (h *Header) CurForkID() uint64 { return h.getForkID().Cur } +func (h *Header) CurForkID() uint64 { return h.ForkID.Cur } // NextForkID returns the header's next fork ID. -func (h *Header) NextForkID() uint64 { return h.getForkID().Next } - -func (h *Header) getForkID() ForkID { - if forkID := h.forkID.Load(); forkID != nil { - return forkID.(ForkID) - } - forkID := ForkID{} - if len(h.AdditionalFields) > 0 { - rlp.DecodeBytes(h.AdditionalFields[0], &forkID) - h.forkID.Store(forkID) - } - return forkID -} +func (h *Header) NextForkID() uint64 { return h.ForkID.Next } // Block represents an entire block in the blockchain. type Block struct { @@ -97,6 +79,12 @@ type Block struct { size atomic.Value } +// "external" block encoding. used protocol, etc. +type extblock struct { + Header *Header + Txs []*Transaction +} + // NewBlock creates a new block. The input data is copied, // changes to header and to the field values will not affect the // block. @@ -191,6 +179,14 @@ func (b *Block) EncodeRLP() ([]byte, error) { return rlp.EncodeToBytes(b) } +// EncodeRLP serializes b into RLP block format. +func (b *Block) ExtEncodeRLP(w io.Writer) error { + return rlp.Encode(w, extblock{ + Header: b.Head, + Txs: b.Txs, + }) +} + // DecodeRLP decodes the block func (b *Block) DecodeRLP(input []byte) error { err := rlp.Decode(bytes.NewReader(input), &b) @@ -303,7 +299,10 @@ func DeriveReceiptsMerkleRoot(receipts []*Receipt) common.Hash { func rlpHash(x interface{}) (h common.Hash) { hw := sha3.NewLegacyKeccak256() - rlp.Encode(hw, x) + err := rlp.Encode(hw, x) + if err != nil { + panic(fmt.Sprintf("rlp hash encode err: %v", err)) + } hw.Sum(h[:0]) return h } diff --git a/types/block_test.go b/types/block_test.go index d8baf514..472b7e66 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -68,7 +68,6 @@ func TestBlockForkID(t *testing.T) { newBlock := &Block{} assert.NoError(t, newBlock.DecodeRLP(bytes)) - assert.Equal(t, testBlock.CurForkID(), newBlock.CurForkID()) assert.Equal(t, testBlock.NextForkID(), newBlock.NextForkID()) } diff --git a/types/internal.go b/types/internal.go new file mode 100644 index 00000000..363ec9e3 --- /dev/null +++ b/types/internal.go @@ -0,0 +1,43 @@ +// Copyright 2018 The Fractal Team Authors +// This file is part of the fractal project. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package types + +import "github.com/fractalplatform/fractal/common" + +type DetailTx struct { + TxHash common.Hash `json:"txhash"` + Actions []*DetailAction `json:"actions"` +} + +type DetailAction struct { + InternalActions []*InternalAction `json:"internalActions"` +} + +type InternalAction struct { + Action *RPCAction `json:"action"` + ActionType string `json:"actionType"` + GasUsed uint64 `json:"gasUsed"` + GasLimit uint64 `json:"gasLimit"` + Depth uint64 `json:"depth"` + Error string `json:"error"` +} + +type BlockAndResult struct { + Block map[string]interface{} `json:"block"` + Receipts []*Receipt `json:"receipts"` + DetailTxs []*DetailTx `json:"detailTxs"` +} diff --git a/types/optinfo.go b/types/optinfo.go index 234e7e32..87d554cf 100644 --- a/types/optinfo.go +++ b/types/optinfo.go @@ -49,3 +49,9 @@ type SnapshotMsg struct { Number uint64 Root common.Hash } + +type AccountInfo struct { + Name string + Key string + Value []byte +} diff --git a/types/receipt.go b/types/receipt.go index ed8cf86d..20780c99 100644 --- a/types/receipt.go +++ b/types/receipt.go @@ -31,20 +31,28 @@ const ( ) // ActionResult represents the results the transaction action. +type GasDistribution struct { + Account string `json:"name"` + Gas uint64 `json:"gas"` + TypeID uint64 `json:"typeId"` +} + type ActionResult struct { - Status uint64 - Index uint64 - GasUsed uint64 - Error string + Status uint64 + Index uint64 + GasUsed uint64 + GasAllot []*GasDistribution + Error string } // RPCActionResult that will serialize to the RPC representation of a ActionResult. type RPCActionResult struct { - ActionType uint64 `json:"actionType"` - Status uint64 `json:"status"` - Index uint64 `json:"index"` - GasUsed uint64 `json:"gasUsed"` - Error string `json:"error"` + ActionType uint64 `json:"actionType"` + Status uint64 `json:"status"` + Index uint64 `json:"index"` + GasUsed uint64 `json:"gasUsed"` + GasAllot []*GasDistribution `json:"gasAllot"` + Error string `json:"error"` } // NewRPCActionResult returns a ActionResult that will serialize to the RPC. @@ -54,6 +62,7 @@ func (a *ActionResult) NewRPCActionResult(aType ActionType) *RPCActionResult { Status: a.Status, Index: a.Index, GasUsed: a.GasUsed, + GasAllot: a.GasAllot, Error: a.Error, } } @@ -67,6 +76,7 @@ type Receipt struct { Logs []*Log TxHash common.Hash TotalGasUsed uint64 + internalTxsLog *DetailTx } // NewReceipt creates a barebone transaction receipt, copying the init fields. @@ -162,3 +172,11 @@ func (r *Receipt) ConsensusReceipt() *Receipt { result.Logs = logs return result } + +func (r *Receipt) GetInternalTxsLog() *DetailTx { + return r.internalTxsLog +} + +func (r *Receipt) SetInternalTxsLog(dtxs *DetailTx) { + r.internalTxsLog = dtxs +} diff --git a/types/receipt_test.go b/types/receipt_test.go index 41ebad63..d1e20512 100644 --- a/types/receipt_test.go +++ b/types/receipt_test.go @@ -27,7 +27,8 @@ import ( func TestReceiptEncodeAndDecode(t *testing.T) { testR := NewReceipt([]byte("root"), 1000, 1000) testR.Logs = make([]*Log, 0) - testR.ActionResults = append(testR.ActionResults, &ActionResult{Status: ReceiptStatusFailed, Index: uint64(0), GasUsed: uint64(100)}) + gasAllot := make([]*GasDistribution, 0) + testR.ActionResults = append(testR.ActionResults, &ActionResult{Status: ReceiptStatusFailed, Index: uint64(0), GasAllot: gasAllot, GasUsed: uint64(100)}) bytes, err := rlp.EncodeToBytes(testR) if err != nil { t.Fatal(err) diff --git a/types/signer.go b/types/signer.go index 6e7a8be0..a928f278 100644 --- a/types/signer.go +++ b/types/signer.go @@ -31,12 +31,23 @@ var ( ErrInvalidchainID = errors.New("invalid chain id for signer") //ErrSigUnprotected signature is considered unprotected ErrSigUnprotected = errors.New("signature is considered unprotected") + //ErrSignEmpty signature is considered unprotected + ErrSignEmpty = errors.New("signature is nil") ) // sigCache is used to cache the derived sender and contains the signer used to derive it. type sigCache struct { - signer Signer - pubKey []byte + signer Signer + pubKeys []common.PubKey +} + +type KeyPair struct { + priv *ecdsa.PrivateKey + index []uint64 +} + +func MakeKeyPair(priv *ecdsa.PrivateKey, index []uint64) *KeyPair { + return &KeyPair{priv, index} } // MakeSigner returns a Signer based on the given chainID . @@ -44,35 +55,51 @@ func MakeSigner(chainID *big.Int) Signer { return NewSigner(chainID) } -// SignAction signs the action using the given signer and private key -func SignAction(a *Action, tx *Transaction, s Signer, prv *ecdsa.PrivateKey) error { +func SignActionWithMultiKey(a *Action, tx *Transaction, s Signer, keys []*KeyPair) error { h := s.Hash(tx) - sig, err := crypto.Sign(h[:], prv) - if err != nil { - return err + for _, key := range keys { + sig, err := crypto.Sign(h[:], key.priv) + if err != nil { + return err + } + + err = a.WithSignature(s, sig, key.index) + if err != nil { + return err + } } - return a.WithSignature(s, sig) + return nil } -// Recover returns the pubkey derived from the signature (V, R, S) using secp256k1 -// elliptic curve and an error if it failed deriving or upon an incorrect -// signature. -func Recover(signer Signer, a *Action, tx *Transaction) (common.PubKey, error) { +func RecoverMultiKey(signer Signer, a *Action, tx *Transaction) ([]common.PubKey, error) { if sc := a.sender.Load(); sc != nil { sigCache := sc.(sigCache) if sigCache.signer.Equal(signer) { - pk := new(common.PubKey) - pk.SetBytes(sigCache.pubKey) - return *pk, nil + return sigCache.pubKeys, nil } } - pubKey, err := signer.PubKey(a, tx) + pubKeys, err := signer.PubKeys(a, tx) if err != nil { - return common.PubKey{}, err + return []common.PubKey{}, err } - a.sender.Store(sigCache{signer: signer, pubKey: pubKey}) - return common.BytesToPubKey(pubKey), nil + a.sender.Store(sigCache{signer: signer, pubKeys: pubKeys}) + return pubKeys, nil +} + +func StoreAuthorCache(a *Action, authorVersion map[common.Name]uint64) { + a.author.Store(authorVersion) +} + +func GetAuthorCache(a *Action) map[common.Name]uint64 { + authorVersion := make(map[common.Name]uint64, 0) + if ac := a.author.Load(); ac != nil { + aCache := ac.(map[common.Name]uint64) + for name, version := range aCache { + authorVersion[name] = version + } + } + return authorVersion } // Signer implements Signer . @@ -98,14 +125,25 @@ func (s Signer) Equal(s2 Signer) bool { var big8 = big.NewInt(8) -// PubKey return Action sender -func (s Signer) PubKey(a *Action, tx *Transaction) ([]byte, error) { +func (s Signer) PubKeys(a *Action, tx *Transaction) ([]common.PubKey, error) { + if len(a.GetSign()) == 0 { + return nil, ErrSignEmpty + } if a.ChainID().Cmp(s.chainID) != 0 { return nil, ErrInvalidchainID } - V := new(big.Int).Sub(a.data.V, s.chainIDMul) - V.Sub(V, big8) - return recoverPlain(s.Hash(tx), a.data.R, a.data.S, V, true) + var pubKeys []common.PubKey + for _, sign := range a.data.Sign { + V := new(big.Int).Sub(sign.V, s.chainIDMul) + V.Sub(V, big8) + data, err := recoverPlain(s.Hash(tx), sign.R, sign.S, V, true) + if err != nil { + return nil, err + } + pubKey := common.BytesToPubKey(data) + pubKeys = append(pubKeys, pubKey) + } + return pubKeys, nil } // SignatureValues returns a new transaction with the given signature. This signature @@ -128,7 +166,7 @@ func (s Signer) SignatureValues(sig []byte) (R, S, V *big.Int, err error) { // Hash returns the hash to be signed by the sender. func (s Signer) Hash(tx *Transaction) common.Hash { actionHashs := make([]common.Hash, len(tx.GetActions())) - for _, a := range tx.GetActions() { + for i, a := range tx.GetActions() { hash := rlpHash([]interface{}{ a.data.From, a.data.AType, @@ -139,7 +177,7 @@ func (s Signer) Hash(tx *Transaction) common.Hash { a.data.Payload, s.chainID, uint(0), uint(0), }) - actionHashs = append(actionHashs, hash) + actionHashs[i] = hash } return rlpHash([]interface{}{ diff --git a/types/signer_test.go b/types/signer_test.go index c96ca9dd..9e4acf93 100644 --- a/types/signer_test.go +++ b/types/signer_test.go @@ -17,28 +17,66 @@ package types import ( - "bytes" "math/big" "testing" + "github.com/fractalplatform/fractal/common" "github.com/fractalplatform/fractal/crypto" ) -func TestSigning(t *testing.T) { - key, _ := crypto.GenerateKey() - exp := crypto.FromECDSAPub(&key.PublicKey) - signer := NewSigner(big.NewInt(18)) - if err := SignAction(testTx.GetActions()[0], testTx, signer, key); err != nil { +// func TestSigning(t *testing.T) { +// key, _ := crypto.GenerateKey() +// exp := crypto.FromECDSAPub(&key.PublicKey) +// signer := NewSigner(big.NewInt(18)) +// if err := SignAction(testTx.GetActions()[0], testTx, signer, key); err != nil { +// t.Fatal(err) +// } + +// pubkey, err := Recover(signer, testTx.GetActions()[0], testTx) +// if err != nil { +// t.Fatal(err) +// } + +// if bytes.Compare(pubkey.Bytes(), exp) != 0 { +// t.Errorf("exected from and address to be equal. Got %x want %x", pubkey, exp) +// } +// } + +func TestSigningMultiKey(t *testing.T) { + keys := make([]*KeyPair, 0) + pubs := make([]common.PubKey, 0) + for i := 0; i < 4; i++ { + key, _ := crypto.GenerateKey() + exp := crypto.FromECDSAPub(&key.PublicKey) + keys = append(keys, &KeyPair{priv: key, index: []uint64{uint64(i)}}) + pubs = append(pubs, common.BytesToPubKey(exp)) + } + signer := NewSigner(big.NewInt(1)) + if err := SignActionWithMultiKey(testTx.GetActions()[0], testTx, signer, keys); err != nil { + t.Fatal(err) + } + + pubkeys, err := RecoverMultiKey(signer, testTx.GetActions()[0], testTx) + if err != nil { t.Fatal(err) } - pubkey, err := Recover(signer, testTx.GetActions()[0], testTx) + for i, pubkey := range pubkeys { + if pubkey.Compare(pubs[i]) != 0 { + t.Errorf("exected from and address to be equal. Got %x want %x", pubkey, pubs[i]) + } + } + + //test cache + pubkeys, err = RecoverMultiKey(signer, testTx.GetActions()[0], testTx) if err != nil { t.Fatal(err) } - if bytes.Compare(pubkey.Bytes(), exp) != 0 { - t.Errorf("exected from and address to be equal. Got %x want %x", pubkey, exp) + for i, pubkey := range pubkeys { + if pubkey.Compare(pubs[i]) != 0 { + t.Errorf("exected from and address to be equal. Got %x want %x", pubkey, pubs[i]) + } } } @@ -46,7 +84,8 @@ func TestChainID(t *testing.T) { key, _ := crypto.GenerateKey() signer := NewSigner(big.NewInt(1)) - if err := SignAction(testAction, testTx, signer, key); err != nil { + keyPair := MakeKeyPair(key, []uint64{0}) + if err := SignActionWithMultiKey(testAction, testTx, signer, []*KeyPair{keyPair}); err != nil { t.Fatal(err) } @@ -54,3 +93,17 @@ func TestChainID(t *testing.T) { t.Error("expected chainId to be", signer.chainID, "got", testTx.GetActions()[0].ChainID()) } } + +func TestAuthorCache(t *testing.T) { + authorVersion := make(map[common.Name]uint64, 0) + authorVersion[common.Name("fromname")] = 1 + authorVersion[common.Name("toname")] = 10 + StoreAuthorCache(testAction, authorVersion) + + loadAuthorVeriosn := GetAuthorCache(testAction) + for name, version := range loadAuthorVeriosn { + if authorVersion[name] != version { + t.Error("expected version to be", authorVersion[name], "got", version) + } + } +} diff --git a/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go b/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go deleted file mode 100644 index 593f6530..00000000 --- a/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* -Package pbkdf2 implements the key derivation function PBKDF2 as defined in RFC -2898 / PKCS #5 v2.0. - -A key derivation function is useful when encrypting data based on a password -or any other not-fully-random data. It uses a pseudorandom function to derive -a secure encryption key based on the password. - -While v2.0 of the standard defines only one pseudorandom function to use, -HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved -Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To -choose, you can pass the `New` functions from the different SHA packages to -pbkdf2.Key. -*/ -package pbkdf2 // import "golang.org/x/crypto/pbkdf2" - -import ( - "crypto/hmac" - "hash" -) - -// Key derives a key from the password, salt and iteration count, returning a -// []byte of length keylen that can be used as cryptographic key. The key is -// derived based on the method described as PBKDF2 with the HMAC variant using -// the supplied hash function. -// -// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you -// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by -// doing: -// -// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New) -// -// Remember to get a good random salt. At least 8 bytes is recommended by the -// RFC. -// -// Using a higher iteration count will increase the cost of an exhaustive -// search but will also make derivation proportionally slower. -func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte { - prf := hmac.New(h, password) - hashLen := prf.Size() - numBlocks := (keyLen + hashLen - 1) / hashLen - - var buf [4]byte - dk := make([]byte, 0, numBlocks*hashLen) - U := make([]byte, hashLen) - for block := 1; block <= numBlocks; block++ { - // N.B.: || means concatenation, ^ means XOR - // for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter - // U_1 = PRF(password, salt || uint(i)) - prf.Reset() - prf.Write(salt) - buf[0] = byte(block >> 24) - buf[1] = byte(block >> 16) - buf[2] = byte(block >> 8) - buf[3] = byte(block) - prf.Write(buf[:4]) - dk = prf.Sum(dk) - T := dk[len(dk)-hashLen:] - copy(U, T) - - // U_n = PRF(password, U_(n-1)) - for n := 2; n <= iter; n++ { - prf.Reset() - prf.Write(U) - U = U[:0] - U = prf.Sum(U) - for x := range U { - T[x] ^= U[x] - } - } - } - return dk[:keyLen] -} diff --git a/vendor/golang.org/x/crypto/scrypt/scrypt.go b/vendor/golang.org/x/crypto/scrypt/scrypt.go deleted file mode 100644 index 3362afd1..00000000 --- a/vendor/golang.org/x/crypto/scrypt/scrypt.go +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package scrypt implements the scrypt key derivation function as defined in -// Colin Percival's paper "Stronger Key Derivation via Sequential Memory-Hard -// Functions" (https://www.tarsnap.com/scrypt/scrypt.pdf). -package scrypt // import "golang.org/x/crypto/scrypt" - -import ( - "crypto/sha256" - "errors" - - "golang.org/x/crypto/pbkdf2" -) - -const maxInt = int(^uint(0) >> 1) - -// blockCopy copies n numbers from src into dst. -func blockCopy(dst, src []uint32, n int) { - copy(dst, src[:n]) -} - -// blockXOR XORs numbers from dst with n numbers from src. -func blockXOR(dst, src []uint32, n int) { - for i, v := range src[:n] { - dst[i] ^= v - } -} - -// salsaXOR applies Salsa20/8 to the XOR of 16 numbers from tmp and in, -// and puts the result into both tmp and out. -func salsaXOR(tmp *[16]uint32, in, out []uint32) { - w0 := tmp[0] ^ in[0] - w1 := tmp[1] ^ in[1] - w2 := tmp[2] ^ in[2] - w3 := tmp[3] ^ in[3] - w4 := tmp[4] ^ in[4] - w5 := tmp[5] ^ in[5] - w6 := tmp[6] ^ in[6] - w7 := tmp[7] ^ in[7] - w8 := tmp[8] ^ in[8] - w9 := tmp[9] ^ in[9] - w10 := tmp[10] ^ in[10] - w11 := tmp[11] ^ in[11] - w12 := tmp[12] ^ in[12] - w13 := tmp[13] ^ in[13] - w14 := tmp[14] ^ in[14] - w15 := tmp[15] ^ in[15] - - x0, x1, x2, x3, x4, x5, x6, x7, x8 := w0, w1, w2, w3, w4, w5, w6, w7, w8 - x9, x10, x11, x12, x13, x14, x15 := w9, w10, w11, w12, w13, w14, w15 - - for i := 0; i < 8; i += 2 { - u := x0 + x12 - x4 ^= u<<7 | u>>(32-7) - u = x4 + x0 - x8 ^= u<<9 | u>>(32-9) - u = x8 + x4 - x12 ^= u<<13 | u>>(32-13) - u = x12 + x8 - x0 ^= u<<18 | u>>(32-18) - - u = x5 + x1 - x9 ^= u<<7 | u>>(32-7) - u = x9 + x5 - x13 ^= u<<9 | u>>(32-9) - u = x13 + x9 - x1 ^= u<<13 | u>>(32-13) - u = x1 + x13 - x5 ^= u<<18 | u>>(32-18) - - u = x10 + x6 - x14 ^= u<<7 | u>>(32-7) - u = x14 + x10 - x2 ^= u<<9 | u>>(32-9) - u = x2 + x14 - x6 ^= u<<13 | u>>(32-13) - u = x6 + x2 - x10 ^= u<<18 | u>>(32-18) - - u = x15 + x11 - x3 ^= u<<7 | u>>(32-7) - u = x3 + x15 - x7 ^= u<<9 | u>>(32-9) - u = x7 + x3 - x11 ^= u<<13 | u>>(32-13) - u = x11 + x7 - x15 ^= u<<18 | u>>(32-18) - - u = x0 + x3 - x1 ^= u<<7 | u>>(32-7) - u = x1 + x0 - x2 ^= u<<9 | u>>(32-9) - u = x2 + x1 - x3 ^= u<<13 | u>>(32-13) - u = x3 + x2 - x0 ^= u<<18 | u>>(32-18) - - u = x5 + x4 - x6 ^= u<<7 | u>>(32-7) - u = x6 + x5 - x7 ^= u<<9 | u>>(32-9) - u = x7 + x6 - x4 ^= u<<13 | u>>(32-13) - u = x4 + x7 - x5 ^= u<<18 | u>>(32-18) - - u = x10 + x9 - x11 ^= u<<7 | u>>(32-7) - u = x11 + x10 - x8 ^= u<<9 | u>>(32-9) - u = x8 + x11 - x9 ^= u<<13 | u>>(32-13) - u = x9 + x8 - x10 ^= u<<18 | u>>(32-18) - - u = x15 + x14 - x12 ^= u<<7 | u>>(32-7) - u = x12 + x15 - x13 ^= u<<9 | u>>(32-9) - u = x13 + x12 - x14 ^= u<<13 | u>>(32-13) - u = x14 + x13 - x15 ^= u<<18 | u>>(32-18) - } - x0 += w0 - x1 += w1 - x2 += w2 - x3 += w3 - x4 += w4 - x5 += w5 - x6 += w6 - x7 += w7 - x8 += w8 - x9 += w9 - x10 += w10 - x11 += w11 - x12 += w12 - x13 += w13 - x14 += w14 - x15 += w15 - - out[0], tmp[0] = x0, x0 - out[1], tmp[1] = x1, x1 - out[2], tmp[2] = x2, x2 - out[3], tmp[3] = x3, x3 - out[4], tmp[4] = x4, x4 - out[5], tmp[5] = x5, x5 - out[6], tmp[6] = x6, x6 - out[7], tmp[7] = x7, x7 - out[8], tmp[8] = x8, x8 - out[9], tmp[9] = x9, x9 - out[10], tmp[10] = x10, x10 - out[11], tmp[11] = x11, x11 - out[12], tmp[12] = x12, x12 - out[13], tmp[13] = x13, x13 - out[14], tmp[14] = x14, x14 - out[15], tmp[15] = x15, x15 -} - -func blockMix(tmp *[16]uint32, in, out []uint32, r int) { - blockCopy(tmp[:], in[(2*r-1)*16:], 16) - for i := 0; i < 2*r; i += 2 { - salsaXOR(tmp, in[i*16:], out[i*8:]) - salsaXOR(tmp, in[i*16+16:], out[i*8+r*16:]) - } -} - -func integer(b []uint32, r int) uint64 { - j := (2*r - 1) * 16 - return uint64(b[j]) | uint64(b[j+1])<<32 -} - -func smix(b []byte, r, N int, v, xy []uint32) { - var tmp [16]uint32 - x := xy - y := xy[32*r:] - - j := 0 - for i := 0; i < 32*r; i++ { - x[i] = uint32(b[j]) | uint32(b[j+1])<<8 | uint32(b[j+2])<<16 | uint32(b[j+3])<<24 - j += 4 - } - for i := 0; i < N; i += 2 { - blockCopy(v[i*(32*r):], x, 32*r) - blockMix(&tmp, x, y, r) - - blockCopy(v[(i+1)*(32*r):], y, 32*r) - blockMix(&tmp, y, x, r) - } - for i := 0; i < N; i += 2 { - j := int(integer(x, r) & uint64(N-1)) - blockXOR(x, v[j*(32*r):], 32*r) - blockMix(&tmp, x, y, r) - - j = int(integer(y, r) & uint64(N-1)) - blockXOR(y, v[j*(32*r):], 32*r) - blockMix(&tmp, y, x, r) - } - j = 0 - for _, v := range x[:32*r] { - b[j+0] = byte(v >> 0) - b[j+1] = byte(v >> 8) - b[j+2] = byte(v >> 16) - b[j+3] = byte(v >> 24) - j += 4 - } -} - -// Key derives a key from the password, salt, and cost parameters, returning -// a byte slice of length keyLen that can be used as cryptographic key. -// -// N is a CPU/memory cost parameter, which must be a power of two greater than 1. -// r and p must satisfy r * p < 2³⁰. If the parameters do not satisfy the -// limits, the function returns a nil byte slice and an error. -// -// For example, you can get a derived key for e.g. AES-256 (which needs a -// 32-byte key) by doing: -// -// dk, err := scrypt.Key([]byte("some password"), salt, 32768, 8, 1, 32) -// -// The recommended parameters for interactive logins as of 2017 are N=32768, r=8 -// and p=1. The parameters N, r, and p should be increased as memory latency and -// CPU parallelism increases; consider setting N to the highest power of 2 you -// can derive within 100 milliseconds. Remember to get a good random salt. -func Key(password, salt []byte, N, r, p, keyLen int) ([]byte, error) { - if N <= 1 || N&(N-1) != 0 { - return nil, errors.New("scrypt: N must be > 1 and a power of 2") - } - if uint64(r)*uint64(p) >= 1<<30 || r > maxInt/128/p || r > maxInt/256 || N > maxInt/128/r { - return nil, errors.New("scrypt: parameters are too large") - } - - xy := make([]uint32, 64*r) - v := make([]uint32, 32*N*r) - b := pbkdf2.Key(password, salt, 1, p*128*r, sha256.New) - - for i := 0; i < p; i++ { - smix(b[i*128*r:], r, N, v, xy) - } - - return pbkdf2.Key(password, b, 1, keyLen, sha256.New), nil -} diff --git a/vendor/golang.org/x/crypto/sha3/shake.go b/vendor/golang.org/x/crypto/sha3/shake.go index 97c9b062..a39e5d51 100644 --- a/vendor/golang.org/x/crypto/sha3/shake.go +++ b/vendor/golang.org/x/crypto/sha3/shake.go @@ -5,10 +5,18 @@ package sha3 // This file defines the ShakeHash interface, and provides -// functions for creating SHAKE instances, as well as utility +// functions for creating SHAKE and cSHAKE instances, as well as utility // functions for hashing bytes to arbitrary-length output. +// +// +// SHAKE implementation is based on FIPS PUB 202 [1] +// cSHAKE implementations is based on NIST SP 800-185 [2] +// +// [1] https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf +// [2] https://doi.org/10.6028/NIST.SP.800-185 import ( + "encoding/binary" "io" ) @@ -31,8 +39,77 @@ type ShakeHash interface { Reset() } -func (d *state) Clone() ShakeHash { - return d.clone() +// cSHAKE specific context +type cshakeState struct { + state // SHA-3 state context and Read/Write operations + + // initBlock is the cSHAKE specific initialization set of bytes. It is initialized + // by newCShake function and stores concatenation of N followed by S, encoded + // by the method specified in 3.3 of [1]. + // It is stored here in order for Reset() to be able to put context into + // initial state. + initBlock []byte +} + +// Consts for configuring initial SHA-3 state +const ( + dsbyteShake = 0x1f + dsbyteCShake = 0x04 + rate128 = 168 + rate256 = 136 +) + +func bytepad(input []byte, w int) []byte { + // leftEncode always returns max 9 bytes + buf := make([]byte, 0, 9+len(input)+w) + buf = append(buf, leftEncode(uint64(w))...) + buf = append(buf, input...) + padlen := w - (len(buf) % w) + return append(buf, make([]byte, padlen)...) +} + +func leftEncode(value uint64) []byte { + var b [9]byte + binary.BigEndian.PutUint64(b[1:], value) + // Trim all but last leading zero bytes + i := byte(1) + for i < 8 && b[i] == 0 { + i++ + } + // Prepend number of encoded bytes + b[i-1] = 9 - i + return b[i-1:] +} + +func newCShake(N, S []byte, rate int, dsbyte byte) ShakeHash { + c := cshakeState{state: state{rate: rate, dsbyte: dsbyte}} + + // leftEncode returns max 9 bytes + c.initBlock = make([]byte, 0, 9*2+len(N)+len(S)) + c.initBlock = append(c.initBlock, leftEncode(uint64(len(N)*8))...) + c.initBlock = append(c.initBlock, N...) + c.initBlock = append(c.initBlock, leftEncode(uint64(len(S)*8))...) + c.initBlock = append(c.initBlock, S...) + c.Write(bytepad(c.initBlock, c.rate)) + return &c +} + +// Reset resets the hash to initial state. +func (c *cshakeState) Reset() { + c.state.Reset() + c.Write(bytepad(c.initBlock, c.rate)) +} + +// Clone returns copy of a cSHAKE context within its current state. +func (c *cshakeState) Clone() ShakeHash { + b := make([]byte, len(c.initBlock)) + copy(b, c.initBlock) + return &cshakeState{state: *c.clone(), initBlock: b} +} + +// Clone returns copy of SHAKE context within its current state. +func (c *state) Clone() ShakeHash { + return c.clone() } // NewShake128 creates a new SHAKE128 variable-output-length ShakeHash. @@ -42,7 +119,7 @@ func NewShake128() ShakeHash { if h := newShake128Asm(); h != nil { return h } - return &state{rate: 168, dsbyte: 0x1f} + return &state{rate: rate128, dsbyte: dsbyteShake} } // NewShake256 creates a new SHAKE256 variable-output-length ShakeHash. @@ -52,7 +129,33 @@ func NewShake256() ShakeHash { if h := newShake256Asm(); h != nil { return h } - return &state{rate: 136, dsbyte: 0x1f} + return &state{rate: rate256, dsbyte: dsbyteShake} +} + +// NewCShake128 creates a new instance of cSHAKE128 variable-output-length ShakeHash, +// a customizable variant of SHAKE128. +// N is used to define functions based on cSHAKE, it can be empty when plain cSHAKE is +// desired. S is a customization byte string used for domain separation - two cSHAKE +// computations on same input with different S yield unrelated outputs. +// When N and S are both empty, this is equivalent to NewShake128. +func NewCShake128(N, S []byte) ShakeHash { + if len(N) == 0 && len(S) == 0 { + return NewShake128() + } + return newCShake(N, S, rate128, dsbyteCShake) +} + +// NewCShake256 creates a new instance of cSHAKE256 variable-output-length ShakeHash, +// a customizable variant of SHAKE256. +// N is used to define functions based on cSHAKE, it can be empty when plain cSHAKE is +// desired. S is a customization byte string used for domain separation - two cSHAKE +// computations on same input with different S yield unrelated outputs. +// When N and S are both empty, this is equivalent to NewShake256. +func NewCShake256(N, S []byte) ShakeHash { + if len(N) == 0 && len(S) == 0 { + return NewShake256() + } + return newCShake(N, S, rate256, dsbyteCShake) } // ShakeSum128 writes an arbitrary-length digest of data into hash. diff --git a/vendor/gopkg.in/yaml.v2/README.md b/vendor/gopkg.in/yaml.v2/README.md index b50c6e87..baef58a7 100644 --- a/vendor/gopkg.in/yaml.v2/README.md +++ b/vendor/gopkg.in/yaml.v2/README.md @@ -36,7 +36,7 @@ If opened in a browser, the import path itself leads to the API documentation: API stability ------------- -The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in). +The package rpcapi for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in). License diff --git a/vendor/vendor.json b/vendor/vendor.json index 3510af48..38ae6a1a 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -458,29 +458,17 @@ "revision": "c3a204f8e96543bb0cc090385c001078f184fc46", "revisionTime": "2019-03-18T03:00:20Z" }, - { - "checksumSHA1": "1MGpGDQqnUoRpv7VEcQrXOBydXE=", - "path": "golang.org/x/crypto/pbkdf2", - "revision": "a1f597ede03a7bef967a422b5b3a5bd08805a01e", - "revisionTime": "2019-02-05T21:23:42Z" - }, { "checksumSHA1": "UAbH5s3v5AfEvbGMEQAyzSFCMU0=", "path": "golang.org/x/crypto/ripemd160", - "revision": "a1f597ede03a7bef967a422b5b3a5bd08805a01e", - "revisionTime": "2019-02-05T21:23:42Z" - }, - { - "checksumSHA1": "q+Rqy6Spw6qDSj75TGEZF7nzoFM=", - "path": "golang.org/x/crypto/scrypt", - "revision": "a1f597ede03a7bef967a422b5b3a5bd08805a01e", - "revisionTime": "2019-02-05T21:23:42Z" + "revision": "df01cb2cc480549d72034218dd98bf97671450ac", + "revisionTime": "2019-04-17T08:53:58Z" }, { - "checksumSHA1": "asZBHvcTKF5gVlI7AYnMlLXRYys=", + "checksumSHA1": "jBttuA4t/SdGUb+M2kBHmrGOmRs=", "path": "golang.org/x/crypto/sha3", - "revision": "a1f597ede03a7bef967a422b5b3a5bd08805a01e", - "revisionTime": "2019-02-05T21:23:42Z" + "revision": "df01cb2cc480549d72034218dd98bf97671450ac", + "revisionTime": "2019-04-17T08:53:58Z" }, { "checksumSHA1": "F+tqxPGFt5x7DKZakbbMmENX1oQ=", diff --git a/wallet/cache/account.go b/wallet/cache/account.go deleted file mode 100644 index 91d89905..00000000 --- a/wallet/cache/account.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package cache - -import ( - "sort" - "strings" - - "github.com/fractalplatform/fractal/common" -) - -// Account represents an fractal account. -type Account struct { - Addr common.Address `json:"address"` // account address derived from the key. - Path string `json:"path"` // key json file path. - PublicKey string `json:"publicKey"` -} - -// Cmp compares x and y and returns: -// -// -1 if x < y -// 0 if x == y -// +1 if x > y -// -func (a Account) Cmp(account Account) int { - return strings.Compare(a.Path, account.Path) -} - -// Accounts is a Account slice type. -type Accounts []Account - -func (as Accounts) drop(accounts ...Account) Accounts { - for _, account := range accounts { - index := sort.Search(len(as), func(i int) bool { - return as[i].Cmp(account) >= 0 - }) - if index == len(as) { - as = append(as, account) - continue - } - as = append(as[:index], append([]Account{account}, as[index:]...)...) - } - return as -} - -func (as Accounts) merge(accounts ...Account) Accounts { - for _, account := range accounts { - index := sort.Search(len(as), func(i int) bool { - return as[i].Cmp(account) >= 0 - }) - if index == len(as) { - continue - } - as = append(as[:index], as[index+1:]...) - } - return as -} - -func (as Accounts) Len() int { return len(as) } -func (as Accounts) Less(i, j int) bool { return as[i].Cmp(as[j]) < 0 } -func (as Accounts) Swap(i, j int) { as[i], as[j] = as[j], as[i] } diff --git a/wallet/cache/accountcache.go b/wallet/cache/accountcache.go deleted file mode 100644 index 69dd99d5..00000000 --- a/wallet/cache/accountcache.go +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package cache - -import ( - "sort" - "sync" - "time" - - "github.com/ethereum/go-ethereum/log" - "github.com/fractalplatform/fractal/common" - "github.com/fractalplatform/fractal/wallet/keystore" -) - -const reloadInterval = 2 * time.Second - -// AccountCache is a live index of all accounts in the keystore. -type AccountCache struct { - sync.Mutex - keydir string - cache fileCache - reloadTimer *time.Timer - accountsMap map[common.Address]Account -} - -// NewAccountCache creates a account cache. -func NewAccountCache(keydir string) *AccountCache { - ac := &AccountCache{ - keydir: keydir, - cache: fileCache{all: make(map[string]interface{})}, - accountsMap: make(map[common.Address]Account), - } - return ac -} - -// Accounts returns all accounts in cacahe. -func (ac *AccountCache) Accounts() Accounts { - ac.Reload() - ac.Lock() - defer ac.Unlock() - var cpy Accounts - for _, a := range ac.accountsMap { - cpy = append(cpy, a) - } - sort.Sort(cpy) - return cpy -} - -// Reload reload cache. -func (ac *AccountCache) Reload() { - ac.Lock() - if ac.reloadTimer == nil { - ac.reloadTimer = time.NewTimer(0) - } else { - select { - case <-ac.reloadTimer.C: - default: - ac.Unlock() - return // The cache was reloaded recently. - } - } - ac.reloadTimer.Reset(reloadInterval) - ac.Unlock() - ac.updateAccounts() -} - -// Find find account by address. -func (ac *AccountCache) Find(addr common.Address) *Account { - ac.Reload() - ac.Lock() - defer ac.Unlock() - a, ok := ac.accountsMap[addr] - if !ok { - return nil - } - return &a -} - -// Close close account cache. -func (ac *AccountCache) Close() { - ac.Lock() - if ac.reloadTimer != nil { - ac.reloadTimer.Stop() - } - ac.Unlock() -} - -// Has check whether a key with the given address in cache. -func (ac *AccountCache) Has(addr common.Address) bool { - ac.Reload() - ac.Lock() - defer ac.Unlock() - if _, ok := ac.accountsMap[addr]; ok { - return true - } - return false -} - -// Add add a new acount in cache. -func (ac *AccountCache) Add(new Account) { - ac.Lock() - defer ac.Unlock() - ac.accountsMap[new.Addr] = new -} - -// Delete delete a account in cache. -func (ac *AccountCache) Delete(addr common.Address) { - ac.Lock() - defer ac.Unlock() - delete(ac.accountsMap, addr) -} - -func (ac *AccountCache) updateAccounts() error { - creates, deletes, updates, err := ac.cache.scan(ac.keydir) - if err != nil { - return err - } - - if len(creates) == 0 && len(deletes) == 0 && len(updates) == 0 { - return nil - } - - for _, path := range creates { - if a := readAccount(path); a != nil { - ac.Add(*a) - } - } - for _, path := range deletes { - if a := readAccount(path); a != nil { - ac.Delete(a.Addr) - } - } - for _, path := range updates { - if a := readAccount(path); a != nil { - ac.Add(*a) - } - } - - return nil -} - -func readAccount(path string) *Account { - if len(path) < 40 { - log.Error("invalid path", "path", path) - return nil - } - if common.IsHexAddress(path[len(path)-40:]) { - ks := &keystore.KeyStore{DirPath: "", ScryptN: 0, ScryptP: 0} - publicKey, err := ks.GetPublicKey(path) - if err != nil { - return nil - } - return &Account{ - Addr: common.HexToAddress(path[len(path)-40:]), - Path: path, - PublicKey: publicKey, - } - } - return nil -} diff --git a/wallet/cache/accountcache_test.go b/wallet/cache/accountcache_test.go deleted file mode 100644 index e28c803f..00000000 --- a/wallet/cache/accountcache_test.go +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package cache - -import ( - "encoding/hex" - "fmt" - "io/ioutil" - "os" - "sort" - "testing" - "time" - - "github.com/fractalplatform/fractal/common" - "github.com/fractalplatform/fractal/crypto" - "github.com/stretchr/testify/assert" -) - -func TestCache(t *testing.T) { - d, err := ioutil.TempDir("", "") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(d) - - cache := NewAccountCache(d) - - var accs Accounts - for i := 0; i < 7; i++ { - addr := crypto.CreateAddress(common.Address{}, uint64(i)) - account := Account{Addr: addr, Path: keyFileName(addr)} - cache.Add(account) - accs = append(accs, account) - } - - wantAccounts := make(Accounts, len(accs)) - copy(wantAccounts, accs) - sort.Sort(wantAccounts) - - // test cache.Accounts - list := cache.Accounts() - assert.Equal(t, wantAccounts, list) - - // test cache.Has - for _, a := range accs { - assert.Equal(t, true, cache.Has(a.Addr)) - } - - for i := 0; i < len(accs); i += 2 { - cache.Delete(wantAccounts[i].Addr) - } - - // Check content again after deletion. - wantAccountsAfterDelete := Accounts{ - wantAccounts[1], - wantAccounts[3], - wantAccounts[5], - } - - list = cache.Accounts() - assert.Equal(t, wantAccountsAfterDelete, list) - -} - -func keyFileName(keyAddr common.Address) string { - toISO8601 := func(t time.Time) string { - var tz string - name, offset := t.Zone() - if name == "UTC" { - tz = "Z" - } else { - tz = fmt.Sprintf("%03d00", offset/3600) - } - return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz) - } - - return fmt.Sprintf("UTC--%s--%s", toISO8601(time.Now().UTC()), hex.EncodeToString(keyAddr[:])) -} diff --git a/wallet/cache/filecache.go b/wallet/cache/filecache.go deleted file mode 100644 index 99cda981..00000000 --- a/wallet/cache/filecache.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package cache - -import ( - "io/ioutil" - "os" - "path/filepath" - "strings" - "sync" - "time" - - "github.com/ethereum/go-ethereum/log" -) - -type fileCache struct { - all map[string]interface{} - lastModified time.Time - mu sync.RWMutex -} - -func (fc *fileCache) scan(keyDir string) (creates, deletes, updates []string, err error) { - // List all the failes from the keystore folder - files, err := ioutil.ReadDir(keyDir) - if err != nil { - return nil, nil, nil, err - } - - fc.mu.Lock() - defer fc.mu.Unlock() - - // Iterate all the files and gather their metadata - all := make(map[string]interface{}) - mods := make(map[string]interface{}) - createsmap := make(map[string]interface{}) - - var newLastMod time.Time - for _, fi := range files { - path := filepath.Join(keyDir, fi.Name()) - if !isKeyFile(fi) { - log.Trace("Ignoring file on account scan", "path", path) - continue - } - - all[path] = struct{}{} - - modified := fi.ModTime() - if modified.After(fc.lastModified) { - mods[path] = struct{}{} - } - if modified.After(newLastMod) { - newLastMod = modified - } - } - - // Update the tracked files and return the three path slice - deletes = difference(fc.all, all) // Deletes = previous - current - creates = difference(all, fc.all) // Creates = current - previous - - for _, v := range creates { - createsmap[v] = struct{}{} - } - - updates = difference(mods, createsmap) // Updates = modified - creates - - fc.all, fc.lastModified = all, newLastMod - - return creates, deletes, updates, nil -} - -// difference Returns the difference between x and y. The returned string slice -// will contain all elements of x that are not also elements of y. -func difference(x, y map[string]interface{}) []string { - var result []string - for elem := range x { - if _, ok := y[elem]; !ok { - result = append(result, elem) - } - } - return result -} - -func isKeyFile(fi os.FileInfo) bool { - if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") { - return false - } - if fi.IsDir() || fi.Mode()&os.ModeType != 0 { - return false - } - return true -} diff --git a/wallet/keystore/key.go b/wallet/keystore/key.go deleted file mode 100644 index c5555e9b..00000000 --- a/wallet/keystore/key.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package keystore - -import ( - "crypto/ecdsa" - "io" - "io/ioutil" - "os" - "path/filepath" - - "github.com/fractalplatform/fractal/common" - "github.com/fractalplatform/fractal/crypto" -) - -// Key represents a key struct contains address and private key. -type Key struct { - Addr common.Address - PrivateKey *ecdsa.PrivateKey -} - -// NewKey generates a new key. -func NewKey(rand io.Reader) (*Key, error) { - privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand) - if err != nil { - return nil, err - } - - return &Key{ - Addr: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey), - PrivateKey: privateKeyECDSA, - }, nil -} - -func writeTemporaryKeyFile(file string, content []byte) (string, error) { - // Create the keystore directory with appropriate permissions - // in case it is not present yet. - const dirPerm = 0700 - if err := os.MkdirAll(filepath.Dir(file), dirPerm); err != nil { - return "", err - } - // Atomic write: create a temporary hidden file first - // then move it into place. TempFile assigns mode 0600. - f, err := ioutil.TempFile(filepath.Dir(file), "."+filepath.Base(file)+".tmp") - if err != nil { - return "", err - } - if _, err := f.Write(content); err != nil { - f.Close() - os.Remove(f.Name()) - return "", err - } - f.Close() - return f.Name(), nil -} - -func writeKeyFile(file string, content []byte) error { - name, err := writeTemporaryKeyFile(file, content) - if err != nil { - return err - } - return os.Rename(name, file) -} diff --git a/wallet/keystore/keyjson.go b/wallet/keystore/keyjson.go deleted file mode 100644 index f18b43ec..00000000 --- a/wallet/keystore/keyjson.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package keystore - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "encoding/hex" - "github.com/ethereum/go-ethereum/common/hexutil" - "io" - - "github.com/ethereum/go-ethereum/common/math" - "github.com/fractalplatform/fractal/crypto" - "golang.org/x/crypto/scrypt" -) - -const ( - defaultCipher = "aes-128-ctr" - defaultKDF = "scrypt" -) - -type keyJSON struct { - Address string `json:"address"` - PublicKey string `json:"publickey"` - Cipher string `json:"cipher"` - CipherText string `json:"ciphertext"` - CipherIV string `json:"cipheriv"` - KDF kdf `json:"kdf"` - MAC string `json:"mac"` -} - -type kdf struct { - Type string `json:"type"` - KeyLen int `json:"keylen"` - N int `json:"N"` - R int `json:"R"` - P int `json:"P"` - Salt string `json:"salt"` -} - -func (k *kdf) getKey(passphrase string) ([]byte, error) { - salt, err := hex.DecodeString(k.Salt) - if err != nil { - return nil, err - } - return scrypt.Key([]byte(passphrase), salt, k.N, k.R, k.P, k.KeyLen) -} - -func (kj *keyJSON) encryptKey(key *Key, passphrase string, scryptN, scryptP int) error { - keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32) - - salt := make([]byte, 32) - if _, err := io.ReadFull(rand.Reader, salt); err != nil { - panic("reading from crypto/rand failed: " + err.Error()) - } - derivedKey, err := scrypt.Key([]byte(passphrase), salt, scryptN, scryptR, scryptP, scryptDKLen) - if err != nil { - return err - } - - encryptKey := derivedKey[:16] - - iv := make([]byte, aes.BlockSize) // 16 - if _, err := io.ReadFull(rand.Reader, iv); err != nil { - panic("reading from crypto/rand failed: " + err.Error()) - } - cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv) - if err != nil { - return err - } - - mac := crypto.Keccak256(derivedKey[16:32], cipherText) - - kj.Address = key.Addr.Hex() - kj.PublicKey = hexutil.Bytes(crypto.FromECDSAPub(&key.PrivateKey.PublicKey)).String() - kj.Cipher = defaultCipher - kj.CipherIV = hex.EncodeToString(iv) - kj.CipherText = hex.EncodeToString(cipherText) - kj.MAC = hex.EncodeToString(mac) - kj.KDF.Type = defaultKDF - kj.KDF.KeyLen = scryptDKLen - kj.KDF.N = scryptN - kj.KDF.R = scryptR - kj.KDF.P = scryptP - kj.KDF.Salt = hex.EncodeToString(salt) - - return nil -} - -func (kj *keyJSON) decryptKey(passphrase string) (*Key, error) { - mac, err := hex.DecodeString(kj.MAC) - if err != nil { - return nil, err - } - - iv, err := hex.DecodeString(kj.CipherIV) - if err != nil { - return nil, err - } - - cipherText, err := hex.DecodeString(kj.CipherText) - if err != nil { - return nil, err - } - - derivedKey, err := kj.KDF.getKey(passphrase) - if err != nil { - return nil, err - } - - calculatedMAC := crypto.Keccak256(derivedKey[16:32], cipherText) - if !bytes.Equal(calculatedMAC, mac) { - return nil, ErrDecrypt - } - - plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv) - if err != nil { - return nil, err - } - - key := crypto.ToECDSAUnsafe(plainText) - return &Key{ - Addr: crypto.PubkeyToAddress(key.PublicKey), - PrivateKey: key, - }, nil - -} - -func aesCTRXOR(key, inText, iv []byte) ([]byte, error) { - // AES-128 is selected due to size of encryptKey. - aesBlock, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - stream := cipher.NewCTR(aesBlock, iv) - outText := make([]byte, len(inText)) - stream.XORKeyStream(outText, inText) - return outText, err -} diff --git a/wallet/keystore/keystore.go b/wallet/keystore/keystore.go deleted file mode 100644 index 278c6f73..00000000 --- a/wallet/keystore/keystore.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package keystore - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "path/filepath" - - "github.com/fractalplatform/fractal/common" -) - -const ( - // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB - // memory and taking approximately 1s CPU time on a modern processor. - StandardScryptN = 1 << 18 - - // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB - // memory and taking approximately 1s CPU time on a modern processor. - StandardScryptP = 1 - - // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB - // memory and taking approximately 100ms CPU time on a modern processor. - LightScryptN = 1 << 12 - - // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB - // memory and taking approximately 100ms CPU time on a modern processor. - LightScryptP = 6 - - scryptR = 8 - scryptDKLen = 32 -) - -// KeyStore manages a key storage directory on disk. -type KeyStore struct { - DirPath string - ScryptN, ScryptP int -} - -// GetKey load the key from the key file and decrypt it. -func (ks *KeyStore) GetKey(addr common.Address, filename, passphrase string) (*Key, error) { - keyjson, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - key, err := DecryptKey(keyjson, passphrase) - if err != nil { - return nil, err - } - // check address - if key.Addr != addr { - return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Addr, addr) - } - return key, nil -} - -// StoreKey encrypts with 'passphrase' and stores in the given directory -func (ks *KeyStore) StoreKey(key *Key, filename, passphrase string) error { - keyjson, err := EncryptKey(key, passphrase, ks.ScryptN, ks.ScryptP) - if err != nil { - return err - } - return writeKeyFile(filename, keyjson) -} - -func (ks *KeyStore) GetPublicKey(filename string) (string, error) { - keyjson, err := ioutil.ReadFile(filename) - if err != nil { - return "", err - } - keyj := new(keyJSON) - if err := json.Unmarshal(keyjson, keyj); err != nil { - return "", err - } - - return keyj.PublicKey, nil -} - -// JoinPath join file name into key dir path. -func (ks *KeyStore) JoinPath(filename string) string { - if filepath.IsAbs(filename) { - return filename - } - return filepath.Join(ks.DirPath, filename) -} - -// DecryptKey decrypts a key from a json bytes. -func DecryptKey(keyjson []byte, passphrase string) (*Key, error) { - keyj := new(keyJSON) - if err := json.Unmarshal(keyjson, keyj); err != nil { - return nil, err - } - return keyj.decryptKey(passphrase) -} - -// EncryptKey encrypts a key using the specified scrypt parameters into a json bytes. -func EncryptKey(key *Key, passphrase string, scryptN, scryptP int) ([]byte, error) { - keyj := new(keyJSON) - if err := keyj.encryptKey(key, passphrase, scryptN, scryptP); err != nil { - return nil, err - } - return json.Marshal(keyj) -} diff --git a/wallet/keystore/keystore_test.go b/wallet/keystore/keystore_test.go deleted file mode 100644 index b1d573be..00000000 --- a/wallet/keystore/keystore_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package keystore - -import ( - "crypto/ecdsa" - crand "crypto/rand" - "io/ioutil" - "testing" - - "github.com/fractalplatform/fractal/crypto" - "github.com/stretchr/testify/assert" -) - -func TestGetAndStoreKey(t *testing.T) { - privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), crand.Reader) - if err != nil { - t.Fatal(err) - } - key := &Key{ - Addr: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey), - PrivateKey: privateKeyECDSA, - } - - password := "pwd" - - ks := KeyStore{ - ScryptN: StandardScryptN, - ScryptP: StandardScryptP, - } - f, err := ioutil.TempFile("", "") - if err != nil { - t.Fatal(err) - } - - ks.StoreKey(key, f.Name(), password) - - newkey, err := ks.GetKey(key.Addr, f.Name(), password) - if err != nil { - t.Fatal(err) - } - - assert.Equal(t, newkey, key) - -} diff --git a/wallet/wallet.go b/wallet/wallet.go deleted file mode 100644 index e57f2056..00000000 --- a/wallet/wallet.go +++ /dev/null @@ -1,379 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package wallet - -import ( - "crypto/ecdsa" - crand "crypto/rand" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "math/big" - "os" - "time" - - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/log" - am "github.com/fractalplatform/fractal/accountmanager" - "github.com/fractalplatform/fractal/blockchain" - "github.com/fractalplatform/fractal/common" - "github.com/fractalplatform/fractal/crypto" - "github.com/fractalplatform/fractal/types" - "github.com/fractalplatform/fractal/wallet/cache" - "github.com/fractalplatform/fractal/wallet/keystore" -) - -// Wallet represents a software wallet. -type Wallet struct { - accounts cache.Accounts - cache *cache.AccountCache - ks *keystore.KeyStore - bindingFilePath string - blockchain *blockchain.BlockChain -} - -// NewWallet creates a wallet to sign transaction. -func NewWallet(keyStoredir string, scryptN, scryptP int) *Wallet { - log.Info("Disk storage enabled for keystore", "dir", keyStoredir) - w := &Wallet{ - cache: cache.NewAccountCache(keyStoredir), - ks: &keystore.KeyStore{DirPath: keyStoredir, ScryptN: scryptN, ScryptP: scryptP}, - } - - w.bindingFilePath = w.ks.JoinPath("accountKeyBindingInfo.txt") - return w -} - -func (w *Wallet) createFileIfNotExist(filePath string) error { - _, err := os.Stat(filePath) - if err != nil && os.IsNotExist(err) { - _, err = os.Create(filePath) - if err != nil { - log.Error("Create file fail:", "err=", err, "file=", filePath) - return err - } - log.Info("Create file success:", "file=", filePath) - return nil - } - return nil -} -func (w *Wallet) SetBlockChain(blockchain *blockchain.BlockChain) { - w.blockchain = blockchain -} -func (w *Wallet) GetAccountManager() (*am.AccountManager, error) { - statedb, err := w.blockchain.State() - if err != nil { - return nil, err - } - return am.NewAccountManager(statedb) -} - -// NewAccount generates a new key and stores it into the key directory. -func (w *Wallet) NewAccount(passphrase string) (cache.Account, error) { - key, err := keystore.NewKey(crand.Reader) - if err != nil { - return cache.Account{}, err - } - publicKey := hexutil.Bytes(crypto.FromECDSAPub(&key.PrivateKey.PublicKey)).String() - a := cache.Account{Addr: key.Addr, - Path: w.ks.JoinPath(keyFileName(key.Addr)), - PublicKey: publicKey} - - if err := w.ks.StoreKey(key, a.Path, passphrase); err != nil { - return cache.Account{}, err - } - w.cache.Add(a) - return a, nil -} - -// Delete deletes a account by passsphrase. -func (w *Wallet) Delete(a cache.Account, passphrase string) error { - a, _, err := w.getDecryptedKey(a, passphrase) - if err != nil { - return err - } - - if err := os.Remove(a.Path); err != nil { - return err - } - w.cache.Delete(a.Addr) - return nil -} - -// Update changes the passphrase of an existing account. -func (w *Wallet) Update(a cache.Account, passphrase, newPassphrase string) error { - a, key, err := w.getDecryptedKey(a, passphrase) - if err != nil { - return err - } - return w.ks.StoreKey(key, a.Path, newPassphrase) -} - -// Export exports as a JSON key, encrypted with newPassphrase. -func (w *Wallet) Export(a cache.Account, passphrase, newPassphrase string) (keyJSON []byte, err error) { - _, key, err := w.getDecryptedKey(a, passphrase) - if err != nil { - return nil, err - } - N, P := keystore.StandardScryptN, keystore.StandardScryptP - return keystore.EncryptKey(key, newPassphrase, N, P) -} - -// Import stores the given encrypted JSON key into the key directory. -func (w *Wallet) Import(keyJSON []byte, passphrase, newPassphrase string) (cache.Account, error) { - key, err := keystore.DecryptKey(keyJSON, passphrase) - if err != nil { - return cache.Account{}, err - } - return w.importKey(key, newPassphrase) - -} - -// ImportECDSA stores the given key into the key directory, encrypting it with the passphrase. -func (w *Wallet) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (cache.Account, error) { - key := &keystore.Key{ - Addr: crypto.PubkeyToAddress(priv.PublicKey), - PrivateKey: priv, - } - if w.cache.Has(key.Addr) { - return cache.Account{}, ErrAccountExists - } - return w.importKey(key, passphrase) -} - -// HasAddress reports whether a key with the given address is present. -func (w *Wallet) HasAddress(addr common.Address) bool { - return w.cache.Has(addr) -} - -// Accounts returns all key files -func (w *Wallet) Accounts() cache.Accounts { - return w.cache.Accounts() -} - -// Find resolves the given account into a unique entry in the keystore. -func (w *Wallet) Find(addr common.Address) (cache.Account, error) { - account := w.cache.Find(addr) - if account != nil { - return *account, nil - } - return cache.Account{}, ErrNoMatch -} - -// SignHashWithPassphrase signs hash if the private key matching the given address -// can be decrypted with the given passphrase. -func (w *Wallet) SignHashWithPassphrase(a cache.Account, passphrase string, hash []byte) (signature []byte, err error) { - _, key, err := w.getDecryptedKey(a, passphrase) - if err != nil { - return nil, err - } - return crypto.Sign(hash, key.PrivateKey) -} - -// SignTxWithPassphrase signs the Action if the private key matching the given address -// can be decrypted with the given passphrase. -func (w *Wallet) SignTxWithPassphrase(a cache.Account, passphrase string, tx *types.Transaction, action *types.Action, chainID *big.Int) (*types.Transaction, error) { - _, key, err := w.getDecryptedKey(a, passphrase) - if err != nil { - return nil, err - } - if err := types.SignAction(action, tx, types.NewSigner(chainID), key.PrivateKey); err != nil { - return nil, err - } - return tx, nil -} - -func (w *Wallet) importKey(key *keystore.Key, passphrase string) (cache.Account, error) { - a := cache.Account{Addr: key.Addr, Path: w.ks.JoinPath(keyFileName(key.Addr))} - if err := w.ks.StoreKey(key, a.Path, passphrase); err != nil { - return cache.Account{}, err - } - w.cache.Add(a) - return a, nil -} -func (w *Wallet) GetPrivateKey(a cache.Account, passphrase string) (*keystore.Key, error) { - _, key, err := w.getDecryptedKey(a, passphrase) - if err != nil { - return nil, err - } - return key, nil -} -func (w *Wallet) getDecryptedKey(a cache.Account, passphrase string) (cache.Account, *keystore.Key, error) { - a, err := w.Find(a.Addr) - if err != nil { - return a, nil, err - } - key, err := w.ks.GetKey(a.Addr, a.Path, passphrase) - return a, key, err -} - -// keyFileName implements the naming convention for keyfiles: -// UTC---
-func keyFileName(keyAddr common.Address) string { - ts := time.Now().UTC() - return fmt.Sprintf("UTC--%s--%s", toISO8601(ts), hex.EncodeToString(keyAddr[:])) -} - -func toISO8601(t time.Time) string { - var tz string - name, offset := t.Zone() - if name == "UTC" { - tz = "Z" - } else { - tz = fmt.Sprintf("%03d00", offset/3600) - } - return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz) -} - -// BindAccountNameAddr bind the account name and publicKey, the current publicKey is got from account manager. -func (w *Wallet) BindAccountAndPublicKey(accountName string) error { - publicKeyAccountsMap := w.getBindingInfo() - - // get account info firstly - accountMgr, err := w.GetAccountManager() - if err != nil { - return err - } - account, err := accountMgr.GetAccountByName(common.Name(accountName)) - if err != nil { - return err - } - curPublicKey := account.PublicKey.String() - - // We need consider 5 situations below: - // 1:publicKey exist, account NOT exist - // 2:publicKey NOT exist, account exist - // 3:publicKey AND account exist,but NOT matched - // 4:publicKey AND account exist,and matched - // 5:publicKey AND account both NOT exist - - // below loop can resolve: - // (1) when account exist, if 4, return, if 2 and 3, it can dismatch them, but we need match new publicKey then - // (2) when account NOT exist, copy original info - tmpPublicKeyAccountsMap := make(map[string][]string) - for publicKey, accounts := range publicKeyAccountsMap { - tmpAccounts := make([]string, 0) - for _, account := range accounts { - if account != accountName { - tmpAccounts = append(tmpAccounts, account) - continue - } else if curPublicKey == publicKey { - return nil - } - } - if len(tmpAccounts) > 0 { - tmpPublicKeyAccountsMap[publicKey] = tmpAccounts - } - } - if _, ok := tmpPublicKeyAccountsMap[curPublicKey]; ok { - tmpPublicKeyAccountsMap[curPublicKey] = append(tmpPublicKeyAccountsMap[curPublicKey], accountName) - } else { - accounts := make([]string, 0) - accounts = append(accounts, accountName) - tmpPublicKeyAccountsMap[curPublicKey] = accounts - } - - return w.writeBindingInfo(tmpPublicKeyAccountsMap) -} - -// ps: in this func, we can't use account's publicKey to get accounts info, -// because the account's publicKey has been updated by block chain. -func (w *Wallet) DeleteBound(accountName string) error { - publicKeyAccountsMap := w.getBindingInfo() - - tmpPublicKeyAccountsMap := make(map[string][]string) - for publicKey, accounts := range publicKeyAccountsMap { - tmpAccounts := make([]string, 0) - for _, account := range accounts { - if account != accountName { - tmpAccounts = append(tmpAccounts, account) - continue - } - } - if len(tmpAccounts) > 0 { - tmpPublicKeyAccountsMap[publicKey] = tmpAccounts - } - } - return w.writeBindingInfo(tmpPublicKeyAccountsMap) -} - -func (w *Wallet) GetAccountsByPublicKey(publicKey string) ([]am.Account, error) { - publicKeyAccountsMap := w.getBindingInfo() - accounts := make([]am.Account, 0) - if accountNames, ok := publicKeyAccountsMap[publicKey]; ok { - accountMgr, err := w.GetAccountManager() - if err != nil { - return nil, err - } - for _, accountName := range accountNames { - account, err := accountMgr.GetAccountByName(common.Name(accountName)) - if err != nil { - return nil, err - } - accounts = append(accounts, *account) - } - } - return accounts, nil -} - -func (w *Wallet) GetAllAccounts() ([]am.Account, error) { - publicKeyAccountsMap := w.getBindingInfo() - accountMgr, err := w.GetAccountManager() - if err != nil { - return nil, err - } - accounts := make([]am.Account, 0) - for _, accountNames := range publicKeyAccountsMap { - for _, accountName := range accountNames { - account, err := accountMgr.GetAccountByName(common.Name(accountName)) - if err != nil { - return nil, err - } - accounts = append(accounts, *account) - } - } - return accounts, nil -} - -func (w *Wallet) getBindingInfo() map[string][]string { - publicKeyAccountsMap := make(map[string][]string) - fileContent, _ := ioutil.ReadFile(w.bindingFilePath) - if fileContent != nil && len(fileContent) > 0 { - json.Unmarshal(fileContent, &publicKeyAccountsMap) - } - log.Debug("getBindingInfo:", "binging info", publicKeyAccountsMap) - return publicKeyAccountsMap -} - -func (w *Wallet) writeBindingInfo(addrAccountsMap map[string][]string) error { - log.Debug("writeBindingInfo:", "binging info", addrAccountsMap) - fileContent, err := json.Marshal(addrAccountsMap) - if err != nil { - log.Error("fail to marshall map to json string:", addrAccountsMap) - return err - } - - if ioutil.WriteFile(w.bindingFilePath, fileContent, 0666) == nil { - log.Debug("success to write binding info:", string(fileContent)) - return nil - } else { - log.Error("fail to write binding info:", string(fileContent)) - return errors.New("fail to write binding info") - } -} diff --git a/wallet/wallet_test.go b/wallet/wallet_test.go deleted file mode 100644 index 1fd20b84..00000000 --- a/wallet/wallet_test.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2018 The Fractal Team Authors -// This file is part of the fractal project. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package wallet - -import ( - "crypto/ecdsa" - crand "crypto/rand" - "io/ioutil" - "os" - "runtime" - "strings" - "testing" - - "github.com/fractalplatform/fractal/common" - "github.com/fractalplatform/fractal/crypto" - "github.com/fractalplatform/fractal/wallet/keystore" - "github.com/stretchr/testify/assert" -) - -func TestWallet(t *testing.T) { - d, err := ioutil.TempDir("", "") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(d) - - w := NewWallet(d, keystore.StandardScryptN, keystore.StandardScryptP) - - // test wallet.NewAccount - a, err := w.NewAccount("password") - if err != nil { - t.Fatal(err) - } - - if !strings.HasPrefix(a.Path, d) { - t.Errorf("account file %s doesn't have dir prefix", a.Path) - } - - stat, err := os.Stat(a.Path) - if err != nil { - t.Fatalf("account file %s doesn't exist (%v)", a.Path, err) - } - - if runtime.GOOS != "windows" && stat.Mode() != 0600 { - t.Fatalf("account file has wrong mode: got %o, want %o", stat.Mode(), 0600) - } - - // test wallet.HasAddress - if !w.HasAddress(a.Addr) { - t.Fatalf("HasAccount(%x) should've returned true", a.Addr) - } - - // test wallet.Update - if err := w.Update(a, "password", "newpassword"); err != nil { - t.Fatalf("Update error: %v", err) - } - - // test wallet.Delete - if err := w.Delete(a, "newpassword"); err != nil { - t.Fatalf("Delete error: %v", err) - } - - if common.FileExist(a.Path) { - t.Fatalf("account file %s should be gone after Delete", a.Path) - } - - if w.HasAddress(a.Addr) { - t.Fatalf("HasAccount(%x) should've returned true after Delete", a.Addr) - } -} - -func TestExportAndImportKey(t *testing.T) { - d, err := ioutil.TempDir("", "") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(d) - - w := NewWallet(d, keystore.StandardScryptN, keystore.StandardScryptP) - - privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), crand.Reader) - if err != nil { - t.Fatal(err) - } - password := "password" - newpassword := "newpassword" - newnewpassword := "newnewpassword" - - // test wallet.ImportECDSA - a, err := w.ImportECDSA(privateKeyECDSA, password) - if err != nil { - t.Fatal(err) - } - - // test wallet.Export - jsonbytes, err := w.Export(a, password, newpassword) - if err != nil { - t.Fatal(err) - } - - if err := w.Delete(a, password); err != nil { - t.Fatal(err) - } - - // test wallet.Import - newA, err := w.Import(jsonbytes, newpassword, newnewpassword) - if err != nil { - t.Fatal(err) - } - - assert.Equal(t, a.Addr, newA.Addr) -} - -func TestSignWithPassphrase(t *testing.T) { - var hash = make([]byte, 32) - - d, err := ioutil.TempDir("", "") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(d) - - w := NewWallet(d, keystore.StandardScryptN, keystore.StandardScryptP) - - privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), crand.Reader) - if err != nil { - t.Fatal(err) - } - password := "password" - - sig, err := crypto.Sign(hash, privateKeyECDSA) - if err != nil { - t.Fatal(err) - } - - a, err := w.ImportECDSA(privateKeyECDSA, password) - if err != nil { - t.Fatal(err) - } - - nSig, err := w.SignHashWithPassphrase(a, password, hash) - if err != nil { - t.Fatal(err) - } - - assert.Equal(t, nSig, sig) -}