diff --git a/Makefile b/Makefile
index 551294ce..c9e020f1 100644
--- a/Makefile
+++ b/Makefile
@@ -14,6 +14,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+# default target is 'all'
+all:
+
SHELL:=/bin/bash
REPO := $(shell pwd)
GOFILES_NOVENDOR := $(shell GOFLAGS="-mod=vendor" go list -f "{{.Dir}}" ./...)
@@ -24,7 +27,7 @@ export GOFLAGS=-mod=vendor
define build
@go build -ldflags " \
-X github.com/fractalplatform/fractal/cmd/utils.commit=$(shell cat commit_hash.txt) \
- -X github.com/fractalplatform/fractal/cmd/utils.date=$(shell date '+%Y-%m-%d') \
+ -X github.com/fractalplatform/fractal/cmd/utils.date=$(shell date '+%Y-%m-%d-%H:%M:%S') \
-X 'github.com/fractalplatform/fractal/cmd/utils.goversion=$(shell go version)'" \
-o ${REPO}/build/bin/$(1) ./cmd/$(1)
endef
diff --git a/accountmanager/accountmanager.go b/accountmanager/accountmanager.go
index 4b724e15..447d192b 100644
--- a/accountmanager/accountmanager.go
+++ b/accountmanager/accountmanager.go
@@ -136,6 +136,7 @@ func SetAccountNameConfig(config *Config) bool {
accountNameLength = config.AccountNameMaxLength
return true
}
+
func GetAccountNameRegExp() *regexp.Regexp {
return acctRegExp
}
@@ -619,6 +620,7 @@ func (am *AccountManager) getParentAccount(accountName common.Name, parentIndex
// RecoverTx Make sure the transaction is signed properly and validate account authorization.
func (am *AccountManager) RecoverTx(signer types.Signer, tx *types.Transaction) error {
+ authorVersion := make(map[common.Name]common.Hash)
for _, action := range tx.GetActions() {
pubs, err := types.RecoverMultiKey(signer, action, tx)
if err != nil {
@@ -646,7 +648,6 @@ func (am *AccountManager) RecoverTx(signer types.Signer, tx *types.Transaction)
}
}
- authorVersion := make(map[common.Name]common.Hash)
for name, acctAuthor := range recoverRes.acctAuthors {
var count uint64
for _, weight := range acctAuthor.indexWeight {
@@ -664,6 +665,53 @@ func (am *AccountManager) RecoverTx(signer types.Signer, tx *types.Transaction)
types.StoreAuthorCache(action, authorVersion)
}
+ if tx.PayerExist() {
+ for _, action := range tx.GetActions() {
+ pubs, err := types.RecoverPayerMultiKey(signer, action, tx)
+ if 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))
+ }
+
+ sig := action.PayerSignature()
+ if sig == nil {
+ return fmt.Errorf("payer signature is nil")
+ }
+ parentIndex := sig.ParentIndex
+ signSender, err := am.getParentAccount(action.Payer(), parentIndex)
+ if err != nil {
+ return err
+ }
+ recoverRes := &recoverActionResult{make(map[common.Name]*accountAuthor)}
+ for i, pub := range pubs {
+ index := sig.SignData[uint64(i)].Index
+ 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(signSender, pub, index, recoverRes); err != nil {
+ return err
+ }
+ }
+
+ for name, acctAuthor := range recoverRes.acctAuthors {
+ var count uint64
+ for _, weight := range acctAuthor.indexWeight {
+ count += weight
+ }
+ threshold := acctAuthor.threshold
+ if count < threshold {
+ return fmt.Errorf("account %s want threshold %d, but actual is %d", name, threshold, count)
+ }
+ authorVersion[name] = acctAuthor.version
+ }
+
+ types.StoreAuthorCache(action, authorVersion)
+ }
+ }
return nil
}
@@ -1337,12 +1385,12 @@ func (am *AccountManager) IssueAsset(fromName common.Name, asset IssueAsset, num
}
//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 {
+func (am *AccountManager) IncAsset2Acct(fromName common.Name, toName common.Name, assetID uint64, amount *big.Int, forkID uint64) error {
if err := am.ast.CheckOwner(fromName, assetID); err != nil {
return err
}
- if err := am.ast.IncreaseAsset(fromName, assetID, amount); err != nil {
+ if err := am.ast.IncreaseAsset(fromName, assetID, amount, forkID); err != nil {
return err
}
return nil
@@ -1365,7 +1413,7 @@ func (am *AccountManager) process(accountManagerContext *types.AccountManagerCon
var fromAccountExtra []common.Name
fromAccountExtra = append(fromAccountExtra, accountManagerContext.FromAccountExtra...)
- if err := action.Check(accountManagerContext.ChainConfig); err != nil {
+ if err := action.Check(curForkID, accountManagerContext.ChainConfig); err != nil {
return nil, err
}
@@ -1458,7 +1506,7 @@ func (am *AccountManager) process(accountManagerContext *types.AccountManagerCon
return nil, ErrNegativeAmount
}
- if err := am.IncAsset2Acct(action.Sender(), inc.To, inc.AssetID, inc.Amount); err != nil {
+ if err := am.IncAsset2Acct(action.Sender(), inc.To, inc.AssetID, inc.Amount, curForkID); err != nil {
return nil, err
}
@@ -1508,7 +1556,7 @@ func (am *AccountManager) process(accountManagerContext *types.AccountManagerCon
return nil, err
}
- if err := am.ast.UpdateAsset(action.Sender(), asset.AssetID, asset.Founder); err != nil {
+ if err := am.ast.UpdateAsset(action.Sender(), asset.AssetID, asset.Founder, curForkID); err != nil {
return nil, err
}
case types.SetAssetOwner:
diff --git a/accountmanager/accountmanager_test.go b/accountmanager/accountmanager_test.go
index 1148d470..c21a931a 100644
--- a/accountmanager/accountmanager_test.go
+++ b/accountmanager/accountmanager_test.go
@@ -1455,7 +1455,7 @@ func TestAccountManager_IncAsset2Acct(t *testing.T) {
sdb: tt.fields.sdb,
ast: tt.fields.ast,
}
- if err := am.IncAsset2Acct(tt.args.fromName, tt.args.toName, tt.args.AssetID, tt.args.amount); (err != nil) != tt.wantErr {
+ if err := am.IncAsset2Acct(tt.args.fromName, tt.args.toName, tt.args.AssetID, tt.args.amount, 4); (err != nil) != tt.wantErr {
t.Errorf("%q. AccountManager.IncAsset2Acct() error = %v, wantErr %v", tt.name, err, tt.wantErr)
}
}
@@ -2226,3 +2226,89 @@ func Test_CheckAssetContract(t *testing.T) {
}
}
}
+
+func TestAccountManager_CreateAccountFork1(t *testing.T) {
+ type fields struct {
+ sdb *state.StateDB
+ ast *asset.Asset
+ }
+ pubkey := new(common.PubKey)
+ pubkey2 := new(common.PubKey)
+ pubkey.SetBytes([]byte("abcde123456789"))
+
+ pubkey3, _ := GeneragePubKey()
+ type args struct {
+ fromName common.Name
+ accountName common.Name
+ founderName common.Name
+ pubkey common.PubKey
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ wantErr bool
+ }{
+ {"createAccount", fields{sdb, ast}, args{common.Name("fractal.founder"), common.Name("a111222332afork"), common.Name(""), pubkey3}, false},
+ {"createAccountSub", fields{sdb, ast}, args{common.Name("a111222332afork"), common.Name("a111222332afork.sub1"), common.Name(""), pubkey3}, false},
+ {"createAccountWithEmptyKey", fields{sdb, ast}, args{common.Name("fractal.founder"), common.Name("a123456789fork"), common.Name(""), *pubkey2}, false},
+ {"createAccountWithEmptyKey", fields{sdb, ast}, args{common.Name("fractal.founder"), common.Name("a123456789afork"), common.Name(""), *pubkey}, false},
+ {"createAccountWithInvalidName", fields{sdb, ast}, args{common.Name("fractal.founder"), common.Name("a12345678-fork"), common.Name(""), *pubkey}, true},
+ {"createAccountWithInvalidName", fields{sdb, ast}, args{common.Name("fractal.founder"), common.Name("a123456789aeeefork"), common.Name(""), *pubkey}, true},
+ {"createAccountNameInvalid", fields{sdb, ast}, args{common.Name("fractal.founder"), common.Name("a12345678b"), common.Name(""), pubkey3}, true},
+ {"createinvalidAccount0", fields{sdb, ast}, args{common.Name("fractal.founder"), common.Name("\ttesttestf1"), common.Name(""), *pubkey}, true},
+ {"createinvalidAccount1", fields{sdb, ast}, args{common.Name("fractal.founder"), common.Name("testtestf1.."), common.Name(""), *pubkey}, true},
+ {"createinvalidAccount2", fields{sdb, ast}, args{common.Name("fractal.founder"), common.Name("fractal.account"), common.Name(""), *pubkey}, true},
+ {"createinvalidAccount3", fields{sdb, ast}, args{common.Name("fractal.founder"), common.Name("fractal.founder.1"), common.Name(""), *pubkey}, true},
+ {"createinvalidAccount4", fields{sdb, ast}, args{common.Name("fractal.founder"), common.Name("fractal.founder.12"), common.Name(""), *pubkey}, true},
+ {"createinvalidAccount5", fields{sdb, ast}, args{common.Name("a111222332afork"), common.Name("a111222332afork.founder1234"), common.Name(""), *pubkey}, true},
+ }
+ for _, tt := range tests {
+ am := &AccountManager{
+ sdb: tt.fields.sdb,
+ ast: tt.fields.ast,
+ }
+ if err := am.CreateAccount(tt.args.fromName, 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)
+ }
+ }
+}
+
+func TestAccountManager_AccountHaveCode(t *testing.T) {
+ type fields struct {
+ sdb *state.StateDB
+ ast *asset.Asset
+ }
+
+ field := &fields{sdb, ast}
+
+ pubkey, _ := GeneragePubKey()
+
+ am := &AccountManager{
+ sdb: field.sdb,
+ ast: field.ast,
+ }
+
+ err := am.CreateAccount(common.Name("fractal.founder"), common.Name("a111222332code"), common.Name(""), 0, 1, pubkey, "")
+ if err != nil {
+ t.Errorf("TestAccountManager_AccountHaveCode. AccountManager.CreateAccount() error = %v", err)
+ }
+
+ haveCode, err := am.AccountHaveCode(common.Name("a111222332code"))
+
+ if err != nil || haveCode == true {
+ t.Errorf("TestAccountManager_AccountHaveCode. account have code error = %v", err)
+ }
+
+ _, err = am.SetCode(common.Name("a111222332code"), []byte("abcde123456789"))
+
+ if err != nil {
+ t.Errorf("TestAccountManager_AccountHaveCode. set code error = %v", err)
+ }
+
+ haveCode, err = am.AccountHaveCode(common.Name("a111222332code"))
+
+ if err != nil || haveCode == false {
+ t.Errorf("TestAccountManager_AccountHaveCode. account not have code error = %v", err)
+ }
+}
diff --git a/asset/asset.go b/asset/asset.go
index 8aee99fa..35198aac 100644
--- a/asset/asset.go
+++ b/asset/asset.go
@@ -23,6 +23,7 @@ import (
"strconv"
"strings"
+ "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/log"
"github.com/fractalplatform/fractal/common"
"github.com/fractalplatform/fractal/params"
@@ -294,6 +295,11 @@ func (a *Asset) IssueAssetObject(ao *AssetObject) (uint64, error) {
//IssueAsset issue asset
func (a *Asset) IssueAsset(assetName string, number uint64, forkID uint64, symbol string, amount *big.Int, dec uint64, founder common.Name, owner common.Name, limit *big.Int, contract common.Name, description string) (uint64, error) {
+ if forkID >= params.ForkID4 {
+ if amount.Cmp(math.MaxBig256) > 0 {
+ return 0, ErrAmountOverMax256
+ }
+ }
_, err := a.GetAssetIDByName(assetName)
if err != nil && err != ErrAssetNotExist {
return 0, err
@@ -355,7 +361,7 @@ func (a *Asset) DestroyAsset(accountName common.Name, assetID uint64, amount *bi
}
//IncreaseAsset increase asset, upperlimit == 0 means no upper limit
-func (a *Asset) IncreaseAsset(accountName common.Name, assetID uint64, amount *big.Int) error {
+func (a *Asset) IncreaseAsset(accountName common.Name, assetID uint64, amount *big.Int, forkID uint64) error {
if accountName == "" {
return ErrAccountNameNull
}
@@ -372,6 +378,11 @@ func (a *Asset) IncreaseAsset(accountName common.Name, assetID uint64, amount *b
if asset == nil {
return ErrAssetNotExist
}
+ if forkID >= params.ForkID4 {
+ if (new(big.Int).Add(asset.GetAssetAmount(), amount)).Cmp(math.MaxBig256) > 0 {
+ return ErrAmountOverMax256
+ }
+ }
// if asset.GetAssetOwner() != accountName {
// return ErrOwnerMismatch
// }
@@ -398,7 +409,7 @@ func (a *Asset) IncreaseAsset(accountName common.Name, assetID uint64, amount *b
}
//UpdateAsset change asset info
-func (a *Asset) UpdateAsset(accountName common.Name, assetID uint64, founderName common.Name) error {
+func (a *Asset) UpdateAsset(accountName common.Name, assetID uint64, founderName common.Name, curForkID uint64) error {
if accountName == "" {
return ErrAccountNameNull
}
@@ -412,8 +423,16 @@ func (a *Asset) UpdateAsset(accountName common.Name, assetID uint64, founderName
// if asset.GetAssetOwner() != accountName {
// return ErrOwnerMismatch
// }
-
- asset.SetAssetFounder(founderName)
+ if curForkID >= params.ForkID4 {
+ if len(founderName.String()) == 0 {
+ assetOwner := asset.GetAssetOwner()
+ asset.SetAssetFounder(assetOwner)
+ } else {
+ asset.SetAssetFounder(founderName)
+ }
+ } else {
+ asset.SetAssetFounder(founderName)
+ }
return a.SetAssetObject(asset)
}
diff --git a/asset/asset_object_test.go b/asset/asset_object_test.go
index 284dbcda..841845b8 100644
--- a/asset/asset_object_test.go
+++ b/asset/asset_object_test.go
@@ -26,13 +26,14 @@ import (
func Test_newAssetObject(t *testing.T) {
type args struct {
- assetName string
- symbol string
- amount *big.Int
- dec uint64
- founder common.Name
- owner common.Name
- UpperLimit *big.Int
+ assetName string
+ symbol string
+ amount *big.Int
+ dec uint64
+ founder common.Name
+ owner common.Name
+ UpperLimit *big.Int
+ description string
}
tests := []struct {
name string
@@ -41,19 +42,27 @@ 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, 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},
- {"symbolempty", args{"ft", "", big.NewInt(2), 18, common.Name("a123"), common.Name("a123"), big.NewInt(999999)}, nil, true},
- {"amount==0", args{"ft", "z", big.NewInt(-1), 18, common.Name("a123"), common.Name("a123"), big.NewInt(999999)}, nil, true},
- {"ownerempty", args{"ft", "z", big.NewInt(2), 18, common.Name(""), common.Name(""), big.NewInt(999999)}, nil, true},
- {"shortsymbol", args{"ft", "z", big.NewInt(2), 18, common.Name("a123"), common.Name("a123"), big.NewInt(999999)}, nil, true},
- {"longsymbol", args{"ft", "ftt0123456789ftt1", big.NewInt(2), 18, common.Name("a123"), common.Name("a123"), big.NewInt(999999)}, nil, true},
- {"emptyname", args{"ft", "#ip0123456789ft", big.NewInt(2), 18, common.Name("a123"), common.Name("a123"), big.NewInt(999999)}, nil, true},
+ {"normal", args{"ft", "ft", big.NewInt(2), 18, common.Name(""), common.Name("a123"), big.NewInt(999999), ""}, &AssetObject{0, 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},
+ {"symbolempty", args{"ft", "", big.NewInt(2), 18, common.Name("a123"), common.Name("a123"), big.NewInt(999999), ""}, nil, true},
+ {"amount==0", args{"ft", "z", big.NewInt(-1), 18, common.Name("a123"), common.Name("a123"), big.NewInt(999999), ""}, nil, true},
+ {"ownerempty", args{"ft", "z", big.NewInt(2), 18, common.Name(""), common.Name(""), big.NewInt(999999), ""}, nil, true},
+ {"shortsymbol", args{"ft", "z", big.NewInt(2), 18, common.Name("a123"), common.Name("a123"), big.NewInt(999999), ""}, nil, true},
+ {"longsymbol", args{"ft", "ftt0123456789ftt1", big.NewInt(2), 18, common.Name("a123"), common.Name("a123"), big.NewInt(999999), ""}, nil, true},
+ {"emptyname", args{"ft", "#ip0123456789ft", big.NewInt(2), 18, common.Name("a123"), common.Name("a123"), big.NewInt(999999), ""}, nil, true},
+ {"limiterror", args{"ft", "ft", big.NewInt(101), 18, common.Name(""), common.Name("a123"), big.NewInt(100), ""}, nil, true},
+ {"descerror", args{"ft", "ft", big.NewInt(100), 18, common.Name(""), common.Name("a123"), big.NewInt(101),
+ "aaaaaaaaaabbbbbbbbbbaaaaaaaaaabbbbbbbbbbaaaaaaaaaa" +
+ "bbbbbbbbbbaaaaaaaaaabbbbbbbbbbaaaaaaaaaabbbbbbbbbb" +
+ "aaaaaaaaaabbbbbbbbbbaaaaaaaaaabbbbbbbbbbaaaaaaaaaa" +
+ "bbbbbbbbbbaaaaaaaaaabbbbbbbbbbaaaaaaaaaabbbbbbbbbb" +
+ "aaaaaaaaaabbbbbbbbbbaaaaaaaaaabbbbbbbbbbaaaaaaaaaa" +
+ "bbbbbbbbbbaaaaaaaaaabbbbbbbbbbaaaaaaaaaabbbbbbbbbb"}, nil, true},
}
for _, tt := range tests {
- 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(""), "")
+ 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(""), tt.args.description)
if (err != nil) != tt.wantErr {
t.Errorf("%q. newAssetObject() error = %v, wantErr %v", tt.name, err, tt.wantErr)
continue
@@ -456,3 +465,90 @@ func TestAssetObject_SetAssetOwner(t *testing.T) {
ao.SetAssetOwner(tt.args.owner)
}
}
+
+func TestAssetObject_AssetNumber(t *testing.T) {
+ ao := NewAssetObjectNoCheck("", uint64(0), "", big.NewInt(0), 0, common.Name(""), common.Name(""), big.NewInt(1000), common.Name(""), "")
+ // ao := &AssetObject{
+ // UpperLimit: big.NewInt(1000),
+ // }
+
+ id := uint64(1)
+ ao.SetAssetID(id)
+ if got := ao.GetAssetID(); got != id {
+ t.Errorf("AssetObject.GetAssetID() = %v, want %v", got, id)
+ }
+
+ number := uint64(2)
+ ao.SetAssetNumber(number)
+ if got := ao.GetAssetNumber(); got != number {
+ t.Errorf("AssetObject.GetAssetNumber() = %v, want %v", got, number)
+ }
+
+ stats := uint64(3)
+ ao.SetAssetStats(stats)
+ if got := ao.GetAssetStats(); got != stats {
+ t.Errorf("AssetObject.GetAssetStats() = %v, want %v", got, stats)
+ }
+
+ symbol := "symbol"
+ ao.SetSymbol(symbol)
+ if got := ao.GetSymbol(); got != symbol {
+ t.Errorf("AssetObject.GetSymbol() = %v, want %v", got, symbol)
+ }
+
+ decimals := uint64(4)
+ ao.SetDecimals(decimals)
+ if got := ao.GetDecimals(); got != decimals {
+ t.Errorf("AssetObject.GetDecimals() = %v, want %v", got, decimals)
+ }
+
+ name := "name"
+ ao.SetAssetName(name)
+ if got := ao.GetAssetName(); got != name {
+ t.Errorf("AssetObject.GetAssetName() = %v, want %v", got, name)
+ }
+
+ amount := big.NewInt(5)
+ ao.SetAssetAmount(amount)
+ if got := ao.GetAssetAmount(); got != amount {
+ t.Errorf("AssetObject.GetAssetAmount() = %v, want %v", got, amount)
+ }
+
+ issue := big.NewInt(6)
+ ao.SetAssetAddIssue(issue)
+ if got := ao.GetAssetAddIssue(); got != issue {
+ t.Errorf("AssetObject.GetAssetAddIssue() = %v, want %v", got, issue)
+ }
+
+ if got := ao.GetUpperLimit(); got != ao.UpperLimit {
+ t.Errorf("AssetObject.GetUpperLimit() = %v, want %v", got, ao.UpperLimit)
+ }
+
+ contract := common.Name("contract")
+ ao.SetAssetContract(contract)
+ if got := ao.GetAssetContract(); got != contract {
+ t.Errorf("AssetObject.GetAssetContract() = %v, want %v", got, contract)
+ }
+ if got := ao.GetContract(); got != contract {
+ t.Errorf("AssetObject.GetContract() = %v, want %v", got, contract)
+ }
+
+ founder := common.Name("founder")
+ ao.SetAssetFounder(founder)
+ if got := ao.GetAssetFounder(); got != founder {
+ t.Errorf("AssetObject.GetAssetFounder() = %v, want %v", got, founder)
+ }
+
+ owner := common.Name("owner")
+ ao.SetAssetOwner(owner)
+ if got := ao.GetAssetOwner(); got != owner {
+ t.Errorf("AssetObject.GetAssetFounder() = %v, want %v", got, owner)
+ }
+
+ desc := "desc"
+ ao.SetAssetDescription(desc)
+ if got := ao.GetAssetDescription(); got != desc {
+ t.Errorf("AssetObject.GetAssetFounder() = %v, want %v", got, desc)
+ }
+
+}
diff --git a/asset/asset_test.go b/asset/asset_test.go
index e0e96d49..30b8b104 100644
--- a/asset/asset_test.go
+++ b/asset/asset_test.go
@@ -455,7 +455,7 @@ func TestAsset_IncreaseAsset(t *testing.T) {
a := &Asset{
sdb: tt.fields.sdb,
}
- if err := a.IncreaseAsset(tt.args.accountName, tt.args.AssetID, tt.args.amount); (err != nil) != tt.wantErr {
+ if err := a.IncreaseAsset(tt.args.accountName, tt.args.AssetID, tt.args.amount, 4); (err != nil) != tt.wantErr {
t.Errorf("%q. Asset.IncreaseAsset() error = %v, wantErr %v", tt.name, err, tt.wantErr)
}
}
@@ -501,6 +501,7 @@ func TestAsset_UpdateAsset(t *testing.T) {
AssetID uint64
Owner common.Name
founder common.Name
+ forkID uint64
}
tests := []struct {
name string
@@ -509,17 +510,19 @@ func TestAsset_UpdateAsset(t *testing.T) {
wantErr bool
}{
// TODO: Add test cases
- {"nilname", fields{assetDB}, args{common.Name(""), 1, common.Name(""), common.Name("")}, true},
- {"wrongAssetID", fields{assetDB}, args{common.Name("11"), 0, common.Name(""), common.Name("")}, false},
- {"wrongamount", fields{assetDB}, args{common.Name("11"), 123, common.Name(""), common.Name("")}, true},
- {"nilfounder", fields{assetDB}, args{common.Name("a123456789afff"), 1, common.Name("a123456789aeee"), common.Name("")}, false},
- {"normal", fields{assetDB}, args{common.Name("a123456789afff"), 1, common.Name("a123456789afff"), common.Name("a123456789afff")}, false},
+ {"nilname", fields{assetDB}, args{common.Name(""), 1, common.Name(""), common.Name(""), 0}, true},
+ {"wrongAssetID", fields{assetDB}, args{common.Name("11"), 0, common.Name(""), common.Name(""), 0}, false},
+ {"wrongamount", fields{assetDB}, args{common.Name("11"), 123, common.Name(""), common.Name(""), 0}, true},
+ {"nilfounder", fields{assetDB}, args{common.Name("a123456789afff"), 1, common.Name("a123456789aeee"), common.Name(""), 0}, false},
+ {"nilfounder", fields{assetDB}, args{common.Name("a123456789afff"), 1, common.Name("a123456789aeee"), common.Name(""), 4}, false},
+ {"nilfounder", fields{assetDB}, args{common.Name("a123456789afff"), 1, common.Name("a123456789aeee"), common.Name("a123456789afff"), 4}, false},
+ {"normal", fields{assetDB}, args{common.Name("a123456789afff"), 1, common.Name("a123456789afff"), common.Name("a123456789afff"), 0}, false},
}
for _, tt := range tests {
a := &Asset{
sdb: tt.fields.sdb,
}
- if err := a.UpdateAsset(tt.args.accountName, tt.args.AssetID, tt.args.founder); (err != nil) != tt.wantErr {
+ if err := a.UpdateAsset(tt.args.accountName, tt.args.AssetID, tt.args.founder, tt.args.forkID); (err != nil) != tt.wantErr {
t.Errorf("%q. Asset.updateAsset() error = %v, wantErr %v", tt.name, err, tt.wantErr)
}
}
diff --git a/asset/error.go b/asset/error.go
index 9e206ff5..8c194b8d 100644
--- a/asset/error.go
+++ b/asset/error.go
@@ -34,4 +34,5 @@ var (
ErrAssetManagerNotExist = errors.New("asset manager name not exist")
ErrDetailTooLong = errors.New("detail info exceed maximum")
ErrNegativeAmount = errors.New("negative amount")
+ ErrAmountOverMax256 = errors.New("amount over max uint256")
)
diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go
index 703b2963..75d068e9 100644
--- a/blockchain/blockchain.go
+++ b/blockchain/blockchain.go
@@ -280,11 +280,6 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
return batch.Write()
}
-// GasLimit returns the gas limit of the current HEAD block.
-func (bc *BlockChain) GasLimit() uint64 {
- return bc.CurrentBlock().GasLimit()
-}
-
// CurrentBlock retrieves the current head block of the canonical chain.
func (bc *BlockChain) CurrentBlock() *types.Block {
return bc.currentBlock.Load().(*types.Block)
diff --git a/blockchain/blockchain_test.go b/blockchain/blockchain_test.go
index b720ab2c..f8e9715a 100644
--- a/blockchain/blockchain_test.go
+++ b/blockchain/blockchain_test.go
@@ -20,6 +20,8 @@ import (
"testing"
"github.com/ethereum/go-ethereum/log"
+ "github.com/fractalplatform/fractal/processor/vm"
+ "github.com/fractalplatform/fractal/txpool"
)
// So we can deterministically seed different blockchains
@@ -66,3 +68,43 @@ func TestSystemForkChain(t *testing.T) {
// check if is complete block chain
checkCompleteChain(t, chain)
}
+
+func TestBadBlockHashes(t *testing.T) {
+ genesis := DefaultGenesis()
+ chain := newCanonical(t, genesis)
+ defer chain.Stop()
+
+ _, blocks := makeNewChain(t, genesis, chain, 10, canonicalSeed)
+
+ chain.badHashes[blocks[2].Header().Hash()] = true
+
+ _, err := chain.InsertChain(blocks)
+ if err != ErrBlacklistedHash {
+ t.Errorf("error mismatch: have: %v, want: %v", err, ErrBlacklistedHash)
+ }
+
+ // test NewBlockChain()badblock err
+ delete(chain.badHashes, blocks[2].Header().Hash())
+
+ _, err = chain.InsertChain(blocks)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ newChain, err := NewBlockChain(chain.db, false, vm.Config{}, chain.chainConfig,
+ []string{blocks[2].Header().Hash().String()}, 0, txpool.SenderCacher)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer newChain.Stop()
+
+ // if db have bad block then block will be reset, newChain.CurrentBlock().Hash() must equal chain or newchain genesis block hash.
+ if newChain.CurrentBlock().Hash() != chain.GetBlockByNumber(0).Hash() ||
+ newChain.CurrentBlock().Hash() != newChain.GetBlockByNumber(0).Hash() {
+ t.Fatalf("cur hash %x , genesis hash %v", newChain.CurrentBlock().Hash(),
+ newChain.Genesis().Hash())
+ }
+
+ t.Log(newChain.CurrentBlock().Hash().String())
+ t.Log(newChain.Genesis().Hash().String())
+}
diff --git a/blockchain/downloader.go b/blockchain/downloader.go
index 43089953..c2719c6c 100644
--- a/blockchain/downloader.go
+++ b/blockchain/downloader.go
@@ -417,7 +417,8 @@ func (dl *Downloader) shortcutDownload(status *stationStatus, startNumber uint64
}
// return true means need call again
-func (dl *Downloader) multiplexDownload(status *stationStatus) bool {
+func (dl *Downloader) multiplexDownload() bool {
+ status := dl.bestStation()
log.Debug("multiplexDownload start")
defer log.Debug("multiplexDownload end")
if status == nil {
@@ -467,10 +468,15 @@ func (dl *Downloader) multiplexDownload(status *stationStatus) bool {
ancestor, ancestorHash, err := dl.findAncestor(stationSearch, status.station, headNumber, status.ancestor, status.errCh)
if err != nil {
log.Warn("ancestor err", "err", err, "errID:", err.eid)
+ router.AddErr(status.station, 1)
if err.eid == notFind {
log.Warn("Disconnect because ancestor not find:", "node:", adaptor.GetFnode(status.station))
router.SendTo(nil, nil, router.OneMinuteLimited, status.station) // disconnect and put into blacklist
+ } else if router.Err(status.station) > 50 {
+ log.Warn("Disconnect because too much error:", "node:", adaptor.GetFnode(status.station))
+ router.SendTo(nil, nil, router.OneMinuteLimited, status.station) // disconnect and put into blacklist
}
+
return false
}
log.Debug("downloader ancestor:", "ancestor", ancestor)
@@ -556,19 +562,16 @@ func (dl *Downloader) loopStart() {
func (dl *Downloader) loop() {
defer dl.loopWG.Done()
- download := func() {
- //for status := dl.bestStation(); dl.download(status); {
- for status := dl.bestStation(); dl.multiplexDownload(status); {
- }
- }
timer := time.NewTimer(10 * time.Second)
for {
select {
case <-dl.quit:
return
case <-dl.downloadTrigger:
- download()
timer.Stop()
+ if dl.multiplexDownload() {
+ dl.loopStart()
+ }
timer.Reset(10 * time.Second)
case <-timer.C:
dl.loopStart()
@@ -716,18 +719,35 @@ func (task *downloadTask) Do() {
}, downloadAmount, 0, false,
}, task.worker.errCh)
if err != nil || len(headers) != int(downloadAmount) {
- log.Debug(fmt.Sprint("err-2:", err, len(headers), downloadAmount))
+ log.Debug("download header failed",
+ "err", err,
+ "recvAmount", len(headers),
+ "taskAmount", downloadAmount,
+ )
return
}
if headers[0].Number.Uint64() != task.startNumber+1 || headers[0].ParentHash != task.startHash ||
headers[len(headers)-1].Number.Uint64() != task.endNumber || headers[len(headers)-1].Hash() != task.endHash {
- log.Debug(fmt.Sprintf("e2-1 0d:%d\n0ed:%d\nsd:%d\nsed:%d", headers[0].Number.Uint64(), headers[len(headers)-1].Number.Uint64(), task.startNumber, task.endNumber))
- log.Debug(fmt.Sprintf("e2-2 0:%x\n0e:%x\ns:%x\nse:%x", headers[0].Hash(), headers[len(headers)-1].Hash(), task.startHash, task.endHash))
+ log.Debug("download header don't match task",
+ "recv.Start.Number", headers[0].Number.Uint64(),
+ "recv.End.Number", headers[len(headers)-1].Number.Uint64(),
+ "recv.Start.ParentHash", headers[0].ParentHash,
+ "recv.End.Hash", headers[len(headers)-1].Hash(),
+ "task.Start.Number", task.startNumber,
+ "task.End.Number", task.endNumber,
+ "task.Start.Hash", task.startHash,
+ "task.End.Hash", task.endHash,
+ )
return
}
for i := 1; i < len(headers); i++ {
if headers[i].ParentHash != headers[i-1].Hash() || headers[i].Number.Uint64() != headers[i-1].Number.Uint64()+1 {
- log.Debug(fmt.Sprintf("err-3: phash:%x n->phash:%x\npn+1:%d n:%d", headers[i-1].Hash(), headers[i].ParentHash, headers[i-1].Number.Uint64()+1, headers[i].Number.Uint64()))
+ log.Debug("download headers are discontinuous",
+ "parent.number", headers[i-1].Number.Uint64(),
+ "parent.hash", headers[i-1].Hash(),
+ "n.number", headers[i].Number.Uint64(),
+ "n.parentHash", headers[i].ParentHash,
+ )
return
}
}
@@ -741,7 +761,10 @@ func (task *downloadTask) Do() {
bodies, err = getBlocks(station, remote, reqHashes, task.worker.errCh)
if err != nil || len(bodies) != len(reqHashes) {
- log.Debug(fmt.Sprint("err-4:", err, len(bodies), len(reqHashes)))
+ log.Debug("download blocks failed",
+ "err", err,
+ "recvAmount", len(bodies),
+ "taskAmount", len(reqHashes))
return
}
diff --git a/blockchain/genesis.go b/blockchain/genesis.go
index 60bab804..45754d26 100644
--- a/blockchain/genesis.go
+++ b/blockchain/genesis.go
@@ -50,7 +50,7 @@ type GenesisAccount struct {
// GenesisCandidate is an candidate in the state of the genesis block.
type GenesisCandidate struct {
Name string `json:"name,omitempty"`
- URL string `json:"url,omitempty"`
+ Info string `json:"info,omitempty"`
Stake *big.Int `json:"stake,omitempty"`
}
@@ -202,6 +202,7 @@ func (g *Genesis) ToBlock(db fdb.Database) (*types.Block, []*types.Receipt, erro
SubAssetNameMinLength: 1,
SubAssetNameMaxLength: 8,
})
+
am.SetAcctMangerName(common.StrToName(g.Config.AccountName))
at.SetAssetMangerName(common.StrToName(g.Config.AssetName))
fm.SetFeeManagerName(common.StrToName(g.Config.FeeName))
@@ -402,7 +403,7 @@ func (g *Genesis) ToBlock(db fdb.Database) (*types.Block, []*types.Receipt, erro
if err := sys.SetCandidate(&dpos.CandidateInfo{
Epoch: epoch,
Name: candidate.Name,
- URL: candidate.URL,
+ Info: candidate.Info,
Quantity: big.NewInt(0),
TotalQuantity: big.NewInt(0),
Number: number.Uint64(),
diff --git a/cmd/ft/p2p.go b/cmd/ft/p2p.go
index ab773ba5..1cb09ac3 100644
--- a/cmd/ft/p2p.go
+++ b/cmd/ft/p2p.go
@@ -28,141 +28,117 @@ var p2pCmd = &cobra.Command{
Args: cobra.NoArgs,
}
-var addCmd = &cobra.Command{
- Use: "add ",
- Short: "Connecting to a remote node.",
- Long: `Connecting to a remote node.`,
- Args: cobra.ExactArgs(1),
- Run: func(cmd *cobra.Command, args []string) {
- var result bool
- clientCall(ipcEndpoint, &result, "p2p_addPeer", args[0])
+var commonCall = func(method string) func(*cobra.Command, []string) {
+ return func(cmd *cobra.Command, args []string) {
+ params := make([]interface{}, len(args))
+ for i, arg := range args {
+ params[i] = arg
+ }
+ result := clientCallRaw(ipcEndpoint, method, params...)
printJSON(result)
- },
+ }
}
-var removeCmd = &cobra.Command{
- Use: "remove ",
- Short: "Disconnects from a remote node if the connection exists.",
- Long: `Disconnects from a remote node if the connection exists.`,
- Args: cobra.ExactArgs(1),
- Run: func(cmd *cobra.Command, args []string) {
- var result bool
- clientCall(ipcEndpoint, &result, "p2p_removePeer", args[0])
- printJSON(result)
+var p2pSubCmds = []*cobra.Command{
+ &cobra.Command{
+ Use: "add ",
+ Short: "Connecting to a remote node.",
+ Long: `Connecting to a remote node.`,
+ Args: cobra.ExactArgs(1),
+ Run: commonCall("p2p_addPeer"),
},
-}
-var addtrustedCmd = &cobra.Command{
- Use: "addtrusted ",
- Short: "Allows a remote node to always connect, even if slots are full.",
- Long: `Allows a remote node to always connect, even if slots are full.`,
- Args: cobra.ExactArgs(1),
- Run: func(cmd *cobra.Command, args []string) {
- var result bool
- clientCall(ipcEndpoint, &result, "p2p_addTrustedPeer", args[0])
- printJSON(result)
+ &cobra.Command{
+ Use: "remove ",
+ Short: "Disconnects from a remote node if the connection exists.",
+ Long: `Disconnects from a remote node if the connection exists.`,
+ Args: cobra.ExactArgs(1),
+ Run: commonCall("p2p_removePeer"),
},
-}
-var removetrustedCmd = &cobra.Command{
- Use: "removetrusted ",
- Short: "Removes a remote node from the trusted peer set, but it does not disconnect it automatically.",
- Long: `Removes a remote node from the trusted peer set, but it does not disconnect it automatically.`,
- Args: cobra.ExactArgs(1),
- Run: func(cmd *cobra.Command, args []string) {
- var result bool
- clientCall(ipcEndpoint, &result, "p2p_removeTrustedPeer", args[0])
- printJSON(result)
+ &cobra.Command{
+ Use: "addtrusted ",
+ Short: "Allows a remote node to always connect, even if slots are full.",
+ Long: `Allows a remote node to always connect, even if slots are full.`,
+ Args: cobra.ExactArgs(1),
+ Run: commonCall("p2p_addTrustedPeer"),
},
-}
-var addbadCmd = &cobra.Command{
- Use: "addbad ",
- Short: "Add a bad node in black list.",
- Long: `Add a bad node in black list..`,
- Args: cobra.ExactArgs(1),
- Run: func(cmd *cobra.Command, args []string) {
- var result bool
- clientCall(ipcEndpoint, &result, "p2p_addBadNode", args[0])
- printJSON(result)
+ &cobra.Command{
+ Use: "removetrusted ",
+ Short: "Removes a remote node from the trusted peer set, but it does not disconnect it automatically.",
+ Long: `Removes a remote node from the trusted peer set, but it does not disconnect it automatically.`,
+ Args: cobra.ExactArgs(1),
+ Run: commonCall("p2p_removeTrustedPeer"),
},
-}
-var removebadCmd = &cobra.Command{
- Use: "removebad ",
- Short: "Removes a bad node from the black peer set.",
- Long: `Removes a bad node from the black peer set.`,
- Args: cobra.ExactArgs(1),
- Run: func(cmd *cobra.Command, args []string) {
- var result bool
- clientCall(ipcEndpoint, &result, "p2p_removeBadNode", args[0])
- printJSON(result)
+ &cobra.Command{
+ Use: "addbad ",
+ Short: "Add a bad node in black list.",
+ Long: `Add a bad node in black list..`,
+ Args: cobra.ExactArgs(1),
+ Run: commonCall("p2p_addBadNode"),
},
-}
-var countCmd = &cobra.Command{
- Use: "count",
- Short: "Return number of connected peers.",
- Long: `Return number of connected peers.`,
- Args: cobra.NoArgs,
- Run: func(cmd *cobra.Command, args []string) {
- var result int
- clientCall(ipcEndpoint, &result, "p2p_peerCount")
- printJSON(result)
+ &cobra.Command{
+ Use: "removebad ",
+ Short: "Removes a bad node from the black peer set.",
+ Long: `Removes a bad node from the black peer set.`,
+ Args: cobra.ExactArgs(1),
+ Run: commonCall("p2p_removeBadNode"),
},
-}
-var listCmd = &cobra.Command{
- Use: "list",
- Short: "Return connected peers list.",
- Long: `Return connected peers list.`,
- Args: cobra.NoArgs,
- Run: func(cmd *cobra.Command, args []string) {
- var result []string
- clientCall(ipcEndpoint, &result, "p2p_peers")
- printJSONList(result)
+ &cobra.Command{
+ Use: "count",
+ Short: "Return number of connected peers.",
+ Long: `Return number of connected peers.`,
+ Args: cobra.NoArgs,
+ Run: commonCall("p2p_peerCount"),
},
-}
-var badcountCmd = &cobra.Command{
- Use: "badcount",
- Short: "Return number of bad nodes .",
- Long: `Return number of bad nodes .`,
- Args: cobra.NoArgs,
- Run: func(cmd *cobra.Command, args []string) {
- var result int
- clientCall(ipcEndpoint, &result, "p2p_badNodesCount")
- printJSON(result)
+ &cobra.Command{
+ Use: "list",
+ Short: "Return connected peers list.",
+ Long: `Return connected peers list.`,
+ Args: cobra.NoArgs,
+ Run: commonCall("p2p_peers"),
},
-}
-var badlistCmd = &cobra.Command{
- Use: "badlist",
- Short: "Return bad nodes list.",
- Long: `Return bad nodes list.`,
- Args: cobra.NoArgs,
- Run: func(cmd *cobra.Command, args []string) {
- var result []string
- clientCall(ipcEndpoint, &result, "p2p_badNodes")
- printJSONList(result)
+ &cobra.Command{
+ Use: "badcount",
+ Short: "Return number of bad nodes .",
+ Long: `Return number of bad nodes .`,
+ Args: cobra.NoArgs,
+ Run: commonCall("p2p_badNodesCount"),
},
-}
-var selfnodeCmd = &cobra.Command{
- Use: "selfnode",
- Short: "Return self enode url.",
- Long: `Return self enode url.`,
- Args: cobra.NoArgs,
- Run: func(cmd *cobra.Command, args []string) {
- var result string
- clientCall(ipcEndpoint, &result, "p2p_selfNode")
- printJSON(result)
+ &cobra.Command{
+ Use: "badlist",
+ Short: "Return bad nodes list.",
+ Long: `Return bad nodes list.`,
+ Args: cobra.NoArgs,
+ Run: commonCall("p2p_badNodes"),
+ },
+
+ &cobra.Command{
+ Use: "selfnode",
+ Short: "Return self enode url.",
+ Long: `Return self enode url.`,
+ Args: cobra.NoArgs,
+ Run: commonCall("p2p_selfNode"),
+ },
+
+ &cobra.Command{
+ Use: "seednodes",
+ Short: "Return seed enode url.",
+ Long: `Return seed enode url.`,
+ Args: cobra.NoArgs,
+ Run: commonCall("p2p_seedNodes"),
},
}
func init() {
RootCmd.AddCommand(p2pCmd)
- p2pCmd.AddCommand(addCmd, removeCmd, addtrustedCmd, removetrustedCmd,
- addbadCmd, removebadCmd, countCmd, listCmd, badcountCmd, badlistCmd, selfnodeCmd)
+ p2pCmd.AddCommand(p2pSubCmds...)
p2pCmd.PersistentFlags().StringVarP(&ipcEndpoint, "ipcpath", "i", defaultIPCEndpoint(params.ClientIdentifier), "IPC Endpoint path")
}
diff --git a/cmd/ft/root.go b/cmd/ft/root.go
index a5ec539d..cf9e4d30 100644
--- a/cmd/ft/root.go
+++ b/cmd/ft/root.go
@@ -94,6 +94,7 @@ var RootCmd = &cobra.Command{
}
func makeNode() (*node.Node, error) {
+ genesis := blockchain.DefaultGenesis()
// set miner config
SetupMetrics()
// Make sure we have a valid genesis JSON
@@ -105,13 +106,18 @@ func makeNode() (*node.Node, error) {
}
defer file.Close()
- genesis := blockchain.DefaultGenesis()
if err := json.NewDecoder(file).Decode(genesis); err != nil {
return nil, fmt.Errorf("invalid genesis file: %v(%v)", ftCfgInstance.GenesisFile, err)
}
ftCfgInstance.FtServiceCfg.Genesis = genesis
- }
+ }
+ block, _, err := genesis.ToBlock(nil)
+ if err != nil {
+ return nil, err
+ }
+ // p2p used to generate MagicNetID
+ ftCfgInstance.NodeCfg.P2PConfig.GenesisHash = block.Hash()
return node.New(ftCfgInstance.NodeCfg)
}
diff --git a/cmd/ft/txpool.go b/cmd/ft/txpool.go
index 2d16e697..66bb1c8d 100644
--- a/cmd/ft/txpool.go
+++ b/cmd/ft/txpool.go
@@ -32,10 +32,17 @@ var contentCmd = &cobra.Command{
Use: "content ",
Short: "Returns the transactions contained within the transaction pool.",
Long: `Returns the transactions contained within the transaction pool.`,
- Args: cobra.ExactArgs(1),
+ Args: cobra.RangeArgs(0, 1),
Run: func(cmd *cobra.Command, args []string) {
- var result interface{}
- clientCall(ipcEndpoint, &result, "txpool_content", parseBool(args[0]))
+ var (
+ result interface{}
+ fullTx bool
+ )
+ if len(args) == 1 {
+ fullTx = parseBool(args[0])
+ }
+
+ clientCall(ipcEndpoint, &result, "txpool_content", fullTx)
printJSON(result)
},
}
@@ -64,16 +71,37 @@ var setGasPriceCmd = &cobra.Command{
},
}
-var getTxCmd = &cobra.Command{
- Use: "gettx ",
+var getTxsCmd = &cobra.Command{
+ Use: "gettxs ",
Short: "Returns the transaction for the given hash",
Long: `Returns the transaction for the given hash`,
+ Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
var result []*types.RPCTransaction
- clientCall(ipcEndpoint, &result, "txpool_getPoolTransactions", args)
+ clientCall(ipcEndpoint, &result, "txpool_getTransactions", args)
printJSONList(result)
},
}
+
+var getTxsByAccountCmd = &cobra.Command{
+ Use: "gettxsbyname ",
+ Short: "Returns the transaction for the given account",
+ Long: `Returns the transaction for the given account`,
+ Args: cobra.RangeArgs(1, 2),
+ Run: func(cmd *cobra.Command, args []string) {
+ var (
+ result interface{}
+ fullTx bool
+ )
+ if len(args) > 1 {
+ fullTx = parseBool(args[1])
+ }
+
+ clientCall(ipcEndpoint, &result, "txpool_getTransactionsByAccount", args[0], fullTx)
+ printJSON(result)
+ },
+}
+
var getPendingTxsCmd = &cobra.Command{
Use: "getpending ",
Short: "Returns the pending transactions that are in the transaction pool",
@@ -88,6 +116,6 @@ var getPendingTxsCmd = &cobra.Command{
func init() {
RootCmd.AddCommand(txpoolCommand)
- txpoolCommand.AddCommand(contentCmd, statusCmd, setGasPriceCmd, getTxCmd, getPendingTxsCmd)
+ txpoolCommand.AddCommand(contentCmd, statusCmd, setGasPriceCmd, getTxsCmd, getTxsByAccountCmd, getPendingTxsCmd)
txpoolCommand.PersistentFlags().StringVarP(&ipcEndpoint, "ipcpath", "i", defaultIPCEndpoint(params.ClientIdentifier), "IPC Endpoint path")
}
diff --git a/cmd/ft/utils.go b/cmd/ft/utils.go
index cdab5739..5174b05d 100644
--- a/cmd/ft/utils.go
+++ b/cmd/ft/utils.go
@@ -101,6 +101,20 @@ var tomlSettings = toml.Config{
},
}
+func clientCallRaw(endpoint string, method string, args ...interface{}) json.RawMessage {
+ client, err := dialRPC(ipcEndpoint)
+ if err != nil {
+ jww.ERROR.Println(err)
+ os.Exit(-1)
+ }
+ msg, err := client.CallRaw(method, args...)
+ if err != nil {
+ jww.ERROR.Println(err)
+ os.Exit(-1)
+ }
+ return msg
+}
+
func clientCall(endpoint string, result interface{}, method string, args ...interface{}) {
client, err := dialRPC(ipcEndpoint)
if err != nil {
diff --git a/cmd/ftfinder/root.go b/cmd/ftfinder/root.go
index 229e274f..0af09024 100644
--- a/cmd/ftfinder/root.go
+++ b/cmd/ftfinder/root.go
@@ -27,11 +27,14 @@ import (
"github.com/fractalplatform/fractal/common"
"github.com/fractalplatform/fractal/node"
"github.com/fractalplatform/fractal/p2p"
+ "github.com/fractalplatform/fractal/rpc"
"github.com/spf13/cobra"
)
var nodeConfig = node.Config{
- P2PConfig: &p2p.Config{},
+ P2PConfig: &p2p.Config{},
+ IPCPath: "ftfinder.ipc",
+ P2PNodeDatabase: "nodedb",
}
// RootCmd represents the base command when called without any subcommands
@@ -49,19 +52,38 @@ var RootCmd = &cobra.Command{
nodeConfig.P2PConfig.GenesisHash = common.HexToHash(hexStr)
nodeConfig.P2PConfig.Logger = log.New()
nodeConfig.P2PConfig.NodeDatabase = nodeConfig.NodeDB()
- srv := p2p.Server{
+ srv := &p2p.Server{
Config: nodeConfig.P2PConfig,
}
for i, n := range srv.Config.BootstrapNodes {
fmt.Println(i, n.String())
}
- srv.DiscoverOnly()
+ err := srv.DiscoverOnly()
+ defer srv.Stop()
+ if err != nil {
+ log.Error("ftfinder start failed", "error", err)
+ return
+ }
+ rpcListener, rpcHandler, err := rpc.StartIPCEndpoint(nodeConfig.IPCEndpoint(), []rpc.API{
+ rpc.API{
+ Namespace: "p2p",
+ Version: "1.0",
+ Service: &FinderRPC{srv},
+ Public: false,
+ },
+ })
+ if err != nil {
+ log.Error("ipc start failed", "error", err)
+ return
+ }
+ defer rpcHandler.Stop()
+ defer rpcListener.Close()
+
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
defer signal.Stop(sigc)
<-sigc
log.Info("Got interrupt, shutting down...")
- srv.Stop()
},
}
diff --git a/cmd/ftfinder/rpc.go b/cmd/ftfinder/rpc.go
new file mode 100644
index 00000000..87909077
--- /dev/null
+++ b/cmd/ftfinder/rpc.go
@@ -0,0 +1,23 @@
+package main
+
+import (
+ "github.com/fractalplatform/fractal/p2p/enode"
+)
+
+type Backend interface {
+ SeedNodes() []*enode.Node
+}
+
+type FinderRPC struct {
+ b Backend
+}
+
+// SeedNodes returns all seed nodes.
+func (fr *FinderRPC) SeedNodes() []string {
+ nodes := fr.b.SeedNodes()
+ ns := make([]string, len(nodes))
+ for i, node := range nodes {
+ ns[i] = node.String()
+ }
+ return ns
+}
diff --git a/cmd/utils/version.go b/cmd/utils/version.go
index abbb258f..4afadec7 100644
--- a/cmd/utils/version.go
+++ b/cmd/utils/version.go
@@ -48,7 +48,9 @@ func version() {
fmt.Println("Go Version:", goversion)
}
fmt.Println("Operating System:", runtime.GOOS)
- fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
+ if goPath := os.Getenv("GOPATH"); goPath != "" {
+ fmt.Printf("GOPATH=%s\n", goPath)
+ }
fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
}
@@ -59,7 +61,7 @@ func FullVersion() string {
version += "+commit." + commit
}
if date != "" {
- version += "+" + date
+ version += "+date." + date
}
return version
}
diff --git a/common/address.go b/common/address.go
index 3eddea5b..9ec53a01 100644
--- a/common/address.go
+++ b/common/address.go
@@ -55,7 +55,7 @@ func HexToAddress(s string) Address { return BytesToAddress(FromHex(s)) }
// IsHexAddress verifies whether a string can represent a valid hex-encoded
// address or not.
func IsHexAddress(s string) bool {
- if hasHexPrefix(s) {
+ if has0xPrefix(s) {
s = s[2:]
}
return len(s) == 2*AddressLength && isHex(s)
@@ -113,7 +113,8 @@ func (a *Address) SetBytes(b []byte) {
copy(a[AddressLength-len(b):], b)
}
-// MarshalText returns the hex representation of a.
+// MarshalText returns the hex representation of a. Implements encoding.TextMarshaler
+// is supported by most codec implementations (e.g. for yaml or toml).
func (a Address) MarshalText() ([]byte, error) {
return hexutil.Bytes(a[:]).MarshalText()
}
@@ -140,7 +141,8 @@ func (a *UnprefixedAddress) UnmarshalText(input []byte) error {
return hexutil.UnmarshalFixedUnprefixedText("UnprefixedAddress", input, a[:])
}
-// MarshalText encodes the address as hex.
+// MarshalText encodes the address as hex.Implements encoding.TextMarshaler
+// is supported by most codec implementations (e.g. for yaml or toml).
func (a UnprefixedAddress) MarshalText() ([]byte, error) {
return []byte(hex.EncodeToString(a[:])), nil
}
diff --git a/common/address_test.go b/common/address_test.go
index e3ba715c..40e9b4b9 100644
--- a/common/address_test.go
+++ b/common/address_test.go
@@ -17,10 +17,13 @@
package common
import (
+ "bytes"
"encoding/json"
-
+ "math"
"math/big"
"testing"
+
+ "gopkg.in/yaml.v2"
)
func TestIsHexAddress(t *testing.T) {
@@ -102,6 +105,88 @@ func TestAddressHexChecksum(t *testing.T) {
}
}
+func TestAddressConvert(t *testing.T) {
+ var tests = []struct {
+ BigInt *big.Int
+ HexString string
+ Bytes []byte
+ }{
+ {big.NewInt(math.MaxInt64),
+ "0x0000000000000000000000007FFfFFFffFfFfFff",
+ []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 255, 255, 255, 255, 255}},
+ {big.NewInt(10),
+ "0x000000000000000000000000000000000000000A",
+ []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10}},
+ {big.NewInt(15),
+ "0x000000000000000000000000000000000000000F",
+ []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15}},
+ }
+ for i, test := range tests {
+ addr := BigToAddress(test.BigInt)
+ if addr.Hex() != test.HexString {
+ t.Errorf("test #%d: failed to match when it should (%s != %s)", i, addr.Hex(), test.HexString)
+ }
+ if !bytes.Equal(addr.Bytes(), test.Bytes) {
+ t.Errorf("test #%d: failed to match when it should (%x != %x)", i, addr.Bytes(), test.Bytes)
+ }
+ }
+}
+
+func TestAddressMarshal(t *testing.T) {
+ testAddr := HexToAddress("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed")
+ if marshaltext, err := yaml.Marshal(testAddr); err != nil {
+ t.Errorf("MarshalText err: %v", err)
+ } else {
+ target := []byte{48, 120, 53, 97, 97, 101, 98, 54, 48, 53, 51, 102, 51, 101, 57, 52, 99, 57, 98, 57, 97, 48, 57, 102, 51, 51, 54, 54, 57, 52, 51, 53, 101, 55, 101, 102, 49, 98, 101, 97, 101, 100, 10}
+ if !bytes.Equal(marshaltext, target) {
+ t.Errorf("MarshalText mismatch when it should (%x != %x)", marshaltext, target)
+ }
+
+ newAddress := Address{}
+ if err := yaml.Unmarshal(marshaltext, &newAddress); err != nil {
+ t.Errorf("UnmarshalText err: %v", err)
+ }
+ if 0 != newAddress.Compare(testAddr) {
+ t.Errorf("UnmarshalText address mismatch")
+ }
+ }
+}
+
+func TestUnprefixedAddressMarshal(t *testing.T) {
+ marshaltext := []byte{53, 97, 97, 101, 98, 54, 48, 53, 51, 102, 51, 101, 57, 52, 99, 57, 98, 57, 97, 48, 57, 102, 51, 51, 54, 54, 57, 52, 51, 53, 101, 55, 101, 102, 49, 98, 101, 97, 101, 100, 10}
+ unprefixedAddr := UnprefixedAddress{}
+ if err := yaml.Unmarshal(marshaltext, &unprefixedAddr); err != nil {
+ t.Errorf("UnmarshalText err: %v", err)
+ }
+
+ if fetchedMarshaltext, err := yaml.Marshal(unprefixedAddr); err != nil {
+ t.Errorf("MarshalText err: %v", err)
+ } else {
+ if !bytes.Equal(marshaltext, fetchedMarshaltext) {
+ t.Errorf("MarshalText mismatch when it should (%s != %s)", marshaltext, fetchedMarshaltext)
+ }
+ }
+}
+
+func TestMixedcaseAddress(t *testing.T) {
+ hexString := "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed"
+ originalAddr := HexToAddress(hexString)
+ testAddr := NewMixedcaseAddress(originalAddr)
+ mixedcaseRes := "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed [chksum ok]"
+ if mixedcaseRes != testAddr.String() {
+ t.Errorf("MixedcaseAddress String mismatched")
+ }
+
+ mixedcaseRes = "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
+ if mixedcaseRes != testAddr.Original() {
+ t.Errorf("MixedcaseAddress Original mismatched")
+ }
+
+ if 0 != testAddr.Address().Compare(originalAddr) {
+ t.Errorf("MixedcaseAddress address mismatch")
+ }
+}
+
func BenchmarkAddressHex(b *testing.B) {
testAddr := HexToAddress("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed")
for n := 0; n < b.N; n++ {
diff --git a/common/author.go b/common/author.go
index a205e2b2..f4d71285 100644
--- a/common/author.go
+++ b/common/author.go
@@ -133,15 +133,7 @@ func (a *Author) decode(sa *StorageAuthor) error {
}
func (a *Author) MarshalJSON() ([]byte, error) {
- switch aTy := a.Owner.(type) {
- case Name:
- return json.Marshal(&AuthorJSON{authorType: AccountNameType, OwnerStr: aTy.String(), Weight: a.Weight})
- case PubKey:
- return json.Marshal(&AuthorJSON{authorType: PubKeyType, OwnerStr: aTy.String(), Weight: a.Weight})
- case Address:
- return json.Marshal(&AuthorJSON{authorType: AddressType, OwnerStr: aTy.String(), Weight: a.Weight})
- }
- return nil, errors.New("Author marshal failed")
+ return json.Marshal(&AuthorJSON{OwnerStr: a.Owner.String(), Weight: a.Weight})
}
func (a *Author) UnmarshalJSON(data []byte) error {
@@ -149,16 +141,7 @@ func (a *Author) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, aj); err != nil {
return err
}
- switch aj.authorType {
- case AccountNameType:
- a.Owner = Name(aj.OwnerStr)
- a.Weight = aj.Weight
- case PubKeyType:
- a.Owner = HexToPubKey(aj.OwnerStr)
- a.Weight = aj.Weight
- case AddressType:
- a.Owner = HexToAddress(aj.OwnerStr)
- a.Weight = aj.Weight
- }
+ a.Owner = Name(aj.OwnerStr)
+ a.Weight = aj.Weight
return nil
}
diff --git a/common/author_test.go b/common/author_test.go
index 2963a544..643684eb 100644
--- a/common/author_test.go
+++ b/common/author_test.go
@@ -51,11 +51,12 @@ func TestAuthorMarshalAndUnMarshal(t *testing.T) {
inputAuthor *Author
}{
{&Author{Owner: Name("test"), Weight: 1}},
- {&Author{Owner: HexToPubKey("test"), Weight: 1}},
- {&Author{Owner: HexToAddress("test"), Weight: 1}},
+ {&Author{Owner: HexToPubKey("123455"), Weight: 1}},
+ {&Author{Owner: HexToAddress("13123123123"), Weight: 1}},
}
for _, test := range tests {
authorBytes, err := json.Marshal(test.inputAuthor)
+
if err != nil {
t.Fatal(err)
}
diff --git a/common/bytes.go b/common/bytes.go
index 880841ec..5f456ab3 100644
--- a/common/bytes.go
+++ b/common/bytes.go
@@ -17,11 +17,7 @@
// Package common contains various helper functions.
package common
-import (
- "encoding/binary"
- "encoding/hex"
- "io"
-)
+import "encoding/hex"
// ToHex returns the hex representation of b, prefixed with '0x'.
// For empty slices, the return value is "0x0".
@@ -35,13 +31,20 @@ func ToHex(b []byte) string {
return "0x" + hex
}
+// ToHexArray creates a array of hex-string based on []byte
+func ToHexArray(b [][]byte) []string {
+ r := make([]string, len(b))
+ for i := range b {
+ r[i] = ToHex(b[i])
+ }
+ return r
+}
+
// FromHex returns the bytes represented by the hexadecimal string s.
// s may be prefixed with "0x".
func FromHex(s string) []byte {
- if len(s) > 1 {
- if s[0:2] == "0x" || s[0:2] == "0X" {
- s = s[2:]
- }
+ if has0xPrefix(s) {
+ s = s[2:]
}
if len(s)%2 == 1 {
s = "0" + s
@@ -60,8 +63,8 @@ func CopyBytes(b []byte) (copiedBytes []byte) {
return
}
-// hasHexPrefix validates str begins with '0x' or '0X'.
-func hasHexPrefix(str string) bool {
+// has0xPrefix validates str begins with '0x' or '0X'.
+func has0xPrefix(str string) bool {
return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')
}
@@ -104,7 +107,7 @@ func Hex2BytesFixed(str string, flen int) []byte {
return h[len(h)-flen:]
}
hh := make([]byte, flen)
- copy(hh[flen-len(h):flen], h[:])
+ copy(hh[flen-len(h):flen], h)
return hh
}
@@ -132,29 +135,13 @@ func LeftPadBytes(slice []byte, l int) []byte {
return padded
}
-func ReadVarInt(r io.Reader) (uint64, error) {
- var (
- count = make([]byte, 1)
- buf []byte
- err error
- )
- if n, err := r.Read(count); err != nil {
- return uint64(n), err
- }
- switch count[0] {
- case 0xFD:
- buf := make([]byte, 2)
- _, err = io.ReadFull(r, buf)
- return (uint64)(binary.LittleEndian.Uint16(buf)), err
- case 0xFE:
- buf = make([]byte, 4)
- _, err = io.ReadFull(r, buf)
- return (uint64)(binary.LittleEndian.Uint32(buf)), err
- case 0xFF:
- buf = make([]byte, 8)
- _, err = io.ReadFull(r, buf)
- return binary.LittleEndian.Uint64(buf), err
- default:
- return uint64(uint8(count[0])), err
+// TrimLeftZeroes returns a subslice of s without leading zeroes
+func TrimLeftZeroes(s []byte) []byte {
+ idx := 0
+ for ; idx < len(s); idx++ {
+ if s[idx] != 0 {
+ break
+ }
}
+ return s[idx:]
}
diff --git a/common/bytes_test.go b/common/bytes_test.go
index a8976a94..ae48391c 100644
--- a/common/bytes_test.go
+++ b/common/bytes_test.go
@@ -103,3 +103,10 @@ func TestNoPrefixShortHexOddLength(t *testing.T) {
t.Errorf("Expected %x got %x", expected, result)
}
}
+
+func BenchmarkCutCustomTrim(b *testing.B) {
+ value := HexToHash("0x01")
+ for i := 0; i < b.N; i++ {
+ TrimLeftZeroes(value[:])
+ }
+}
diff --git a/common/gaspool_test.go b/common/gaspool_test.go
index 9e13e846..cd89f9d6 100644
--- a/common/gaspool_test.go
+++ b/common/gaspool_test.go
@@ -17,6 +17,7 @@
package common
import (
+ "fmt"
"math"
"testing"
)
@@ -55,4 +56,5 @@ func TestSubGas(t *testing.T) {
if err := gaspool.SubGas(uint64(2)); err == nil || err != ErrGasLimitReached {
t.Fatalf("expect ErrGasLimitReached")
}
+ fmt.Println(gaspool)
}
diff --git a/common/hash.go b/common/hash.go
index 64578976..68fd2a0f 100644
--- a/common/hash.go
+++ b/common/hash.go
@@ -20,7 +20,6 @@ import (
"encoding/hex"
"fmt"
"math/big"
- "math/rand"
"reflect"
"github.com/ethereum/go-ethereum/common/hexutil"
@@ -87,7 +86,8 @@ func (h *Hash) UnmarshalJSON(input []byte) error {
return hexutil.UnmarshalFixedJSON(hashT, input, h[:])
}
-// MarshalText returns the hex representation of h.
+// MarshalText returns the hex representation of h.Implements encoding.TextMarshaler
+// is supported by most codec implementations (e.g. for yaml or toml).
func (h Hash) MarshalText() ([]byte, error) {
return hexutil.Bytes(h[:]).MarshalText()
}
@@ -102,15 +102,6 @@ func (h *Hash) SetBytes(b []byte) {
copy(h[HashLength-len(b):], b)
}
-// Generate implements testing/quick.Generator.
-func (h Hash) Generate(rand *rand.Rand, size int) reflect.Value {
- m := rand.Intn(len(h))
- for i := len(h) - 1; i > m; i-- {
- h[i] = byte(rand.Uint32())
- }
- return reflect.ValueOf(h)
-}
-
// UnprefixedHash allows marshaling a Hash without 0x prefix.
type UnprefixedHash Hash
@@ -119,7 +110,8 @@ func (h *UnprefixedHash) UnmarshalText(input []byte) error {
return hexutil.UnmarshalFixedUnprefixedText("UnprefixedHash", input, h[:])
}
-// MarshalText encodes the hash as hex.
+// MarshalText encodes the hash as hex. Implements encoding.TextMarshaler
+// is supported by most codec implementations (e.g. for yaml or toml).
func (h UnprefixedHash) MarshalText() ([]byte, error) {
return []byte(hex.EncodeToString(h[:])), nil
}
diff --git a/common/path.go b/common/path.go
deleted file mode 100644
index bd7bf44b..00000000
--- a/common/path.go
+++ /dev/null
@@ -1,39 +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 common
-
-import (
- "os"
- "path/filepath"
-)
-
-// FileExist checks if a file exists at filePath.
-func FileExist(filePath string) bool {
- _, err := os.Stat(filePath)
- if err != nil && os.IsNotExist(err) {
- return false
- }
- return true
-}
-
-// AbsolutePath returns datadir + filename, or filename if it is absolute.
-func AbsolutePath(datadir string, filename string) string {
- if filepath.IsAbs(filename) {
- return filename
- }
- return filepath.Join(datadir, filename)
-}
diff --git a/common/pubkey.go b/common/pubkey.go
index 1251ce2a..e87f28ba 100644
--- a/common/pubkey.go
+++ b/common/pubkey.go
@@ -42,7 +42,7 @@ func BytesToPubKey(b []byte) PubKey {
// IsHexPubKey verifies whether a string can represent a valid hex-encoded
// PubKey or not.
func IsHexPubKey(s string) bool {
- if hasHexPrefix(s) {
+ if has0xPrefix(s) {
s = s[2:]
}
return len(s) == 2*(PubKeyLength) && isHex(s)
@@ -73,7 +73,8 @@ func (p PubKey) String() string {
return p.Hex()
}
-// MarshalText returns the hex representation of a.
+// MarshalText returns the hex representation of p. Implements encoding.TextMarshaler
+// is supported by most codec implementations (e.g. for yaml or toml).
func (p PubKey) MarshalText() ([]byte, error) {
return hexutil.Bytes(p[:]).MarshalText()
}
@@ -94,3 +95,6 @@ func (p *PubKey) UnmarshalJSON(input []byte) error {
func (p PubKey) Compare(x PubKey) int {
return bytes.Compare(p.Bytes(), x.Bytes())
}
+
+// EmptyPubKey empty
+var EmptyPubKey PubKey
diff --git a/consensus/dpos/api.go b/consensus/dpos/api.go
index 476cdaf9..88609418 100644
--- a/consensus/dpos/api.go
+++ b/consensus/dpos/api.go
@@ -227,6 +227,7 @@ func (api *API) BrowserAllEpoch() (interface{}, error) {
if err != nil {
return nil, err
}
+
for {
data := &Epoch{}
timestamp := sys.config.epochTimeStamp(epochNumber)
@@ -234,18 +235,36 @@ func (api *API) BrowserAllEpoch() (interface{}, error) {
if err != nil {
return nil, err
}
- if sys.config.epoch(sys.config.ReferenceTime) == gstate.PreEpoch {
- timestamp = sys.config.epochTimeStamp(gstate.PreEpoch)
+ if gstate.PreEpoch == gstate.Epoch {
+ timestamp = sys.config.ReferenceTime
}
-
data.Start = timestamp / 1000000000
data.Epoch = epochNumber
epochs.Data = append(epochs.Data, data)
- if epochNumber == 1 {
+ if gstate.PreEpoch == gstate.Epoch {
break
}
epochNumber = gstate.PreEpoch
}
+ // for {
+ // data := &Epoch{}
+ // timestamp := sys.config.epochTimeStamp(epochNumber)
+ // gstate, err := sys.GetState(epochNumber)
+ // if err != nil {
+ // return nil, err
+ // }
+ // if sys.config.epoch(sys.config.ReferenceTime) == gstate.PreEpoch {
+ // timestamp = sys.config.epochTimeStamp(gstate.PreEpoch)
+ // }
+
+ // data.Start = timestamp / 1000000000
+ // data.Epoch = epochNumber
+ // epochs.Data = append(epochs.Data, data)
+ // if epochNumber == 1 {
+ // break
+ // }
+ // epochNumber = gstate.PreEpoch
+ // }
return epochs, nil
}
@@ -326,7 +345,7 @@ func (api *API) BrowserEpochRecord(reqEpochNumber uint64) (interface{}, error) {
}
}
candidateInfo.Holder = balance.String()
- // candidateInfo.URL = tmp.URL
+ // candidateInfo.Info = tmp.Info
candidateInfos.Data = append(candidateInfos.Data, candidateInfo)
}
@@ -415,7 +434,7 @@ func (api *API) BrowserVote(reqEpochNumber uint64) (interface{}, error) {
candidateInfo.Quantity = c.Quantity.Mul(c.Quantity, api.dpos.config.unitStake()).String()
candidateInfo.TotalQuantity = c.TotalQuantity.String()
- // candidateInfo.URL = c.URL
+ // candidateInfo.Info = c.Info
candidateInfo.Holder = balance.String()
tmp, err := sys.GetCandidate(req, c.Name)
@@ -435,28 +454,37 @@ func (api *API) BrowserVote(reqEpochNumber uint64) (interface{}, error) {
// BrowserAllEpoch2 get all epoch info for browser api
func (api *API) BrowserAllEpoch2() (interface{}, error) {
- epochs := Epochs{}
- epochs.Data = make([]*Epoch, 0)
+ epochs := VoteEpochs{}
+ epochs.Data = make([]*VoteEpoch, 0)
epochNumber, _ := api.epoch(api.chain.CurrentHeader().Number.Uint64())
sys, err := api.system()
if err != nil {
return nil, err
}
for {
- data := &Epoch{}
+ data := &VoteEpoch{}
timestamp := sys.config.epochTimeStamp(epochNumber)
gstate, err := sys.GetState(epochNumber)
if err != nil {
return nil, err
}
- if sys.config.epoch(sys.config.ReferenceTime) == gstate.PreEpoch {
- timestamp = sys.config.epochTimeStamp(gstate.PreEpoch)
+ if gstate.PreEpoch == gstate.Epoch {
+ timestamp = sys.config.ReferenceTime
+ }
+
+ dataEpoch, err := sys.GetState(gstate.PreEpoch)
+ if err != nil {
+ return nil, err
+ }
+
+ if dataEpoch.Dpos {
+ data.Dpos = 1
}
data.Start = timestamp / 1000000000
data.Epoch = epochNumber + 1
epochs.Data = append(epochs.Data, data)
- if epochNumber == 1 {
+ if gstate.PreEpoch == gstate.Epoch {
break
}
epochNumber = gstate.PreEpoch
@@ -542,7 +570,7 @@ func (api *API) VoterInfo(reqEpochNumber uint64) (interface{}, error) {
tmp := c.Quantity.Mul(c.Quantity, api.dpos.config.unitStake())
voter.Quantity = tmp.Div(tmp, declimsBigInt).String()
voter.TotalQuantity = c.TotalQuantity.String()
- voter.URL = c.URL
+ voter.Info = c.Info
voter.Holder = balance.Div(balance, declimsBigInt).String()
data = append(data, voter)
diff --git a/consensus/dpos/config_test.go b/consensus/dpos/config_test.go
index 146f643f..9d51e479 100644
--- a/consensus/dpos/config_test.go
+++ b/consensus/dpos/config_test.go
@@ -51,6 +51,10 @@ func TestConfig(t *testing.T) {
panic(fmt.Errorf("Config consensusSize mismatch"))
}
+ if 3 != DefaultConfig.consensusSize() {
+ panic(fmt.Errorf("Config Cache consensusSize mismatch"))
+ }
+
if 0 != DefaultConfig.slot(1567591745) {
panic(fmt.Errorf("Config slot mismatch"))
}
@@ -63,6 +67,10 @@ func TestConfig(t *testing.T) {
panic(fmt.Errorf("Config getoffset mismatch"))
}
+ if 0 != DefaultConfig.getoffset(1567591745, 2) {
+ panic(fmt.Errorf("Config getoffset mismatch"))
+ }
+
if 15639786 != DefaultConfig.epoch(1567591745) {
panic(fmt.Errorf("Config epoch mismatch"))
}
@@ -71,7 +79,11 @@ func TestConfig(t *testing.T) {
panic(fmt.Errorf("Config epochTimeStamp mismatch"))
}
- if 0 != DefaultConfig.shouldCounter(1567591745, 1567591745) {
+ if 0 != DefaultConfig.shouldCounter(1567591745000, 1567591745000) {
+ panic(fmt.Errorf("Config epochTimeStamp mismatch"))
+ }
+
+ if 3 != DefaultConfig.shouldCounter(1521370523000, 1567591745000) {
panic(fmt.Errorf("Config epochTimeStamp mismatch"))
}
@@ -86,4 +98,9 @@ func TestConfig(t *testing.T) {
if err := DefaultConfig.IsValid(); err != nil {
panic(fmt.Errorf("Config IsValid err %v", err))
}
+
+ DefaultConfig.epochInter.Store(uint64(1070000))
+ if err := DefaultConfig.IsValid(); err == nil {
+ panic(fmt.Errorf("Config IsValid err %v", err))
+ }
}
diff --git a/consensus/dpos/database.go b/consensus/dpos/database.go
index d9347550..5dea20e9 100644
--- a/consensus/dpos/database.go
+++ b/consensus/dpos/database.go
@@ -23,6 +23,7 @@ import (
"math/big"
"strings"
+ "github.com/fractalplatform/fractal/common"
"github.com/fractalplatform/fractal/types"
)
@@ -54,12 +55,14 @@ type IDB interface {
GetTakeOver() (uint64, error)
Undelegate(string, *big.Int) (*types.Action, error)
- IncAsset2Acct(string, string, *big.Int) (*types.Action, error)
+ IncAsset2Acct(string, string, *big.Int, uint64) (*types.Action, error)
GetBalanceByTime(name string, timestamp uint64) (*big.Int, error)
GetCandidateInfoByTime(epoch uint64, name string, timestamp uint64) (*CandidateInfo, error)
+
+ CanMine(name string, pub []byte) error
}
-// CandidateType candiate status
+// CandidateType candidate status
type CandidateType uint64
const (
@@ -75,7 +78,8 @@ const (
Unkown
)
-// MarshalText returns the hex representation of a.
+// MarshalText returns the hex representation of a. Implements encoding.TextMarshaler
+// is supported by most codec implementations (e.g. for yaml or toml).
func (t CandidateType) MarshalText() ([]byte, error) {
return t.MarshalJSON()
}
@@ -127,7 +131,7 @@ func (t *CandidateType) UnmarshalJSON(data []byte) error {
type CandidateInfo struct {
Epoch uint64 `json:"epoch"`
Name string `json:"name"` // candidate name
- URL string `json:"url"` // candidate url
+ Info string `json:"info"` // candidate url
Quantity *big.Int `json:"quantity"` // candidate stake quantity
TotalQuantity *big.Int `json:"totalQuantity"` // candidate total stake quantity
Number uint64 `json:"number"` // timestamp
@@ -136,19 +140,21 @@ type CandidateInfo struct {
Type CandidateType `json:"type"`
PrevKey string `json:"-"`
NextKey string `json:"-"`
+ PubKey common.PubKey `json:"pubkey" rlp:"-"`
}
func (candidateInfo *CandidateInfo) copy() *CandidateInfo {
return &CandidateInfo{
Epoch: candidateInfo.Epoch,
Name: candidateInfo.Name,
- URL: candidateInfo.URL,
+ Info: candidateInfo.Info,
Quantity: candidateInfo.Quantity,
TotalQuantity: candidateInfo.TotalQuantity,
Number: candidateInfo.Number,
Counter: candidateInfo.Counter,
ActualCounter: candidateInfo.ActualCounter,
Type: candidateInfo.Type,
+ PubKey: candidateInfo.PubKey,
}
}
@@ -209,7 +215,7 @@ type CandidateInfoForBrowser struct {
ActualCounter uint64 `json:"actualCounter"`
NowCounter uint64 `json:"nowShouldCounter"`
NowActualCounter uint64 `json:"nowActualCounter"`
- // URL string `json:"url"`
+ // Info string `json:"info"`
// Status uint64 `json:"status"` //0:die 1:activate 2:spare
}
@@ -218,7 +224,7 @@ type VoterInfoFractal struct {
Holder string `json:"holder"`
Quantity string `json:"quantity"`
TotalQuantity string `json:"totalQuantity"`
- URL string `json:"url"`
+ Info string `json:"info"`
State uint64 `json:"state"`
Vote uint64 `json:"vote"`
CanVote bool `json:"canVote"`
@@ -238,6 +244,18 @@ type Epoch struct {
Epoch uint64 `json:"epoch"`
}
+// VoteEpochs array of epcho
+type VoteEpochs struct {
+ Data []*VoteEpoch `json:"data"`
+}
+
+// VoteEpoch timestamp & epoch number & dpos status
+type VoteEpoch struct {
+ Start uint64 `json:"start"`
+ Epoch uint64 `json:"epoch"`
+ Dpos uint64 `json:"dpos"`
+}
+
func (prods CandidateInfoArray) Len() int {
return len(prods)
}
diff --git a/consensus/dpos/database_test.go b/consensus/dpos/database_test.go
index 4703ec78..768e4d2e 100644
--- a/consensus/dpos/database_test.go
+++ b/consensus/dpos/database_test.go
@@ -20,16 +20,18 @@ import (
"fmt"
"math/big"
"testing"
+
+ yaml "gopkg.in/yaml.v2"
)
-func TestCandidateType(t *testing.T) {
+func TestCandidateMarshalText(t *testing.T) {
typeList := []CandidateType{Normal, Freeze, Black, Jail, Unkown}
for _, t := range typeList {
- if text, err := t.MarshalText(); err != nil {
+ if text, err := yaml.Marshal(t); err != nil {
panic(fmt.Errorf("MarshalText --- %v", err))
} else {
var newCt CandidateType
- if err := newCt.UnmarshalText(text); err != nil {
+ if err := yaml.Unmarshal(text, &newCt); err != nil {
panic(fmt.Errorf("UnmarshalText --- %v", err))
} else {
if newCt != t {
@@ -45,7 +47,7 @@ func TestCandidateInfo(t *testing.T) {
candidateInfo := &CandidateInfo{
Epoch: 1,
Name: "candidate1",
- URL: "",
+ Info: "",
Quantity: big.NewInt(100),
TotalQuantity: big.NewInt(100),
Number: 1,
diff --git a/consensus/dpos/dpos.go b/consensus/dpos/dpos.go
index a47f2e01..91676c63 100644
--- a/consensus/dpos/dpos.go
+++ b/consensus/dpos/dpos.go
@@ -86,20 +86,20 @@ func (s *stateDB) Undelegate(to string, amount *big.Int) (*types.Action, error)
}
return action, accountDB.TransferAsset(common.StrToName(s.name), common.StrToName(to), s.assetid, amount)
}
-func (s *stateDB) IncAsset2Acct(from string, to string, amount *big.Int) (*types.Action, error) {
+func (s *stateDB) IncAsset2Acct(from string, to string, amount *big.Int, forkID uint64) (*types.Action, error) {
action := types.NewAction(types.IncreaseAsset, common.StrToName(s.name), common.StrToName(to), 0, s.assetid, 0, amount, nil, nil)
accountDB, err := accountmanager.NewAccountManager(s.state)
if err != nil {
return action, err
}
- return action, accountDB.IncAsset2Acct(common.StrToName(from), common.StrToName(to), s.assetid, amount)
+ return action, accountDB.IncAsset2Acct(common.StrToName(from), common.StrToName(to), s.assetid, amount, forkID)
}
-func (s *stateDB) IsValidSign(name string, pubkey []byte) bool {
+func (s *stateDB) IsValidSign(name string, pubkey []byte) error {
accountDB, err := accountmanager.NewAccountManager(s.state)
if err != nil {
- return false
+ return err
}
- return accountDB.IsValidSign(common.StrToName(name), common.BytesToPubKey(pubkey)) == nil
+ return accountDB.IsValidSign(common.StrToName(name), common.BytesToPubKey(pubkey))
}
func (s *stateDB) GetBalanceByTime(name string, timestamp uint64) (*big.Int, error) {
accountDB, err := accountmanager.NewAccountManager(s.state)
@@ -135,7 +135,7 @@ func Genesis(cfg *Config, state *state.StateDB, timestamp uint64, number uint64)
if err := sys.SetCandidate(&CandidateInfo{
Epoch: epoch,
Name: cfg.SystemName,
- URL: cfg.SystemURL,
+ Info: cfg.SystemURL,
Quantity: big.NewInt(0),
TotalQuantity: big.NewInt(0),
Number: number,
@@ -531,7 +531,7 @@ func (dpos *Dpos) finalize0(chain consensus.IChainReader, header *types.Header,
counter := int64(0)
extraReward := new(big.Int).Mul(dpos.config.extraBlockReward(), big.NewInt(counter))
reward := new(big.Int).Add(dpos.config.blockReward(), extraReward)
- sys.IncAsset2Acct(dpos.config.SystemName, header.Coinbase.String(), reward)
+ sys.IncAsset2Acct(dpos.config.SystemName, header.Coinbase.String(), reward, header.CurForkID())
blk := types.NewBlock(header, txs, receipts)
@@ -689,7 +689,7 @@ func (dpos *Dpos) finalize1(chain consensus.IChainReader, header *types.Header,
extraCounter := int64(0)
extraReward := new(big.Int).Mul(dpos.config.extraBlockReward(), big.NewInt(extraCounter))
reward := new(big.Int).Add(dpos.config.blockReward(), extraReward)
- sys.IncAsset2Acct(dpos.config.SystemName, header.Coinbase.String(), reward)
+ sys.IncAsset2Acct(dpos.config.SystemName, header.Coinbase.String(), reward, header.CurForkID())
blk := types.NewBlock(header, txs, receipts)
// first hard fork at a specific number
@@ -789,20 +789,6 @@ func (dpos *Dpos) IsValidateCandidate(chain consensus.IChainReader, parent *type
return errInvalidMintBlockTime
}
- db := &stateDB{
- name: dpos.config.AccountName,
- state: state,
- }
- has := false
- for _, pubkey := range pubkeys {
- if db.IsValidSign(candidate, pubkey) {
- has = true
- }
- }
- if !has {
- return ErrIllegalCandidatePubKey
- }
-
sys := NewSystem(state, dpos.config)
pepoch := dpos.config.epoch(parent.Time.Uint64())
gstate, err := sys.GetState(pepoch)
@@ -1015,6 +1001,16 @@ func (dpos *Dpos) IsValidateCandidate(chain consensus.IChainReader, parent *type
if strings.Compare(tname, candidate) != 0 {
return fmt.Errorf("%v %v, except %v %v(%v) index %v (%v epoch) ", errInvalidBlockCandidate, candidate, tname, pstate.ActivatedCandidateSchedule, pstate.UsingCandidateIndexSchedule, offset, pstate.Epoch)
}
+
+ has := false
+ for _, pubkey := range pubkeys {
+ if sys.CanMine(candidate, pubkey) == nil {
+ has = true
+ }
+ }
+ if !has {
+ return ErrIllegalCandidatePubKey
+ }
return nil
}
diff --git a/consensus/dpos/ldb.go b/consensus/dpos/ldb.go
index da30689e..6aad1821 100644
--- a/consensus/dpos/ldb.go
+++ b/consensus/dpos/ldb.go
@@ -17,12 +17,14 @@
package dpos
import (
+ "bytes"
"encoding/binary"
"encoding/hex"
"fmt"
"math/big"
"strings"
+ "github.com/fractalplatform/fractal/common"
"github.com/fractalplatform/fractal/types"
"github.com/fractalplatform/fractal/utils/rlp"
)
@@ -34,9 +36,9 @@ type IDatabase interface {
Delete(key string) error
Undelegate(string, *big.Int) (*types.Action, error)
- IncAsset2Acct(string, string, *big.Int) (*types.Action, error)
+ IncAsset2Acct(string, string, *big.Int, uint64) (*types.Action, error)
GetBalanceByTime(name string, timestamp uint64) (*big.Int, error)
-
+ IsValidSign(name string, pubkey []byte) error
GetSnapshot(key string, timestamp uint64) ([]byte, error)
}
@@ -48,6 +50,9 @@ var (
// ActivatedCandidateKeyPrefix candidateInfo
ActivatedCandidateKeyPrefix = "ap"
+ // CandidatePubKeyPrefix candidateInfo
+ CandidatePubKeyPrefix = "pk"
+
// VoterKeyPrefix voterInfo
VoterKeyPrefix = "v"
// VoterHead head
@@ -80,6 +85,20 @@ func NewLDB(db IDatabase) (*LDB, error) {
return ldb, nil
}
+// CanMine allow mining block
+func (db *LDB) CanMine(name string, pub []byte) error {
+ pubkey := strings.Join([]string{CandidatePubKeyPrefix, fmt.Sprintf("%s", name)}, Separator)
+ if val, _ := db.Get(pubkey); val != nil {
+ if bytes.Compare(common.EmptyPubKey.Bytes(), val) != 0 {
+ if bytes.Compare(pub, val) == 0 {
+ return nil
+ }
+ return fmt.Errorf("need pubkey %s", common.BytesToPubKey(val).String())
+ }
+ }
+ return db.IsValidSign(name, pub)
+}
+
// SetCandidate update candidate info
func (db *LDB) SetCandidate(candidate *CandidateInfo) error {
if candidate.Name != CandidateHead && len(candidate.PrevKey) == 0 && len(candidate.NextKey) == 0 {
@@ -119,6 +138,18 @@ func (db *LDB) SetCandidate(candidate *CandidateInfo) error {
} else if err := db.Put(key, val); err != nil {
return err
}
+
+ if common.EmptyPubKey.Compare(candidate.PubKey) != 0 {
+ pubkey := strings.Join([]string{CandidatePubKeyPrefix, fmt.Sprintf("%s", candidate.Name)}, Separator)
+ if err := db.Put(pubkey, candidate.PubKey.Bytes()); err != nil {
+ return err
+ }
+ } else {
+ pubkey := strings.Join([]string{CandidatePubKeyPrefix, fmt.Sprintf("%s", candidate.Name)}, Separator)
+ if err := db.Delete(pubkey); err != nil {
+ return err
+ }
+ }
return nil
}
@@ -155,6 +186,11 @@ func (db *LDB) DelCandidate(epoch uint64, name string) error {
if err := db.Delete(key); err != nil {
return err
}
+ pubkey := strings.Join([]string{CandidatePubKeyPrefix, fmt.Sprintf("%s", name)}, Separator)
+ if err := db.Delete(pubkey); err != nil {
+ return err
+ }
+
head, err := db.GetCandidate(epoch, CandidateHead)
if err != nil {
return err
@@ -174,6 +210,12 @@ func (db *LDB) GetCandidate(epoch uint64, name string) (*CandidateInfo, error) {
} else if err := rlp.DecodeBytes(val, candidateInfo); err != nil {
return nil, err
}
+
+ pubkey := strings.Join([]string{CandidatePubKeyPrefix, fmt.Sprintf("%s", name)}, Separator)
+ if val, _ := db.Get(pubkey); val != nil {
+ candidateInfo.PubKey.SetBytes(val)
+ }
+
return candidateInfo, nil
}
diff --git a/consensus/dpos/ldb_test.go b/consensus/dpos/ldb_test.go
index 4144e6ad..626efa7e 100644
--- a/consensus/dpos/ldb_test.go
+++ b/consensus/dpos/ldb_test.go
@@ -61,7 +61,7 @@ func (ldb *levelDB) Delegate(string, *big.Int) error {
func (ldb *levelDB) Undelegate(string, *big.Int) (*types.Action, error) {
return nil, nil
}
-func (ldb *levelDB) IncAsset2Acct(string, string, *big.Int) (*types.Action, error) {
+func (ldb *levelDB) IncAsset2Acct(string, string, *big.Int, uint64) (*types.Action, error) {
return nil, nil
}
func (ldb *levelDB) GetSnapshot(string, uint64) ([]byte, error) {
@@ -70,6 +70,11 @@ func (ldb *levelDB) GetSnapshot(string, uint64) ([]byte, error) {
func (ldb *levelDB) GetBalanceByTime(name string, timestamp uint64) (*big.Int, error) {
return new(big.Int).Mul(big.NewInt(1000000000), DefaultConfig.decimals()), nil
}
+
+func (ldb *levelDB) IsValidSign(name string, pubkey []byte) error {
+ return nil
+}
+
func newTestLDB() (*levelDB, func()) {
dirname, err := ioutil.TempDir(os.TempDir(), "dpos_test_")
if err != nil {
@@ -119,7 +124,7 @@ func TestLDBCandidate(t *testing.T) {
candidateInfo := &CandidateInfo{
Epoch: uint64(index),
Name: candidate,
- URL: fmt.Sprintf("www.%v.com", candidate),
+ Info: fmt.Sprintf("www.%v.com", candidate),
Quantity: big.NewInt(0),
TotalQuantity: big.NewInt(0),
}
@@ -281,7 +286,7 @@ func TestLDBVoter(t *testing.T) {
candidateInfo := &CandidateInfo{
Epoch: 0,
Name: candidate,
- URL: fmt.Sprintf("www.%v.com", candidate),
+ Info: fmt.Sprintf("www.%v.com", candidate),
Quantity: big.NewInt(0),
TotalQuantity: big.NewInt(0),
}
diff --git a/consensus/dpos/processor.go b/consensus/dpos/processor.go
index 68dfa4bc..b6101c03 100644
--- a/consensus/dpos/processor.go
+++ b/consensus/dpos/processor.go
@@ -22,6 +22,7 @@ import (
"strings"
"github.com/fractalplatform/fractal/accountmanager"
+ "github.com/fractalplatform/fractal/common"
"github.com/fractalplatform/fractal/utils/rlp"
"github.com/fractalplatform/fractal/params"
@@ -31,12 +32,17 @@ import (
// RegisterCandidate candidate info
type RegisterCandidate struct {
- URL string
+ Info string
}
// UpdateCandidate candidate info
type UpdateCandidate struct {
- URL string
+ Info string
+}
+
+// UpdateCandidatePubKey candidate info
+type UpdateCandidatePubKey struct {
+ PubKey common.PubKey
}
// VoteCandidate vote info
@@ -66,7 +72,7 @@ func (dpos *Dpos) ProcessAction(fid uint64, number uint64, chainCfg *params.Chai
}
func (dpos *Dpos) processAction(fid uint64, number uint64, chainCfg *params.ChainConfig, state *state.StateDB, action *types.Action) ([]*types.InternalAction, error) {
- if err := action.Check(chainCfg); err != nil {
+ if err := action.Check(fid, chainCfg); err != nil {
return nil, err
}
sys := NewSystem(state, dpos.config)
@@ -95,7 +101,7 @@ func (dpos *Dpos) processAction(fid uint64, number uint64, chainCfg *params.Chai
if err := rlp.DecodeBytes(action.Data(), &arg); err != nil {
return nil, err
}
- if err := sys.RegCandidate(epoch, action.Sender().String(), arg.URL, action.Value(), number, fid); err != nil {
+ if err := sys.RegCandidate(epoch, action.Sender().String(), arg.Info, action.Value(), number, fid); err != nil {
return nil, err
}
case types.UpdateCandidate:
@@ -108,9 +114,25 @@ func (dpos *Dpos) processAction(fid uint64, number uint64, chainCfg *params.Chai
if err := rlp.DecodeBytes(action.Data(), &arg); err != nil {
return nil, err
}
- if err := sys.UpdateCandidate(epoch, action.Sender().String(), arg.URL, action.Value(), number, fid); err != nil {
+ if err := sys.UpdateCandidate(epoch, action.Sender().String(), arg.Info, action.Value(), number, fid); err != nil {
+ return nil, err
+ }
+ case types.UpdateCandidatePubKey:
+ if fid >= params.ForkID4 {
+ arg := &UpdateCandidatePubKey{}
+ if err := rlp.DecodeBytes(action.Data(), &arg); err != nil {
+ return nil, err
+ }
+ candidate, err := sys.GetCandidate(epoch, action.Sender().String())
+ if err != nil {
+ return nil, err
+ }
+ candidate.PubKey.SetBytes(arg.PubKey.Bytes())
+
+ err = sys.SetCandidate(candidate)
return nil, err
}
+ return nil, accountmanager.ErrUnKnownTxType
case types.UnregCandidate:
if strings.Compare(action.Sender().String(), dpos.config.SystemName) == 0 {
return nil, fmt.Errorf("no permission")
diff --git a/consensus/dpos/vote.go b/consensus/dpos/vote.go
index c7319659..e7ae3e86 100644
--- a/consensus/dpos/vote.go
+++ b/consensus/dpos/vote.go
@@ -96,7 +96,7 @@ func (sys *System) RegCandidate(epoch uint64, candidate string, url string, stak
prod = &CandidateInfo{
Epoch: epoch,
Name: candidate,
- URL: url,
+ Info: url,
Quantity: big.NewInt(0),
TotalQuantity: big.NewInt(0),
Number: number,
@@ -125,9 +125,9 @@ func (sys *System) RegCandidate(epoch uint64, candidate string, url string, stak
}
// UpdateCandidate update a candidate
-func (sys *System) UpdateCandidate(epoch uint64, candidate string, url string, nstake *big.Int, number uint64, fid uint64) error {
+func (sys *System) UpdateCandidate(epoch uint64, candidate string, info string, nstake *big.Int, number uint64, fid uint64) error {
// url validity
- if uint64(len(url)) > sys.config.MaxURLLen {
+ if uint64(len(info)) > sys.config.MaxURLLen {
return fmt.Errorf("invalid url (too long, max %v)", sys.config.MaxURLLen)
}
@@ -185,7 +185,7 @@ func (sys *System) UpdateCandidate(epoch uint64, candidate string, url string, n
// })
// }
- prod.URL = url
+ prod.Info = info
prod.Quantity = new(big.Int).Add(prod.Quantity, q)
prod.TotalQuantity = new(big.Int).Add(prod.TotalQuantity, q)
prod.Number = number
@@ -688,6 +688,8 @@ func (sys *System) UpdateElectedCandidates1(pepoch uint64, epoch uint64, number
activatedTotalQuantity = new(big.Int).Add(activatedTotalQuantity, candidateInfo.TotalQuantity)
}
}
+ gstate.ActivatedCandidateSchedule = activatedCandidateSchedule
+ gstate.ActivatedTotalQuantity = activatedTotalQuantity
} else {
tstate := &GlobalState{
Epoch: math.MaxUint64,
@@ -733,10 +735,11 @@ func (sys *System) UpdateElectedCandidates1(pepoch uint64, epoch uint64, number
index++
}
}
+ gstate.ActivatedCandidateSchedule = activatedCandidateSchedule
+ gstate.ActivatedTotalQuantity = activatedTotalQuantity
}
}
- gstate.ActivatedCandidateSchedule = activatedCandidateSchedule
- gstate.ActivatedTotalQuantity = activatedTotalQuantity
+
if err := sys.SetState(gstate); err != nil {
return err
}
diff --git a/consensus/miner/miner.go b/consensus/miner/miner.go
index 4664775b..782ea9e1 100644
--- a/consensus/miner/miner.go
+++ b/consensus/miner/miner.go
@@ -101,7 +101,6 @@ func (miner *Miner) Start(force bool) bool {
// Stop stop worker
func (miner *Miner) Stop() bool {
if !atomic.CompareAndSwapInt32(&miner.mining, 2, 3) {
- log.Error("miner already stopped")
return false
}
log.Info("Stopping mining operation")
diff --git a/consensus/miner/worker.go b/consensus/miner/worker.go
index 185ad7cc..cd0227bb 100644
--- a/consensus/miner/worker.go
+++ b/consensus/miner/worker.go
@@ -28,7 +28,6 @@ import (
"time"
"github.com/ethereum/go-ethereum/log"
- "github.com/fractalplatform/fractal/accountmanager"
"github.com/fractalplatform/fractal/blockchain"
"github.com/fractalplatform/fractal/common"
"github.com/fractalplatform/fractal/consensus"
@@ -121,23 +120,20 @@ func (worker *Worker) start(force bool) {
}
func (worker *Worker) mintLoop() {
- dpos, ok := worker.Engine().(*dpos.Dpos)
+ cdpos, ok := worker.Engine().(*dpos.Dpos)
if !ok {
panic("only support dpos engine")
}
- dpos.SetSignFn(func(content []byte, state *state.StateDB) ([]byte, error) {
- accountDB, err := accountmanager.NewAccountManager(state)
- if err != nil {
- return nil, err
- }
+ cdpos.SetSignFn(func(content []byte, state *state.StateDB) ([]byte, error) {
+ sys := dpos.NewSystem(state, cdpos.Config())
for index, privKey := range worker.privKeys {
- if err := accountDB.IsValidSign(common.StrToName(worker.coinbase), common.BytesToPubKey(worker.pubKeys[index])); err == nil {
+ if err := sys.CanMine(worker.coinbase, worker.pubKeys[index]); err == nil {
return crypto.Sign(content, privKey)
}
}
return nil, fmt.Errorf("not found match private key for sign")
})
- interval := int64(dpos.BlockInterval())
+ interval := int64(cdpos.BlockInterval())
c := make(chan time.Time)
to := time.Now()
worker.utimerTo(to.Add(time.Duration(interval-(to.UnixNano()%interval))), c)
@@ -156,7 +152,7 @@ func (worker *Worker) mintLoop() {
}
worker.wgWork.Wait()
worker.quitWork = make(chan struct{})
- timestamp := int64(dpos.Slot(uint64(now.UnixNano())))
+ timestamp := int64(cdpos.Slot(uint64(now.UnixNano())))
worker.wg.Add(1)
worker.wgWork.Add(1)
go func(quit chan struct{}) {
@@ -240,6 +236,9 @@ func (worker *Worker) setDelayDuration(delay uint64) error {
}
func (worker *Worker) setCoinbase(name string, privKeys []*ecdsa.PrivateKey) {
+ state, _ := worker.StateAt(worker.CurrentHeader().Root)
+ cdpos := worker.Engine().(*dpos.Dpos)
+ sys := dpos.NewSystem(state, cdpos.Config())
worker.mu.Lock()
defer worker.mu.Unlock()
worker.coinbase = name
@@ -247,7 +246,11 @@ func (worker *Worker) setCoinbase(name string, privKeys []*ecdsa.PrivateKey) {
worker.pubKeys = nil
for index, privkey := range privKeys {
pubkey := crypto.FromECDSAPub(&privkey.PublicKey)
- log.Info("setCoinbase", "coinbase", name, fmt.Sprintf("pubKey_%03d", index), common.BytesToPubKey(pubkey).String())
+ if err := sys.CanMine(name, pubkey); err == nil {
+ log.Info("setCoinbase[valid]", "coinbase", name, fmt.Sprintf("pubKey_%03d", index), common.BytesToPubKey(pubkey).String())
+ } else {
+ log.Warn("setCoinbase[invalid]", "coinbase", name, fmt.Sprintf("pubKey_%03d", index), common.BytesToPubKey(pubkey).String(), "detail", err)
+ }
worker.pubKeys = append(worker.pubKeys, pubkey)
}
}
@@ -364,6 +367,9 @@ func (worker *Worker) commitTransactions(work *Work, txs *types.TransactionsByPr
var coalescedLogs []*types.Log
endTimeStamp := work.currentHeader.Time.Uint64() + interval - 2*interval/5
endTime := time.Unix((int64)(endTimeStamp)/(int64)(time.Second), (int64)(endTimeStamp)%(int64)(time.Second))
+ t := work.currentHeader.Time.Uint64()
+ s := worker.Config().SnapshotInterval * uint64(time.Millisecond)
+ isSnapshot := t%s == 0
for {
select {
case <-worker.quit:
@@ -391,11 +397,22 @@ func (worker *Worker) commitTransactions(work *Work, txs *types.TransactionsByPr
action := tx.GetActions()[0]
+ if isSnapshot {
+ if action.Type() == types.RegCandidate ||
+ action.Type() == types.VoteCandidate {
+ log.Trace("Skipping regcandidate transaction when snapshot block", "hash", tx.Hash())
+ txs.Pop()
+ continue
+ }
+ }
+
if strings.Compare(work.currentHeader.Coinbase.String(), worker.Config().SysName) != 0 {
switch action.Type() {
case types.KickedCandidate:
fallthrough
case types.ExitTakeOver:
+ log.Trace("Skipping system transaction when not take over", "hash", tx.Hash())
+ txs.Pop()
continue
default:
}
diff --git a/event/router.go b/event/router.go
index 90063ccf..8c4ee4ea 100644
--- a/event/router.go
+++ b/event/router.go
@@ -195,7 +195,7 @@ func (router *Router) StationUnregister(station Station) {
router.stationMutex.Lock()
delete(router.stations, station.Name())
router.stationMutex.Unlock()
- if station.IsRemote() {
+ if station.IsRemote() && !station.IsBroadcast() {
router.eval.unregister(station)
}
}
diff --git a/ftservice/apibackend.go b/ftservice/apibackend.go
index aa75581e..2adf017a 100644
--- a/ftservice/apibackend.go
+++ b/ftservice/apibackend.go
@@ -336,6 +336,16 @@ func (b *APIBackend) RemoveTrustedPeer(url string) error {
return err
}
+// SeedNodes returns all seed nodes.
+func (b *APIBackend) SeedNodes() []string {
+ nodes := b.ftservice.p2pServer.SeedNodes()
+ ns := make([]string, len(nodes))
+ for i, node := range nodes {
+ ns[i] = node.String()
+ }
+ return ns
+}
+
// PeerCount returns the number of connected peers.
func (b *APIBackend) PeerCount() int {
return b.ftservice.p2pServer.PeerCount()
diff --git a/ftservice/ftservice.go b/ftservice/ftservice.go
index c3fa5f14..2a52d146 100644
--- a/ftservice/ftservice.go
+++ b/ftservice/ftservice.go
@@ -82,8 +82,6 @@ func New(ctx *node.ServiceContext, config *Config) (*FtService, error) {
if err != nil {
return nil, err
}
- // used to generate MagicNetID
- ftservice.p2pServer.GenesisHash = ftservice.blockchain.Genesis().Hash()
// txpool
if config.TxPool.Journal != "" {
diff --git a/go.mod b/go.mod
index d0980cba..3ca38a80 100644
--- a/go.mod
+++ b/go.mod
@@ -32,4 +32,5 @@ require (
gopkg.in/fatih/set.v0 v0.2.1
gopkg.in/karalabe/cookiejar.v2 v2.0.0-20150724131613-8dcd6a7f4951
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce
+ gopkg.in/yaml.v2 v2.2.2
)
diff --git a/p2p/dial.go b/p2p/dial.go
index 1270dca6..192b8299 100644
--- a/p2p/dial.go
+++ b/p2p/dial.go
@@ -89,6 +89,7 @@ type discoverTable interface {
Resolve(*enode.Node) *enode.Node
LookupRandom() []*enode.Node
ReadRandomNodes([]*enode.Node) int
+ SeedNodes() []*enode.Node
}
// the dial history remembers recent dials.
diff --git a/p2p/dial_test.go b/p2p/dial_test.go
index 056f370e..3a4670ab 100644
--- a/p2p/dial_test.go
+++ b/p2p/dial_test.go
@@ -85,6 +85,7 @@ func (t fakeTable) Close() {}
func (t fakeTable) LookupRandom() []*enode.Node { return nil }
func (t fakeTable) Resolve(*enode.Node) *enode.Node { return nil }
func (t fakeTable) ReadRandomNodes(buf []*enode.Node) int { return copy(buf, t) }
+func (t fakeTable) SeedNodes() []*enode.Node { return nil }
// This test checks that dynamic dials are launched from discovery results.
func TestDialStateDynDial(t *testing.T) {
@@ -703,3 +704,4 @@ func (t *resolveMock) Self() *enode.Node { return new(enode.
func (t *resolveMock) Close() {}
func (t *resolveMock) LookupRandom() []*enode.Node { return nil }
func (t *resolveMock) ReadRandomNodes(buf []*enode.Node) int { return 0 }
+func (t *resolveMock) SeedNodes() []*enode.Node { return nil }
diff --git a/p2p/discover/table.go b/p2p/discover/table.go
index 8247f8b4..97986aae 100644
--- a/p2p/discover/table.go
+++ b/p2p/discover/table.go
@@ -141,6 +141,11 @@ func (tab *Table) Self() *enode.Node {
return unwrapNode(tab.self)
}
+// SeedNodes return all of the seed nodes
+func (tab *Table) SeedNodes() []*enode.Node {
+ return tab.db.QueryAllSeeds()
+}
+
// ReadRandomNodes fills the given slice with random nodes from the table. The results
// are guaranteed to be unique for a single invocation, no node will appear twice.
func (tab *Table) ReadRandomNodes(buf []*enode.Node) (n int) {
diff --git a/p2p/enode/nodedb.go b/p2p/enode/nodedb.go
index 442b72bc..e5f71179 100644
--- a/p2p/enode/nodedb.go
+++ b/p2p/enode/nodedb.go
@@ -329,6 +329,30 @@ seek:
return nodes
}
+// QueryAllSeeds retrieves all nodes to be used as potential seed nodes
+// for bootstrapping.
+func (db *DB) QueryAllSeeds() []*Node {
+ var (
+ nodes []*Node
+ it = db.lvl.NewIterator(nil, nil)
+ )
+ defer it.Release()
+seek:
+ for it.Next() {
+ n := nextNode(it)
+ if n == nil {
+ break
+ }
+ for i := range nodes {
+ if nodes[i].ID() == n.ID() {
+ continue seek
+ }
+ }
+ nodes = append(nodes, n)
+ }
+ return nodes
+}
+
// reads the next node record from the iterator, skipping over other
// database entries.
func nextNode(it iterator.Iterator) *Node {
diff --git a/p2p/enode/urlv4_test.go b/p2p/enode/urlv4_test.go
index 2ddbdb75..8db7b4b7 100644
--- a/p2p/enode/urlv4_test.go
+++ b/p2p/enode/urlv4_test.go
@@ -43,10 +43,11 @@ var parseNodeTests = []struct {
wantError: `invalid node ID (wrong length, want 128 hex chars)`,
},
// Complete nodes with IP address.
- {
- rawurl: "fnode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:foo",
- wantError: `invalid port`,
- },
+ // this test go 1.13 is not work, wantError: `invalid port:foo`
+ // {
+ // rawurl: "fnode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:foo",
+ // wantError: `invalid port`,
+ // },
{
rawurl: "fnode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:3?discport=foo",
wantError: `invalid discport in query`,
diff --git a/p2p/protoadaptor/ProtoAdaptor.go b/p2p/protoadaptor/ProtoAdaptor.go
index f227d070..28d82bf7 100644
--- a/p2p/protoadaptor/ProtoAdaptor.go
+++ b/p2p/protoadaptor/ProtoAdaptor.go
@@ -173,16 +173,12 @@ func checkDDOS(m map[int][]int64, e *router.Event) bool {
}
//m[t][0] time
//m[t][1] request per second
- if m[t][0] == time.Now().Unix() {
- m[t][1]++
- } else {
- if m[t][1] > limit {
- return true
- }
+ if m[t][0] != time.Now().Unix() {
m[t][0] = time.Now().Unix()
- m[t][1] = 1
+ m[t][1] = 0
}
- return false
+ m[t][1]++
+ return m[t][1] > limit
}
// Protocols .
@@ -200,6 +196,7 @@ func (adaptor *ProtoAdaptor) Protocols() []p2p.Protocol {
// Stop .
func (adaptor *ProtoAdaptor) Stop() {
close(adaptor.quit)
+ router.StationRegister(adaptor.peerMangaer.station)
for _, sub := range adaptor.subs {
sub.Unsubscribe()
}
diff --git a/p2p/server.go b/p2p/server.go
index 28e06e13..529cabec 100644
--- a/p2p/server.go
+++ b/p2p/server.go
@@ -302,6 +302,10 @@ func (srv *Server) Peers() []*Peer {
return ps
}
+func (srv *Server) SeedNodes() []*enode.Node {
+ return srv.ntab.SeedNodes()
+}
+
// PeerCount returns the number of connected peers.
func (srv *Server) PeerCount() int {
var count int
@@ -501,6 +505,8 @@ func (srv *Server) DiscoverOnly() error {
srv.log = log.New()
}
+ srv.quit = make(chan struct{})
+
srv.log.Info("Starting P2P discovery networking", "NetID", srv.magicNetID(), "UsrNetID", srv.NetworkID)
// static fields
@@ -516,7 +522,6 @@ func (srv *Server) DiscoverOnly() error {
if err != nil {
return err
}
- srv.quit = make(chan struct{})
cfg := discover.Config{
TCPPort: 0,
MagicNetID: srv.magicNetID(),
@@ -531,11 +536,13 @@ func (srv *Server) DiscoverOnly() error {
if err != nil {
return err
}
-
+ srv.ntab = ntab
+ srv.loopWG.Add(1)
go func() {
timeout := time.NewTicker(10 * time.Minute)
+ defer srv.loopWG.Done()
defer timeout.Stop()
- defer ntab.Close()
+ defer srv.ntab.Close()
for {
select {
case <-timeout.C:
diff --git a/params/chainconfig.go b/params/chainconfig.go
index af12c956..5f8b2ae1 100644
--- a/params/chainconfig.go
+++ b/params/chainconfig.go
@@ -152,7 +152,9 @@ const (
ForkID2 = uint64(2)
//ForkID3 dpos config candidateAvailableMinQuantity modified
ForkID3 = uint64(3)
+ //ForkID4 miner pubkey separate
+ ForkID4 = uint64(4)
// NextForkID is the id of next fork
- NextForkID uint64 = ForkID3
+ NextForkID uint64 = ForkID4
)
diff --git a/processor/error.go b/processor/error.go
index 5788cd44..b862bdbd 100644
--- a/processor/error.go
+++ b/processor/error.go
@@ -55,7 +55,8 @@ var (
errParentBlock = errors.New("parent block not exist")
- ErrActionInvalid = errors.New("action field invalid")
+ ErrActionInvalid = errors.New("action field invalid")
+ errPayerNotSupport = errors.New("payer not support")
)
// GenesisMismatchError is raised when trying to overwrite an existing
diff --git a/processor/processor.go b/processor/processor.go
index cced30e0..712c3f12 100644
--- a/processor/processor.go
+++ b/processor/processor.go
@@ -24,6 +24,7 @@ import (
"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/state"
"github.com/fractalplatform/fractal/types"
@@ -100,7 +101,6 @@ func (p *StateProcessor) ApplyTransaction(author *common.Name, gp *common.GasPoo
if assetID != tx.GasAssetID() {
return nil, 0, fmt.Errorf("only support system asset %d as tx fee", p.bc.Config().SysTokenID)
}
- gasPrice := tx.GasPrice()
//timer for vm exec overtime
var t *time.Timer
//
@@ -125,11 +125,26 @@ func (p *StateProcessor) ApplyTransaction(author *common.Name, gp *common.GasPoo
return nil, 0, ErrNonceTooLow
}
+ var gasPayer = action.Sender()
+ var gasPrice = tx.GasPrice()
+ if tx.PayerExist() {
+ if header.CurForkID() >= params.ForkID4 {
+ gasPayer = action.Payer()
+ gasPrice = action.PayerGasPrice()
+ } else {
+ return nil, 0, errPayerNotSupport
+ }
+ } else {
+ if action.PayerIsExist() {
+ return nil, 0, errPayerNotSupport
+ }
+ }
+
evmcontext := &EvmContext{
ChainContext: p.bc,
EngineContext: p.engine,
}
- context := NewEVMContext(action.Sender(), action.Recipient(), assetID, tx.GasPrice(), header, evmcontext, author)
+ context := NewEVMContext(action.Sender(), action.Recipient(), assetID, gasPrice, header, evmcontext, author)
vmenv := vm.NewEVM(context, accountDB, statedb, config, cfg)
//will abort the vm if overtime
@@ -139,7 +154,7 @@ func (p *StateProcessor) ApplyTransaction(author *common.Name, gp *common.GasPoo
})
}
- _, gas, failed, err, vmerr := ApplyMessage(accountDB, vmenv, action, gp, gasPrice, assetID, config, p.engine)
+ _, gas, failed, err, vmerr := ApplyMessage(accountDB, vmenv, action, gp, gasPrice, gasPayer, assetID, config, p.engine)
if false == cfg.EndTime.IsZero() {
//close timer
diff --git a/processor/transition.go b/processor/transition.go
index 4d518f73..7d89b2f5 100644
--- a/processor/transition.go
+++ b/processor/transition.go
@@ -44,6 +44,7 @@ type StateTransition struct {
gas uint64
initialGas uint64
gasPrice *big.Int
+ gasPayer common.Name
assetID uint64
account *accountmanager.AccountManager
evm *vm.EVM
@@ -52,7 +53,7 @@ type StateTransition struct {
// NewStateTransition initialises and returns a new state transition object.
func NewStateTransition(accountDB *accountmanager.AccountManager, evm *vm.EVM,
- action *types.Action, gp *common.GasPool, gasPrice *big.Int, assetID uint64,
+ action *types.Action, gp *common.GasPool, gasPrice *big.Int, gasPayer common.Name, assetID uint64,
config *params.ChainConfig, engine EngineContext) *StateTransition {
return &StateTransition{
engine: engine,
@@ -61,6 +62,7 @@ func NewStateTransition(accountDB *accountmanager.AccountManager, evm *vm.EVM,
evm: evm,
action: action,
gasPrice: gasPrice,
+ gasPayer: gasPayer,
assetID: assetID,
account: accountDB,
chainConfig: config,
@@ -69,9 +71,9 @@ func NewStateTransition(accountDB *accountmanager.AccountManager, evm *vm.EVM,
// ApplyMessage computes the new state by applying the given message against the old state within the environment.
func ApplyMessage(accountDB *accountmanager.AccountManager, evm *vm.EVM,
- action *types.Action, gp *common.GasPool, gasPrice *big.Int,
+ action *types.Action, gp *common.GasPool, gasPrice *big.Int, gasPayer common.Name,
assetID uint64, config *params.ChainConfig, engine EngineContext) ([]byte, uint64, bool, error, error) {
- return NewStateTransition(accountDB, evm, action, gp, gasPrice,
+ return NewStateTransition(accountDB, evm, action, gp, gasPrice, gasPayer,
assetID, config, engine).TransitionDb()
}
@@ -89,7 +91,7 @@ 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, 0)
+ balance, err := st.account.GetAccountBalanceByID(st.gasPayer, st.assetID, 0)
if err != nil {
return err
}
@@ -101,7 +103,7 @@ func (st *StateTransition) buyGas() error {
}
st.gas += st.action.Gas()
st.initialGas = st.action.Gas()
- return st.account.TransferAsset(st.from, common.Name(st.chainConfig.FeeName), st.assetID, mgval)
+ return st.account.TransferAsset(st.gasPayer, common.Name(st.chainConfig.FeeName), st.assetID, mgval)
}
// TransitionDb will transition the state by applying the current message and
@@ -135,10 +137,47 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo
ret, st.gas, vmerr = evm.Create(sender, st.action, st.gas)
case actionType == types.CallContract:
ret, st.gas, vmerr = evm.Call(sender, st.action, st.gas)
+ case actionType == types.Transfer:
+ var fromExtra common.Name
+ if evm.ForkID >= params.ForkID4 {
+ if asset, err := st.account.GetAssetInfoByID(st.action.AssetID()); err == nil {
+ assetContract := asset.GetContract()
+ if len(assetContract) != 0 && assetContract != sender.Name() && assetContract != st.action.Recipient() {
+ var cantransfer bool
+ st.gas, cantransfer = evm.CanTransferContractAsset(sender, st.gas, st.action.AssetID(), asset.GetContract())
+ if cantransfer {
+ fromExtra = asset.GetContract()
+ }
+ }
+ }
+ }
+ if len(fromExtra) == 0 {
+ internalLogs, err := st.account.Process(&types.AccountManagerContext{
+ Action: st.action,
+ Number: st.evm.Context.BlockNumber.Uint64(),
+ CurForkID: st.evm.Context.ForkID,
+ ChainConfig: st.chainConfig,
+ })
+ vmerr = err
+ evm.InternalTxs = append(evm.InternalTxs, internalLogs...)
+ } else {
+ internalLogs, err := st.account.Process(&types.AccountManagerContext{
+ Action: st.action,
+ Number: st.evm.Context.BlockNumber.Uint64(),
+ CurForkID: st.evm.Context.ForkID,
+ ChainConfig: st.chainConfig,
+ FromAccountExtra: []common.Name{fromExtra},
+ })
+ vmerr = err
+ evm.InternalTxs = append(evm.InternalTxs, internalLogs...)
+ }
+
case actionType == types.RegCandidate:
fallthrough
case actionType == types.UpdateCandidate:
fallthrough
+ case actionType == types.UpdateCandidatePubKey:
+ fallthrough
case actionType == types.UnregCandidate:
fallthrough
case actionType == types.VoteCandidate:
@@ -188,29 +227,61 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo
if err := st.distributeFee(); err != nil {
return ret, st.gasUsed(), true, err, vmerr
}
+
+ // action := types.NewAction(types.Transfer, st.from, common.Name(st.chainConfig.FeeName), 0, st.assetID, 0, big.NewInt(0).SetUint64(st.gasUsed()), nil, nil)
+ // internalAction := &types.InternalAction{Action: action.NewRPCAction(0), ActionType: "addfee", GasUsed: 0, GasLimit: 0, Depth: 0}
+ // evm.InternalTxs = append(evm.InternalTxs, internalAction)
+
return ret, st.gasUsed(), vmerr != nil, nil, vmerr
}
func (st *StateTransition) distributeGas(intrinsicGas uint64) {
switch st.action.Type() {
case types.Transfer:
- assetInfo, _ := st.evm.AccountDB.GetAssetInfoByID(st.action.AssetID())
- assetName := common.Name(assetInfo.GetAssetName())
- assetFounderRatio := st.chainConfig.ChargeCfg.AssetRatio
-
- key := vm.DistributeKey{ObjectName: assetName,
- ObjectType: params.AssetFeeType}
- assetGas := int64(st.gasUsed() * assetFounderRatio / 100)
- dGas := vm.DistributeGas{
- Value: assetGas,
- TypeID: params.AssetFeeType}
- st.evm.FounderGasMap[key] = dGas
-
- key = vm.DistributeKey{ObjectName: st.evm.Coinbase,
- ObjectType: params.CoinbaseFeeType}
- st.evm.FounderGasMap[key] = vm.DistributeGas{
- Value: int64(st.gasUsed()) - assetGas,
- TypeID: params.CoinbaseFeeType}
+ if st.evm.ForkID >= params.ForkID4 {
+ if asset, err := st.account.GetAssetInfoByID(st.action.AssetID()); err == nil {
+ assetContract := asset.GetContract()
+ if len(assetContract) != 0 && assetContract != st.action.Sender() && assetContract != st.action.Recipient() {
+ st.distributeToContract(asset.GetContract(), intrinsicGas)
+ } else {
+ assetInfo, _ := st.evm.AccountDB.GetAssetInfoByID(st.action.AssetID())
+ assetName := common.Name(assetInfo.GetAssetName())
+ assetFounderRatio := st.chainConfig.ChargeCfg.AssetRatio
+
+ key := vm.DistributeKey{ObjectName: assetName,
+ ObjectType: params.AssetFeeType}
+ assetGas := int64(st.gasUsed() * assetFounderRatio / 100)
+ dGas := vm.DistributeGas{
+ Value: assetGas,
+ TypeID: params.AssetFeeType}
+ st.evm.FounderGasMap[key] = dGas
+
+ key = vm.DistributeKey{ObjectName: st.evm.Coinbase,
+ ObjectType: params.CoinbaseFeeType}
+ st.evm.FounderGasMap[key] = vm.DistributeGas{
+ Value: int64(st.gasUsed()) - assetGas,
+ TypeID: params.CoinbaseFeeType}
+ }
+ }
+ } else {
+ assetInfo, _ := st.evm.AccountDB.GetAssetInfoByID(st.action.AssetID())
+ assetName := common.Name(assetInfo.GetAssetName())
+ assetFounderRatio := st.chainConfig.ChargeCfg.AssetRatio
+
+ key := vm.DistributeKey{ObjectName: assetName,
+ ObjectType: params.AssetFeeType}
+ assetGas := int64(st.gasUsed() * assetFounderRatio / 100)
+ dGas := vm.DistributeGas{
+ Value: assetGas,
+ TypeID: params.AssetFeeType}
+ st.evm.FounderGasMap[key] = dGas
+
+ key = vm.DistributeKey{ObjectName: st.evm.Coinbase,
+ ObjectType: params.CoinbaseFeeType}
+ st.evm.FounderGasMap[key] = vm.DistributeGas{
+ Value: int64(st.gasUsed()) - assetGas,
+ TypeID: params.CoinbaseFeeType}
+ }
case types.CreateContract:
fallthrough
@@ -243,6 +314,8 @@ func (st *StateTransition) distributeGas(intrinsicGas uint64) {
fallthrough
case types.UpdateCandidate:
fallthrough
+ case types.UpdateCandidatePubKey:
+ fallthrough
case types.UnregCandidate:
fallthrough
case types.VoteCandidate:
@@ -332,7 +405,7 @@ func (st *StateTransition) distributeFee() error {
func (st *StateTransition) refundGas() {
remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice)
- st.account.TransferAsset(common.Name(st.chainConfig.FeeName), st.from, st.assetID, remaining)
+ st.account.TransferAsset(common.Name(st.chainConfig.FeeName), st.gasPayer, st.assetID, remaining)
st.gp.AddGas(st.gas)
}
diff --git a/processor/validator.go b/processor/validator.go
index 1110116e..172bded3 100644
--- a/processor/validator.go
+++ b/processor/validator.go
@@ -133,9 +133,16 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
return err
}
- // Header validity is known at this point, check the uncles and transactions
- if hash := types.DeriveTxsMerkleRoot(block.Txs); hash != block.TxHash() {
- return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, block.TxHash())
+ if block.CurForkID() >= params.ForkID4 {
+ // Header validity is known at this point, check the uncles and transactions
+ if hash := types.DeriveExtensTxsMerkleRoot(block.Txs); hash != block.TxHash() {
+ return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, block.TxHash())
+ }
+ } else {
+ // Header validity is known at this point, check the uncles and transactions
+ if hash := types.DeriveTxsMerkleRoot(block.Txs); hash != block.TxHash() {
+ return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, block.TxHash())
+ }
}
return nil
}
diff --git a/processor/vm/gas_table.go b/processor/vm/gas_table.go
index 3a6b54be..f5f79224 100644
--- a/processor/vm/gas_table.go
+++ b/processor/vm/gas_table.go
@@ -319,6 +319,11 @@ func gasCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem
gas = gt.Calls
transfersValue = stack.Back(2).Sign() != 0
)
+ if evm.ForkID >= params.ForkID4 {
+ if p := PrecompiledContracts[stack.Back(1).Uint64()]; p != nil {
+ return gas, nil
+ }
+ }
if transfersValue {
gas += gt.CallValueTransferGas
}
diff --git a/processor/vm/instructions.go b/processor/vm/instructions.go
index ca9045cf..f2afe42d 100644
--- a/processor/vm/instructions.go
+++ b/processor/vm/instructions.go
@@ -522,6 +522,7 @@ func opSnapBalance(pc *uint64, evm *EVM, contract *Contract, memory *Memory, sta
evm.interpreter.intPool.put(time, assetId)
return nil, nil
}
+
func opBalanceex(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
assetId := stack.pop()
slot := stack.peek()
@@ -1598,9 +1599,12 @@ func execWithdrawFee(evm *EVM, contract *Contract, withdrawTo common.Name, objec
return errEnc
}
- action := types.NewAction(types.Transfer, common.Name(evm.chainConfig.FeeName), withdrawInfo.Founder, 0, 0, 0, big.NewInt(0), paload, nil)
- internalAction := &types.InternalAction{Action: action.NewRPCAction(0), ActionType: "transfer", GasUsed: 0, GasLimit: contract.Gas, Depth: uint64(evm.depth)}
- evm.InternalTxs = append(evm.InternalTxs, internalAction)
+ for _, assetInfo := range withdrawInfo.AssetInfo {
+ action := types.NewAction(types.Transfer, common.Name(evm.chainConfig.FeeName), withdrawInfo.Founder, 0, assetInfo.AssetID, 0, assetInfo.Amount, paload, nil)
+ internalAction := &types.InternalAction{Action: action.NewRPCAction(0), ActionType: "transfer", GasUsed: 0, GasLimit: contract.Gas, Depth: uint64(evm.depth)}
+ evm.InternalTxs = append(evm.InternalTxs, internalAction)
+ }
+
}
return err
}
@@ -1628,7 +1632,21 @@ func opCallEx(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *S
return nil, nil
}
- err = evm.AccountDB.TransferAsset(action.Sender(), action.Recipient(), action.AssetID(), action.Value())
+ var fromExtra common.Name
+ if evm.ForkID >= params.ForkID4 {
+ if asset, err := evm.AccountDB.GetAssetInfoByID(action.AssetID()); err == nil {
+ assetContract := asset.GetContract()
+ if len(assetContract) != 0 && assetContract != action.Sender() && assetContract != action.Recipient() {
+ var cantransfer bool
+ contract.Gas, cantransfer = evm.CanTransferContractAsset(contract, contract.Gas, action.AssetID(), assetContract)
+ if cantransfer {
+ fromExtra = assetContract
+ }
+ }
+ }
+ }
+
+ err = evm.AccountDB.TransferAsset(action.Sender(), action.Recipient(), action.AssetID(), action.Value(), fromExtra)
//distribute gas
var assetName common.Name
assetFounder, _ := evm.AccountDB.GetAssetFounder(action.AssetID()) //get asset founder name
diff --git a/processor/vm/vm.go b/processor/vm/vm.go
index 68741723..601905a7 100644
--- a/processor/vm/vm.go
+++ b/processor/vm/vm.go
@@ -30,6 +30,8 @@ import (
"github.com/fractalplatform/fractal/types"
)
+var contractAssetTransferable = common.Hex2Bytes("92ff0d31")
+
type (
// GetHashFunc returns the nth block hash in the blockchain and is used by the BLOCKHASH EVM op code.
GetHashFunc func(uint64) common.Hash
@@ -312,7 +314,21 @@ func (evm *EVM) Call(caller ContractRef, action *types.Action, gas uint64) (ret
}
}
- if err := evm.AccountDB.TransferAsset(action.Sender(), action.Recipient(), action.AssetID(), action.Value()); err != nil {
+ var fromExtra common.Name
+ if evm.ForkID >= params.ForkID4 {
+ if asset, err := evm.AccountDB.GetAssetInfoByID(action.AssetID()); err == nil {
+ assetContract := asset.GetContract()
+ if len(assetContract) != 0 && assetContract != caller.Name() && assetContract != action.Recipient() {
+ var cantransfer bool
+ gas, cantransfer = evm.CanTransferContractAsset(caller, gas, action.AssetID(), assetContract)
+ if cantransfer {
+ fromExtra = assetContract
+ }
+ }
+ }
+ }
+
+ if err := evm.AccountDB.TransferAsset(action.Sender(), action.Recipient(), action.AssetID(), action.Value(), fromExtra); err != nil {
return nil, gas, err
}
@@ -564,9 +580,17 @@ func (evm *EVM) Create(caller ContractRef, action *types.Action, gas uint64) (re
snapshot := evm.StateDB.Snapshot()
if b, err := evm.AccountDB.AccountHaveCode(contractName); err != nil {
- return nil, 0, err
+ if evm.ForkID >= params.ForkID4 {
+ return nil, gas, err
+ } else {
+ return nil, 0, err
+ }
} else if b {
- return nil, 0, ErrContractCodeCollision
+ if evm.ForkID >= params.ForkID4 {
+ return nil, gas, ErrContractCodeCollision
+ } else {
+ return nil, 0, ErrContractCodeCollision
+ }
}
if err := evm.AccountDB.TransferAsset(action.Sender(), action.Recipient(), evm.AssetID, action.Value()); err != nil {
@@ -629,6 +653,57 @@ func (evm *EVM) Create(caller ContractRef, action *types.Action, gas uint64) (re
return ret, contract.Gas, err
}
+func (evm *EVM) CanTransferContractAsset(caller ContractRef, gas uint64, assetID uint64, assetContract common.Name) (uint64, bool) {
+ // Fail if we're trying to execute above the call depth limit
+ if evm.depth > int(params.CallCreateDepth) {
+ return gas, false
+ }
+ var (
+ to = AccountRef(assetContract)
+ snapshot = evm.StateDB.Snapshot()
+ )
+
+ // 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.
+ contract := NewContract(caller, to, big.NewInt(0), gas, assetID)
+ acct, err := evm.AccountDB.GetAccountByName(assetContract)
+ if err != nil {
+ return 0, false
+ }
+ if acct == nil {
+ return 0, false
+ }
+ codeHash, err := acct.GetCodeHash()
+ if err != nil {
+ return 0, false
+ }
+ code, _ := acct.GetCode()
+ contract.SetCallCode(&assetContract, codeHash, code)
+
+ ret, err := run(evm, contract, contractAssetTransferable)
+ runGas := gas - contract.Gas
+
+ evm.distributeContractGas(runGas, assetContract, caller.Name())
+
+ // When an error was returned by the EVM or when setting the creation code
+ // above we revert to the snapshot and consume any gas remaining. Additionally
+ // when we're in homestead this also counts for code storage gas errors.
+ if err != nil {
+ evm.StateDB.RevertToSnapshot(snapshot)
+ if err != errExecutionReverted {
+ contract.UseGas(contract.Gas)
+ }
+ }
+ actualUsedGas := gas - contract.Gas
+ evm.distributeGasByScale(actualUsedGas, runGas)
+
+ if new(big.Int).SetBytes(ret).Cmp(big.NewInt(0)) > 0 && err == nil {
+ return contract.Gas, true
+ } else {
+ return contract.Gas, false
+ }
+}
+
// ChainConfig returns the environment's chain configuration
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
diff --git a/rpc/client.go b/rpc/client.go
index 14f01e7b..41fc52f0 100644
--- a/rpc/client.go
+++ b/rpc/client.go
@@ -283,6 +283,36 @@ func (c *Client) Call(result interface{}, method string, args ...interface{}) er
return c.CallContext(ctx, result, method, args...)
}
+func (c *Client) CallRaw(method string, args ...interface{}) (json.RawMessage, error) {
+ ctx := context.Background()
+ msg, err := c.newMessage(method, args...)
+ if err != nil {
+ return nil, err
+ }
+ op := &requestOp{ids: []json.RawMessage{msg.ID}, resp: make(chan *jsonrpcMessage, 1)}
+
+ if c.isHTTP {
+ err = c.sendHTTP(ctx, op, msg)
+ } else {
+ err = c.send(ctx, op, msg)
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ // dispatch has accepted the request and will close the channel it when it quits.
+ switch resp, err := op.wait(ctx); {
+ case err != nil:
+ return nil, err
+ case resp.Error != nil:
+ return nil, resp.Error
+ case len(resp.Result) == 0:
+ return nil, ErrNoResult
+ default:
+ return resp.Result, nil
+ }
+}
+
// CallContext performs a JSON-RPC call with the given arguments. If the context is
// canceled before the call has successfully returned, CallContext returns immediately.
//
diff --git a/rpcapi/backend.go b/rpcapi/backend.go
index 0d926eb0..5c975f62 100644
--- a/rpcapi/backend.go
+++ b/rpcapi/backend.go
@@ -81,6 +81,7 @@ type Backend interface {
RemovePeer(url string) error
AddTrustedPeer(url string) error
RemoveTrustedPeer(url string) error
+ SeedNodes() []string
PeerCount() int
Peers() []string
BadNodesCount() int
diff --git a/rpcapi/blockchain.go b/rpcapi/blockchain.go
index f0b00e7b..48b85faa 100644
--- a/rpcapi/blockchain.go
+++ b/rpcapi/blockchain.go
@@ -80,6 +80,21 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(chainID *big.Int, b *types.Block, i
return fields
}
+func (s *PublicBlockChainAPI) GetBlockByNumberWithPayer(ctx context.Context, blockNr rpc.BlockNumber, fullTx bool) map[string]interface{} {
+ block := s.b.BlockByNumber(ctx, blockNr)
+ if block != nil {
+ response := s.rpcOutputBlockWithPayer(s.b.ChainConfig().ChainID, block, true, fullTx)
+ return response
+ }
+ return nil
+}
+
+func (s *PublicBlockChainAPI) rpcOutputBlockWithPayer(chainID *big.Int, b *types.Block, inclTx bool, fullTx bool) map[string]interface{} {
+ fields := RPCMarshalBlockWithPayer(chainID, b, inclTx, fullTx)
+ fields["totalDifficulty"] = s.b.GetTd(b.Hash())
+ return fields
+}
+
// GetTransactionByHash returns the transaction for the given hash
func (s *PublicBlockChainAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) *types.RPCTransaction {
// Try to return an already finalized transaction
@@ -126,6 +141,23 @@ func (s *PublicBlockChainAPI) GetTransactionReceipt(ctx context.Context, hash co
return receipt.NewRPCReceipt(blockHash, blockNumber, index, tx), nil
}
+func (s *PublicBlockChainAPI) GetTransactionReceiptWithPayer(ctx context.Context, hash common.Hash) (*types.RPCReceiptWithPayer, error) {
+ tx, blockHash, blockNumber, index := rawdb.ReadTransaction(s.b.ChainDb(), hash)
+ if tx == nil {
+ return nil, nil
+ }
+
+ receipts, err := s.b.GetReceipts(ctx, blockHash)
+ if err != nil {
+ return nil, err
+ }
+ if len(receipts) <= int(index) {
+ return nil, nil
+ }
+ receipt := receipts[index]
+ return receipt.NewRPCReceiptWithPayer(blockHash, blockNumber, index, tx), nil
+}
+
func (s *PublicBlockChainAPI) GetBlockAndResultByNumber(ctx context.Context, blockNr rpc.BlockNumber) *types.BlockAndResult {
r := s.b.GetBlockDetailLog(ctx, blockNr)
if r == nil {
@@ -136,6 +168,16 @@ func (s *PublicBlockChainAPI) GetBlockAndResultByNumber(ctx context.Context, blo
return r
}
+func (s *PublicBlockChainAPI) GetBlockAndResultByNumberWithPayer(ctx context.Context, blockNr rpc.BlockNumber) *types.BlockAndResult {
+ r := s.b.GetBlockDetailLog(ctx, blockNr)
+ if r == nil {
+ return nil
+ }
+ block := s.GetBlockByNumberWithPayer(ctx, blockNr, true)
+ r.Block = block
+ return r
+}
+
// checkRangeInputArgs checks the input arguments of
// GetTxsByAccount,GetTxsByBloom,GetInternalTxByAccount,GetInternalTxByBloom
func (s *PublicBlockChainAPI) checkRangeInputArgs(blockNr, lookbackNum uint64) error {
@@ -306,7 +348,7 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
// and apply the message.
gp := new(common.GasPool).AddGas(math.MaxUint64)
action := types.NewAction(args.ActionType, args.From, args.To, 0, assetID, gas, value, args.Data, args.Remark)
- res, gas, failed, err, _ := processor.ApplyMessage(account, evm, action, gp, gasPrice, assetID, s.b.ChainConfig(), s.b.Engine())
+ res, gas, failed, err, _ := processor.ApplyMessage(account, evm, action, gp, gasPrice, action.Sender(), assetID, s.b.ChainConfig(), s.b.Engine())
if err := vmError(); err != nil {
return nil, 0, false, err
}
diff --git a/rpcapi/p2p.go b/rpcapi/p2p.go
index a436b349..a2458b0d 100644
--- a/rpcapi/p2p.go
+++ b/rpcapi/p2p.go
@@ -111,6 +111,11 @@ func (api *PrivateP2pAPI) RemoveTrustedPeer(url string) (bool, error) {
return true, nil
}
+// SeedNodes returns all seed nodes.
+func (api *PrivateP2pAPI) SeedNodes() []string {
+ return api.b.SeedNodes()
+}
+
// PeerCount return number of connected peers
func (api *PrivateP2pAPI) PeerCount() int {
return api.b.PeerCount()
diff --git a/rpcapi/txpool.go b/rpcapi/txpool.go
index 40b80333..4f1d1598 100644
--- a/rpcapi/txpool.go
+++ b/rpcapi/txpool.go
@@ -79,7 +79,7 @@ func (s *PrivateTxPoolAPI) Content(fullTx bool) interface{} {
return content
}
-// PendingTransactions returns the pending transactions that are in the transaction pool
+// PendingTransactions returns the pending transactions that are in the transaction pool.
func (s *PrivateTxPoolAPI) PendingTransactions(fullTx bool) (interface{}, error) {
pending, err := s.b.TxPool().Pending()
if err != nil {
@@ -106,8 +106,8 @@ func (s *PrivateTxPoolAPI) PendingTransactions(fullTx bool) (interface{}, error)
return txsHashes, nil
}
-// GetPoolTransactions txpool returns the transaction for the given hash
-func (s *PrivateTxPoolAPI) GetPoolTransactions(hashes []common.Hash) []*types.RPCTransaction {
+// GetTransactions txpool returns the transaction by the given hash.
+func (s *PrivateTxPoolAPI) GetTransactions(hashes []common.Hash) []*types.RPCTransaction {
var txs []*types.RPCTransaction
for _, hash := range hashes {
if tx := s.b.TxPool().Get(hash); tx != nil {
@@ -117,6 +117,36 @@ func (s *PrivateTxPoolAPI) GetPoolTransactions(hashes []common.Hash) []*types.RP
return txs
}
+// GetTransactionsByAccount txpool returns the transaction by the given account name.
+func (s *PrivateTxPoolAPI) GetTransactionsByAccount(name common.Name, fullTx bool) interface{} {
+ content := map[string]map[string]interface{}{
+ "pending": make(map[string]interface{}),
+ "queued": make(map[string]interface{}),
+ }
+
+ txsFunc := func(name common.Name, m map[common.Name][]*types.Transaction, fullTx bool) map[string]interface{} {
+ dump := make(map[string]interface{})
+ txs, ok := m[name]
+ if ok {
+ for _, tx := range txs {
+ if fullTx {
+ dump[fmt.Sprintf("%d", tx.GetActions()[0].Nonce())] = tx.NewRPCTransaction(common.Hash{}, 0, 0)
+ } else {
+ dump[fmt.Sprintf("%d", tx.GetActions()[0].Nonce())] = tx.Hash()
+ }
+ }
+ }
+ return dump
+ }
+
+ pending, queue := s.b.TxPool().Content()
+
+ content["pending"] = txsFunc(name, pending, fullTx)
+ content["queued"] = txsFunc(name, queue, fullTx)
+
+ return content
+}
+
// SetGasPrice set txpool gas price
func (s *PrivateTxPoolAPI) SetGasPrice(gasprice *big.Int) bool {
return s.b.SetGasPrice(gasprice)
diff --git a/rpcapi/utils.go b/rpcapi/utils.go
index dd8f2c4f..6c240dbd 100644
--- a/rpcapi/utils.go
+++ b/rpcapi/utils.go
@@ -78,3 +78,44 @@ func RPCMarshalBlock(chainID *big.Int, b *types.Block, inclTx bool, fullTx bool)
return fields
}
+
+func RPCMarshalBlockWithPayer(chainID *big.Int, b *types.Block, inclTx bool, fullTx bool) map[string]interface{} {
+ head := b.Header() // copies the header once
+ fields := map[string]interface{}{
+ "number": head.Number,
+ "hash": b.Hash(),
+ "proposedIrreversible": head.ProposedIrreversible,
+ "parentHash": head.ParentHash,
+ "logsBloom": head.Bloom,
+ "stateRoot": head.Root,
+ "miner": head.Coinbase,
+ "difficulty": head.Difficulty,
+ "extraData": hexutil.Bytes(head.Extra),
+ "size": b.Size(),
+ "gasLimit": head.GasLimit,
+ "gasUsed": head.GasUsed,
+ "timestamp": head.Time,
+ "transactionsRoot": head.TxsRoot,
+ "receiptsRoot": head.ReceiptsRoot,
+ "forkID": head.ForkID,
+ }
+
+ if inclTx {
+ formatTx := func(tx *types.Transaction, index uint64) interface{} {
+ return tx.Hash()
+ }
+ if fullTx {
+ formatTx = func(tx *types.Transaction, index uint64) interface{} {
+ return tx.NewRPCTransactionWithPayer(b.Hash(), b.NumberU64(), index)
+ }
+ }
+ txs := b.Transactions()
+ transactions := make([]interface{}, len(txs))
+ for i, tx := range txs {
+ transactions[i] = formatTx(tx, uint64(i))
+ }
+ fields["transactions"] = transactions
+ }
+
+ return fields
+}
diff --git a/scripts/commit_hash.sh b/scripts/commit_hash.sh
index dd3511dd..51662737 100755
--- a/scripts/commit_hash.sh
+++ b/scripts/commit_hash.sh
@@ -19,6 +19,9 @@
# Gets the git commit hash of the working dir and adds an additional hash of any tracked modified files
commit=$(git describe --tags)
dirty=$(git ls-files -m)
+branch=$(git branch | grep '*' | cut -d ' ' -f 2)
+
+commit="$commit+branch.$branch"
if [[ -n ${dirty} ]]; then
commit="$commit+dirty.$(echo ${dirty} | git hash-object --stdin | head -c8)"
fi
diff --git a/sdk/account_test.go b/sdk/account_test.go
index f0e88f07..fad9fbc8 100644
--- a/sdk/account_test.go
+++ b/sdk/account_test.go
@@ -193,7 +193,7 @@ func TestRegCandidate(t *testing.T) {
// RegCandidate
act := NewAccount(api, common.StrToName(name), priv, chainCfg.SysTokenID, math.MaxUint64, true, chainCfg.ChainID)
hash, err := act.RegCandidate(common.StrToName(chainCfg.DposName), new(big.Int).Mul(new(big.Int).Div(val, big.NewInt(4)), decimals), chainCfg.SysTokenID, gas, &dpos.RegisterCandidate{
- URL: fmt.Sprintf("www.%s.com", name),
+ Info: fmt.Sprintf("www.%s.com", name),
})
So(err, ShouldBeNil)
So(hash, ShouldNotBeNil)
@@ -204,7 +204,7 @@ func TestUpdateCandidate(t *testing.T) {
// UpdateCandidate
act := NewAccount(api, common.StrToName(name), priv, chainCfg.SysTokenID, math.MaxUint64, true, chainCfg.ChainID)
hash, err := act.UpdateCandidate(common.StrToName(chainCfg.DposName), new(big.Int).Mul(new(big.Int).Div(val, big.NewInt(4)), decimals), chainCfg.SysTokenID, gas, &dpos.UpdateCandidate{
- URL: fmt.Sprintf("www.%s.com", name),
+ Info: fmt.Sprintf("www.%s.com", name),
})
So(err, ShouldBeNil)
So(hash, ShouldNotBeNil)
diff --git a/sdk/test/dpos/createandregcandidate.json b/sdk/test/dpos/createandregcandidate.json
index 65c3d818..51d7ec01 100644
--- a/sdk/test/dpos/createandregcandidate.json
+++ b/sdk/test/dpos/createandregcandidate.json
@@ -22,7 +22,7 @@
"gas": 30000000,
"value": 20000000000000000000000,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true,
@@ -34,7 +34,7 @@
"gas": 30000000,
"value": 5000000000000000000000,
"payload": {
- "URL": "www.dpos0001.com"
+ "Info": "www.dpos0001.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
@@ -64,7 +64,7 @@
"gas": 30000000,
"value": 20000000000000000000000,
"payload": {
- "URL": "www.xxxxx.com"
+ "Info": "www.xxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true,
@@ -76,7 +76,7 @@
"gas": 30000000,
"value": 5000000000000000000000,
"payload": {
- "URL": "www.dpos0002.com"
+ "Info": "www.dpos0002.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
@@ -106,7 +106,7 @@
"gas": 30000000,
"value": 20000000000000000000000,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true,
@@ -118,7 +118,7 @@
"gas": 30000000,
"value": 5000000000000000000000,
"payload": {
- "URL": "www.dpos0003.com"
+ "Info": "www.dpos0003.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
@@ -148,7 +148,7 @@
"gas": 30000000,
"value": 20000000000000000000000,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true,
@@ -160,7 +160,7 @@
"gas": 30000000,
"value": 5000000000000000000000,
"payload": {
- "URL": "www.dpos0004.com"
+ "Info": "www.dpos0004.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
@@ -190,7 +190,7 @@
"gas": 30000000,
"value": 20000000000000000000000,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true,
@@ -202,7 +202,7 @@
"gas": 30000000,
"value": 5000000000000000000000,
"payload": {
- "URL": "www.dpos0005.com"
+ "Info": "www.dpos0005.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
@@ -232,7 +232,7 @@
"gas": 30000000,
"value": 20000000000000000000000,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true,
@@ -244,7 +244,7 @@
"gas": 30000000,
"value": 5000000000000000000000,
"payload": {
- "URL": "www.dpos0006.com"
+ "Info": "www.dpos0006.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
diff --git a/sdk/test/dpos/dposstartlowc.json b/sdk/test/dpos/dposstartlowc.json
index 18d9dafc..0d98b63b 100644
--- a/sdk/test/dpos/dposstartlowc.json
+++ b/sdk/test/dpos/dposstartlowc.json
@@ -22,7 +22,7 @@
"gas": 30000000,
"value": 500000000000000000000000,
"payload": {
- "URL": "www.dpos0001.com"
+ "Info": "www.dpos0001.com"
},
"succeed": true
}]
@@ -51,7 +51,7 @@
"gas": 30000000,
"value": 1000000000000000000000000,
"payload": {
- "URL": "www.dpos0002.com"
+ "Info": "www.dpos0002.com"
},
"succeed": true
}]
@@ -80,7 +80,7 @@
"gas": 30000000,
"value": 3000000000000000000000000,
"payload": {
- "URL": "www.dpos0006.com"
+ "Info": "www.dpos0006.com"
},
"succeed": true,
"childs": [{
@@ -91,7 +91,7 @@
"gas": 30000000,
"value": 1000000000000000000000000,
"payload": {
- "URL": "www.dpos0006.com"
+ "Info": "www.dpos0006.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
@@ -132,7 +132,7 @@
"gas": 30000000,
"value": 4000000000000000000000000,
"payload": {
- "URL": "www.dpos0007.com"
+ "Info": "www.dpos0007.com"
},
"succeed": true,
"childs": [{
@@ -143,7 +143,7 @@
"gas": 30000000,
"value": 1000000000000000000000000,
"payload": {
- "URL": "www.dpos0007.com"
+ "Info": "www.dpos0007.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
@@ -184,7 +184,7 @@
"gas": 30000000,
"value": 3000000000000000000000000,
"payload": {
- "URL": "www.dpos0008.com"
+ "Info": "www.dpos0008.com"
},
"succeed": true,
"childs": [{
@@ -195,7 +195,7 @@
"gas": 30000000,
"value": 1000000000000000000000000,
"payload": {
- "URL": "www.dpos0008.com"
+ "Info": "www.dpos0008.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
@@ -239,7 +239,7 @@
"gas": 30000000,
"value": 1000000000000000000000000,
"payload": {
- "URL": "www.dpos0007.com"
+ "Info": "www.dpos0007.com"
},
"succeed": true,
"childs": [{
@@ -250,7 +250,7 @@
"gas": 30000000,
"value": 3000000000000000000000000,
"payload": {
- "URL": "www.dpos0009.com"
+ "Info": "www.dpos0009.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
diff --git a/sdk/test/dpos/dposstartlowq.json b/sdk/test/dpos/dposstartlowq.json
index 6978c912..d7ba7659 100644
--- a/sdk/test/dpos/dposstartlowq.json
+++ b/sdk/test/dpos/dposstartlowq.json
@@ -22,7 +22,7 @@
"gas": 30000000,
"value": 10000000000000000000000,
"payload": {
- "URL": "www.dpos0001.com"
+ "Info": "www.dpos0001.com"
},
"succeed": true
}]
@@ -51,7 +51,7 @@
"gas": 30000000,
"value": 10000000000000000000000,
"payload": {
- "URL": "www.dpos0002.com"
+ "Info": "www.dpos0002.com"
},
"succeed": true
}]
@@ -80,7 +80,7 @@
"gas": 30000000,
"value": 10000000000000000000000,
"payload": {
- "URL": "www.dpos0003.com"
+ "Info": "www.dpos0003.com"
},
"succeed": true
}]
@@ -109,7 +109,7 @@
"gas": 30000000,
"value": 10000000000000000000000,
"payload": {
- "URL": "www.dpos0004.com"
+ "Info": "www.dpos0004.com"
},
"succeed": true
}]
@@ -138,7 +138,7 @@
"gas": 30000000,
"value": 10000000000000000000000,
"payload": {
- "URL": "www.dpos0005.com"
+ "Info": "www.dpos0005.com"
},
"succeed": true
}]
@@ -167,7 +167,7 @@
"gas": 30000000,
"value": 3000000000000000000000000,
"payload": {
- "URL": "www.dpos0006.com"
+ "Info": "www.dpos0006.com"
},
"succeed": true,
"childs": [{
@@ -178,7 +178,7 @@
"gas": 30000000,
"value": 1000000000000000000000000,
"payload": {
- "URL": "www.dpos0006.com"
+ "Info": "www.dpos0006.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
@@ -219,7 +219,7 @@
"gas": 30000000,
"value": 4000000000000000000000000,
"payload": {
- "URL": "www.dpos0007.com"
+ "Info": "www.dpos0007.com"
},
"succeed": true,
"childs": [{
@@ -230,7 +230,7 @@
"gas": 30000000,
"value": 1000000000000000000000000,
"payload": {
- "URL": "www.dpos0007.com"
+ "Info": "www.dpos0007.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
@@ -271,7 +271,7 @@
"gas": 30000000,
"value": 3000000000000000000000000,
"payload": {
- "URL": "www.dpos0008.com"
+ "Info": "www.dpos0008.com"
},
"succeed": true,
"childs": [{
@@ -282,7 +282,7 @@
"gas": 30000000,
"value": 1000000000000000000000000,
"payload": {
- "URL": "www.dpos0008.com"
+ "Info": "www.dpos0008.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
@@ -326,7 +326,7 @@
"gas": 30000000,
"value": 4000000000000000000000000,
"payload": {
- "URL": "www.dpos0007.com"
+ "Info": "www.dpos0007.com"
},
"succeed": true,
"childs": [{
@@ -337,7 +337,7 @@
"gas": 30000000,
"value": 1000000000000000000000000,
"payload": {
- "URL": "www.dpos0009.com"
+ "Info": "www.dpos0009.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
diff --git a/sdk/test/dpos/dposstartonly.json b/sdk/test/dpos/dposstartonly.json
index 55275ddf..ecce97a6 100644
--- a/sdk/test/dpos/dposstartonly.json
+++ b/sdk/test/dpos/dposstartonly.json
@@ -22,7 +22,7 @@
"gas": 30000000,
"value": 20000000000000000000000,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"succeed": true,
"childs": [{
@@ -33,7 +33,7 @@
"gas": 30000000,
"value": 10000000000000000000000,
"payload": {
- "URL": "www.dpos0001.com"
+ "Info": "www.dpos0001.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
@@ -64,7 +64,7 @@
"gas": 30000000,
"value": 10000000000000000000000,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"succeed": true,
"childs": [{
@@ -75,7 +75,7 @@
"gas": 30000000,
"value": 20000000000000000000000,
"payload": {
- "URL": "www.dpos0002.com"
+ "Info": "www.dpos0002.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
@@ -105,7 +105,7 @@
"gas": 30000000,
"value": 30000000000000000000000,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true,
@@ -117,7 +117,7 @@
"gas": 30000000,
"value": 5000000000000000000000,
"payload": {
- "URL": "www.dpos0003.com"
+ "Info": "www.dpos0003.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
@@ -130,7 +130,7 @@
"gas": 30000000,
"value": 5000000000000000000000,
"payload": {
- "URL": "www.dpos0003.com"
+ "Info": "www.dpos0003.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
diff --git a/sdk/test/dpos/regcandidate.json b/sdk/test/dpos/regcandidate.json
index 2956d5a6..ef18ab9d 100644
--- a/sdk/test/dpos/regcandidate.json
+++ b/sdk/test/dpos/regcandidate.json
@@ -6,7 +6,7 @@
"gas": 30000000,
"value": 10000000000000000000000,
"payload": {
- "URL": "www.dpos0001.com"
+ "Info": "www.dpos0001.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
diff --git a/sdk/test/dpos/updatecandidate.json b/sdk/test/dpos/updatecandidate.json
index ca30e83f..3d2348e9 100644
--- a/sdk/test/dpos/updatecandidate.json
+++ b/sdk/test/dpos/updatecandidate.json
@@ -6,7 +6,7 @@
"gas": 30000000,
"value": 0,
"payload": {
- "URL": "www.dpos0001.com"
+ "Info": "www.dpos0001.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
diff --git a/sdk/test/sample.go b/sdk/test/sample.go
index d4b7015a..b6e94548 100644
--- a/sdk/test/sample.go
+++ b/sdk/test/sample.go
@@ -214,7 +214,7 @@ func sampleRegCandidate() *TTX {
AssetID: chainCfg.SysTokenID,
Value: big.NewInt(0),
Payload: &dpos.RegisterCandidate{
- URL: fmt.Sprintf("www.xxxxxx.com"),
+ Info: fmt.Sprintf("www.xxxxxx.com"),
},
Succeed: false,
Childs: []*TTX{},
@@ -232,7 +232,7 @@ func sampleUpdateCandidate() *TTX {
AssetID: chainCfg.SysTokenID,
Value: big.NewInt(0),
Payload: &dpos.UpdateCandidate{
- URL: fmt.Sprintf("www.xxxxxx.com"),
+ Info: fmt.Sprintf("www.xxxxxx.com"),
},
Succeed: true,
Childs: []*TTX{},
diff --git a/sdk/test/sample.json b/sdk/test/sample.json
index a73e454d..9d5a28d5 100644
--- a/sdk/test/sample.json
+++ b/sdk/test/sample.json
@@ -1 +1 @@
-[{"type":"createaccount","from":"fractal.founder","to":"fractal.account","gas":30000000,"value":100000000000000000000,"payload":{"accountName":"sampleact","founder":"sampleact","publicKey":"0x047db227d7094ce215c3a0f57e1bcc732551fe351f94249471934567e0f5dc1bf795962b8cccb87a2eb56b29fbe37d614e2f4c3c45b789ae4f1f51f4cb21972ffd","description":"sample account"},"succeed":true,"priv":"289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"},{"type":"regcandidate","from":"fractal.founder","to":"fractal.dpos","gas":30000000,"value":0,"payload":{"URL":"www.xxxxxx.com"},"priv":"289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"},{"type":"updatecandidate","from":"fractal.founder","to":"fractal.dpos","gas":30000000,"value":0,"payload":{"URL":"www.xxxxxx.com"},"succeed":true,"priv":"289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"},{"type":"unregcandidate","from":"sampleact","to":"fractal.dpos","gas":30000000,"value":0,"priv":"289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"},{"type":"refundcandidate","from":"fractal.founder","to":"fractal.dpos","gas":30000000,"value":0,"priv":"289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"},{"type":"votecandidate","from":"fractal.founder","to":"fractal.dpos","gas":30000000,"value":0,"payload":{"Candidate":"fractal.founder","Stake":1000000000000000000000},"succeed":true,"priv":"289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"},{"type":"kickedcandidate","from":"fractal.founder","to":"fractal.dpos","gas":30000000,"value":0,"payload":{"Candidates":["candidate"]},"succeed":true,"priv":"289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"},{"type":"exittakeover","from":"fractal.founder","to":"fractal.dpos","gas":30000000,"value":0,"succeed":true,"priv":"289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"}]
\ No newline at end of file
+[{"type":"createaccount","from":"fractal.founder","to":"fractal.account","gas":30000000,"value":100000000000000000000,"payload":{"accountName":"sampleact","founder":"sampleact","publicKey":"0x047db227d7094ce215c3a0f57e1bcc732551fe351f94249471934567e0f5dc1bf795962b8cccb87a2eb56b29fbe37d614e2f4c3c45b789ae4f1f51f4cb21972ffd","description":"sample account"},"succeed":true,"priv":"289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"},{"type":"regcandidate","from":"fractal.founder","to":"fractal.dpos","gas":30000000,"value":0,"payload":{"Info":"www.xxxxxx.com"},"priv":"289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"},{"type":"updatecandidate","from":"fractal.founder","to":"fractal.dpos","gas":30000000,"value":0,"payload":{"Info":"www.xxxxxx.com"},"succeed":true,"priv":"289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"},{"type":"unregcandidate","from":"sampleact","to":"fractal.dpos","gas":30000000,"value":0,"priv":"289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"},{"type":"refundcandidate","from":"fractal.founder","to":"fractal.dpos","gas":30000000,"value":0,"priv":"289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"},{"type":"votecandidate","from":"fractal.founder","to":"fractal.dpos","gas":30000000,"value":0,"payload":{"Candidate":"fractal.founder","Stake":1000000000000000000000},"succeed":true,"priv":"289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"},{"type":"kickedcandidate","from":"fractal.founder","to":"fractal.dpos","gas":30000000,"value":0,"payload":{"Candidates":["candidate"]},"succeed":true,"priv":"289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"},{"type":"exittakeover","from":"fractal.founder","to":"fractal.dpos","gas":30000000,"value":0,"succeed":true,"priv":"289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032"}]
\ No newline at end of file
diff --git a/sdk/test/testcase/dpos/0001regcandidate.json b/sdk/test/testcase/dpos/0001regcandidate.json
index aca4f28f..987ae5e9 100644
--- a/sdk/test/testcase/dpos/0001regcandidate.json
+++ b/sdk/test/testcase/dpos/0001regcandidate.json
@@ -22,7 +22,7 @@
"gas": 30000000,
"value": 10000000000000000000000,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": false,
@@ -36,7 +36,7 @@
"gas": 30000000,
"value": 11111000000000000000000,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": false,
@@ -50,7 +50,7 @@
"gas": 30000000,
"value": 0,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": false,
@@ -64,7 +64,7 @@
"gas": 30000000,
"value": 1000000000000000000000,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": false,
@@ -78,25 +78,25 @@
"gas": 30000000,
"value": 2000000000000000000000000,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": false,
"contain": "insufficient funds for value"
},
{
- "comment": "candidate0001 regcandidate & transfer 10000 ft, wrong url",
+ "comment": "candidate0001 regcandidate & transfer 10000 ft, wrong Info",
"type": "regcandidate",
"from": "candidate0001",
"to": "fractal.dpos",
"gas": 30000000,
"value": 10000000000000000000000,
"payload": {
- "URL": "www.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.com"
+ "Info": "www.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": false,
- "contain": "invalid url"
+ "contain": "invalid Info"
},
{
"comment": "candidate0001 regcandidate & transfer 10000 ft",
@@ -106,7 +106,7 @@
"gas": 30000000,
"value": 10000000000000000000000,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
@@ -119,7 +119,7 @@
"gas": 30000000,
"value": 10000000000000000000000,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": false,
diff --git a/sdk/test/testcase/dpos/0002updatecandidate.json b/sdk/test/testcase/dpos/0002updatecandidate.json
index ed57aef7..41816857 100644
--- a/sdk/test/testcase/dpos/0002updatecandidate.json
+++ b/sdk/test/testcase/dpos/0002updatecandidate.json
@@ -22,7 +22,7 @@
"gas": 30000000,
"value": 0,
"payload": {
- "URL": "www.candidate0002.com"
+ "Info": "www.candidate0002.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": false,
@@ -36,7 +36,7 @@
"gas": 30000000,
"value": 10000000000000000000000,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
@@ -50,25 +50,25 @@
"gas": 30000000,
"value": 0,
"payload": {
- "URL": "www.candidate0002.com"
+ "Info": "www.candidate0002.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": false,
"contain": "fractal.dpos"
},
{
- "comment": "candidate0002 updatecandidate & transfer 0 ft, wrong url",
+ "comment": "candidate0002 updatecandidate & transfer 0 ft, wrong Info",
"type": "updatecandidate",
"from": "candidate0002",
"to": "fractal.dpos",
"gas": 30000000,
"value": 0,
"payload": {
- "URL": "www.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.com"
+ "Info": "www.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.comwww.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": false,
- "contain": "invalid url"
+ "contain": "invalid Info"
}, {
"comment": "candidate0002 updatecandidate & transfer 1001 ft, non divisibility",
"type": "updatecandidate",
@@ -77,7 +77,7 @@
"gas": 30000000,
"value": 1001000000000000000000,
"payload": {
- "URL": "www.candidate0002.com"
+ "Info": "www.candidate0002.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": false,
@@ -90,7 +90,7 @@
"gas": 30000000,
"value": 1100000000000000000000000,
"payload": {
- "URL": "www.candidate0002.com"
+ "Info": "www.candidate0002.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": false,
@@ -104,7 +104,7 @@
"gas": 30000000,
"value": 0,
"payload": {
- "URL": "www.candidate0002.com"
+ "Info": "www.candidate0002.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
@@ -117,7 +117,7 @@
"gas": 30000000,
"value": 1000000000000000000000,
"payload": {
- "URL": "www.candidate0002.com"
+ "Info": "www.candidate0002.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
diff --git a/sdk/test/testcase/dpos/0003unregcandidate.json b/sdk/test/testcase/dpos/0003unregcandidate.json
index c71c1613..065eb820 100644
--- a/sdk/test/testcase/dpos/0003unregcandidate.json
+++ b/sdk/test/testcase/dpos/0003unregcandidate.json
@@ -33,7 +33,7 @@
"gas": 30000000,
"value": 10000000000000000000000,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
@@ -78,7 +78,7 @@
"gas": 30000000,
"value": 10000000000000000000000,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": false,
@@ -92,7 +92,7 @@
"gas": 30000000,
"value": 0,
"payload": {
- "URL": "www.candidate0003.com"
+ "Info": "www.candidate0003.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": false,
diff --git a/sdk/test/testcase/dpos/0004votecandidate.json b/sdk/test/testcase/dpos/0004votecandidate.json
index 1722ba70..912f489f 100644
--- a/sdk/test/testcase/dpos/0004votecandidate.json
+++ b/sdk/test/testcase/dpos/0004votecandidate.json
@@ -126,7 +126,7 @@
"gas": 30000000,
"value": 10000000000000000000000,
"payload": {
- "URL": "www.candidate0004.com"
+ "Info": "www.candidate0004.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
diff --git a/sdk/test/testcase/dpos/0005refundcandidate.json b/sdk/test/testcase/dpos/0005refundcandidate.json
index 0059e3f9..04a6dcab 100644
--- a/sdk/test/testcase/dpos/0005refundcandidate.json
+++ b/sdk/test/testcase/dpos/0005refundcandidate.json
@@ -33,7 +33,7 @@
"gas": 30000000,
"value": 10000000000000000000000,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
diff --git a/sdk/test/testcase/dpos/0006kickedcandidate.json b/sdk/test/testcase/dpos/0006kickedcandidate.json
index e0be11fe..c365add9 100644
--- a/sdk/test/testcase/dpos/0006kickedcandidate.json
+++ b/sdk/test/testcase/dpos/0006kickedcandidate.json
@@ -22,7 +22,7 @@
"gas": 30000000,
"value": 10000000000000000000000,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": true
@@ -90,7 +90,7 @@
"gas": 30000000,
"value": 10000000000000000000000,
"payload": {
- "URL": "www.xxxxxx.com"
+ "Info": "www.xxxxxx.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": false,
@@ -104,7 +104,7 @@
"gas": 30000000,
"value": 0,
"payload": {
- "URL": "www.candidate0006.com"
+ "Info": "www.candidate0006.com"
},
"priv": "289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032",
"succeed": false,
diff --git a/test/common/dpos.go b/test/common/dpos.go
index fb3b4131..2937d2bb 100644
--- a/test/common/dpos.go
+++ b/test/common/dpos.go
@@ -100,8 +100,8 @@ func (acc *Account) Transfer(to common.Name, value *big.Int, id uint64, gas uint
}
// RegCandidate
-func (acc *Account) RegCandidate(to common.Name, value *big.Int, id uint64, gas uint64, url string, state *big.Int) []byte {
- arg := &args.RegisterCandidate{URL: url}
+func (acc *Account) RegCandidate(to common.Name, value *big.Int, id uint64, gas uint64, info string, state *big.Int) []byte {
+ arg := &args.RegisterCandidate{Info: info}
payload, err := rlp.EncodeToBytes(arg)
if err != nil {
panic(err)
@@ -127,8 +127,8 @@ func (acc *Account) RegCandidate(to common.Name, value *big.Int, id uint64, gas
}
// UpdateCandidate
-func (acc *Account) UpdateCandidate(to common.Name, value *big.Int, id uint64, gas uint64, url string, state *big.Int) []byte {
- arg := &args.UpdateCandidate{URL: url}
+func (acc *Account) UpdateCandidate(to common.Name, value *big.Int, id uint64, gas uint64, info string, state *big.Int) []byte {
+ arg := &args.UpdateCandidate{Info: info}
payload, err := rlp.EncodeToBytes(arg)
if err != nil {
panic(err)
diff --git a/test/multisig/transaction_contract.go b/test/multisig/transaction_contract.go
index 97d31fd6..5d094171 100644
--- a/test/multisig/transaction_contract.go
+++ b/test/multisig/transaction_contract.go
@@ -36,9 +36,10 @@ var (
privateKey, _ = crypto.HexToECDSA("289c2857d4598e37fb9647507e47a309d6133539bf21a8b9cb6df88fd5232032")
from = common.Name("fractal.founder")
to = common.Name("fractal.account")
- aca = common.Name("accounta")
- acb = common.Name("accountb")
- acc = common.Name("accountc")
+ aca = common.Name("fcoinaccounta")
+ acb = common.Name("fcoinaccountb")
+ acc = common.Name("fcoinaccountc")
+ acd = common.Name("fcoinaccountd")
a_author_0_priv *ecdsa.PrivateKey
a_author_2_priv *ecdsa.PrivateKey
@@ -48,17 +49,22 @@ var (
c_author_0_priv *ecdsa.PrivateKey
c_author_1_priv *ecdsa.PrivateKey
c_author_2_priv *ecdsa.PrivateKey
+ d_author_0_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
+ newPrivateKey_d *ecdsa.PrivateKey
+
+ pubKey_a common.PubKey
+ pubKey_b common.PubKey
+ pubKey_c common.PubKey
+ pubKey_d common.PubKey
aNonce = uint64(0)
bNonce = uint64(0)
cNonce = uint64(0)
+ dNonce = uint64(0)
assetID = uint64(0)
nonce = uint64(0)
@@ -83,12 +89,18 @@ func generateAccount() {
c_author_0_priv = newPrivateKey_c
fmt.Println("priv_c ", hex.EncodeToString(crypto.FromECDSA(newPrivateKey_c)), " pubKey_c ", pubKey_c.String())
+ newPrivateKey_d, _ = crypto.GenerateKey()
+ pubKey_d = common.BytesToPubKey(crypto.FromECDSAPub(&newPrivateKey_d.PublicKey))
+ d_author_0_priv = newPrivateKey_d
+ fmt.Println("priv_d ", hex.EncodeToString(crypto.FromECDSA(newPrivateKey_d)), " pubKey_d ", pubKey_d.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))
+ aca = common.Name(fmt.Sprintf("fcoinaccounta%d", nonce))
+ acb = common.Name(fmt.Sprintf("fcoinaccountb%d", nonce))
+ acc = common.Name(fmt.Sprintf("fcoinaccountc%d", nonce))
+ acd = common.Name(fmt.Sprintf("fcoinaccountd%d", nonce))
key := types.MakeKeyPair(privateKey, []uint64{0})
acct := &accountmanager.CreateAccountAction{
@@ -97,7 +109,7 @@ func generateAccount() {
PublicKey: pubKey_a,
}
b, _ := rlp.EncodeToBytes(acct)
- sendTransferTx(types.CreateAccount, from, to, nonce, assetID, balance, b, []*types.KeyPair{key})
+ sendTransferTx(types.CreateAccount, from, to, nonce, assetID, balance, b, []*types.KeyPair{key}, nil, nil)
acct = &accountmanager.CreateAccountAction{
AccountName: acb,
@@ -105,7 +117,7 @@ func generateAccount() {
PublicKey: pubKey_b,
}
b, _ = rlp.EncodeToBytes(acct)
- sendTransferTx(types.CreateAccount, from, to, nonce+1, assetID, balance, b, []*types.KeyPair{key})
+ sendTransferTx(types.CreateAccount, from, to, nonce+1, assetID, balance, b, []*types.KeyPair{key}, nil, nil)
acct = &accountmanager.CreateAccountAction{
AccountName: acc,
@@ -113,27 +125,39 @@ func generateAccount() {
PublicKey: pubKey_c,
}
b, _ = rlp.EncodeToBytes(acct)
- sendTransferTx(types.CreateAccount, from, to, nonce+2, assetID, balance, b, []*types.KeyPair{key})
+ sendTransferTx(types.CreateAccount, from, to, nonce+2, assetID, balance, b, []*types.KeyPair{key}, nil, nil)
+
+ acct = &accountmanager.CreateAccountAction{
+ AccountName: acd,
+ Founder: acd,
+ PublicKey: pubKey_d,
+ }
+ b, _ = rlp.EncodeToBytes(acct)
+ sendTransferTx(types.CreateAccount, from, to, nonce+3, assetID, balance, b, []*types.KeyPair{key}, nil, nil)
for {
time.Sleep(10 * time.Second)
aexist, _ := testcommon.CheckAccountIsExist(aca)
bexist, _ := testcommon.CheckAccountIsExist(acb)
cexist, _ := testcommon.CheckAccountIsExist(acc)
+ dexist, _ := testcommon.CheckAccountIsExist(acd)
acaAccount, _ := testcommon.GetAccountByName(aca)
acbAccount, _ := testcommon.GetAccountByName(acb)
accAccount, _ := testcommon.GetAccountByName(acc)
+ acdAccount, _ := testcommon.GetAccountByName(acd)
+
fmt.Println("acaAccount version hash", acaAccount.AuthorVersion.Hex())
fmt.Println("acbAccount version hash", acbAccount.AuthorVersion.Hex())
fmt.Println("accAccount version hash", accAccount.AuthorVersion.Hex())
+ fmt.Println("accAccount version hash", acdAccount.AuthorVersion.Hex())
- if aexist && bexist && cexist {
+ if aexist && bexist && cexist && dexist {
break
}
}
- fmt.Println("aca ", aca, " acb ", acb, " acc ", acc)
+ fmt.Println("aca ", aca, " acb ", acb, " acc ", acc, " acd ", acd)
}
func init() {
@@ -170,7 +194,8 @@ func addAuthorsForAca() {
return
}
key := types.MakeKeyPair(newPrivateKey_a, []uint64{0})
- sendTransferTx(types.UpdateAccountAuthor, aca, to, aNonce, assetID, big.NewInt(0), input, []*types.KeyPair{key})
+
+ sendTransferTx(types.UpdateAccountAuthor, aca, to, aNonce, assetID, big.NewInt(0), input, []*types.KeyPair{key}, nil, nil)
}
func addAuthorsForAcb() {
@@ -192,7 +217,7 @@ func addAuthorsForAcb() {
return
}
key := types.MakeKeyPair(newPrivateKey_b, []uint64{0})
- sendTransferTx(types.UpdateAccountAuthor, acb, to, bNonce, assetID, big.NewInt(0), input, []*types.KeyPair{key})
+ sendTransferTx(types.UpdateAccountAuthor, acb, to, bNonce, assetID, big.NewInt(0), input, []*types.KeyPair{key}, nil, nil)
}
func addAuthorsForAcc() {
@@ -216,7 +241,7 @@ func addAuthorsForAcc() {
return
}
key := types.MakeKeyPair(newPrivateKey_c, []uint64{0})
- sendTransferTx(types.UpdateAccountAuthor, acc, to, cNonce, assetID, big.NewInt(0), input, []*types.KeyPair{key})
+ sendTransferTx(types.UpdateAccountAuthor, acc, to, cNonce, assetID, big.NewInt(0), input, []*types.KeyPair{key}, nil, nil)
}
func transferFromA2B() {
@@ -229,7 +254,7 @@ func transferFromA2B() {
key_1_2 := types.MakeKeyPair(b_author_2_priv, []uint64{1, 2})
aNonce++
- sendTransferTx(types.Transfer, aca, to, 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})
+ sendTransferTx(types.Transfer, aca, to, 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}, nil, nil)
}
func modifyAUpdateAUthorThreshold() {
@@ -250,7 +275,28 @@ func modifyAUpdateAUthorThreshold() {
}
aNonce++
- sendTransferTx(types.UpdateAccountAuthor, aca, to, aNonce, assetID, big.NewInt(0), input, []*types.KeyPair{key_1_0, key_1_1_0, key_1_1_1, key_1_1_2, key_2, key_3, key_1_2, key_0})
+ sendTransferTx(types.UpdateAccountAuthor, aca, to, aNonce, assetID, big.NewInt(0), input, []*types.KeyPair{key_1_0, key_1_1_0, key_1_1_1, key_1_1_2, key_2, key_3, key_1_2, key_0}, nil, nil)
+}
+
+func transferFromA2BWithBAsPayer() {
+ 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})
+
+ gasPrice, _ := testcommon.GasPrice()
+ fp := &types.FeePayer{
+ GasPrice: gasPrice,
+ Payer: acd,
+ Sign: &types.Signature{0, make([]*types.SignData, 0)},
+ }
+ payerKey := types.MakeKeyPair(newPrivateKey_d, []uint64{0})
+
+ aNonce++
+ sendTransferTx(types.Transfer, aca, to, 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}, fp, []*types.KeyPair{payerKey})
}
func main() {
@@ -276,11 +322,16 @@ func main() {
transferFromA2B()
modifyAUpdateAUthorThreshold()
+
+ transferFromA2BWithBAsPayer()
}
-func sendTransferTx(txType types.ActionType, from, to common.Name, nonce, assetID uint64, value *big.Int, input []byte, keys []*types.KeyPair) {
+func sendTransferTx(txType types.ActionType, from, to common.Name, nonce, assetID uint64, value *big.Int, input []byte, keys []*types.KeyPair, fp *types.FeePayer, payerKeys []*types.KeyPair) {
action := types.NewAction(txType, from, to, nonce, assetID, gasLimit, value, input, nil)
gasprice, _ := testcommon.GasPrice()
+ if fp != nil {
+ gasprice = big.NewInt(0)
+ }
tx := types.NewTransaction(0, gasprice, action)
signer := types.MakeSigner(big.NewInt(1))
@@ -289,6 +340,13 @@ func sendTransferTx(txType types.ActionType, from, to common.Name, nonce, assetI
jww.ERROR.Fatalln(err)
}
+ if fp != nil {
+ err = types.SignPayerActionWithMultiKey(action, tx, signer, fp, 0, payerKeys)
+ if err != nil {
+ jww.ERROR.Fatalln(err)
+ }
+ }
+
rawtx, err := rlp.EncodeToBytes(tx)
if err != nil {
jww.ERROR.Fatalln(err)
diff --git a/txpool/config.go b/txpool/config.go
index 328291d0..5b661af1 100644
--- a/txpool/config.go
+++ b/txpool/config.go
@@ -48,7 +48,7 @@ type Config struct {
var DefaultTxPoolConfig = &Config{
Journal: "transactions.rlp",
Rejournal: time.Hour,
- PriceLimit: 1000000000,
+ PriceLimit: 100000000000,
PriceBump: 10,
AccountSlots: 128,
GlobalSlots: 4096,
diff --git a/txpool/error.go b/txpool/error.go
index abb05b26..9d34c5d8 100644
--- a/txpool/error.go
+++ b/txpool/error.go
@@ -24,10 +24,10 @@ var (
// ErrInvalidSender is returned if the transaction contains an invalid signature.
ErrInvalidSender = errors.New("invalid sender")
+ ErrPayerTx = errors.New("payer is exist")
// ErrNonceTooLow is returned if the nonce of a transaction is lower than the
// one present in the local chain.
ErrNonceTooLow = errors.New("nonce too low")
-
// ErrUnderpriced is returned if a transaction's gas price is below the minimum
// configured for the transaction pool.
ErrUnderpriced = errors.New("transaction underpriced")
diff --git a/txpool/test_utils.go b/txpool/test_utils.go
index ae14c536..940fd2c3 100644
--- a/txpool/test_utils.go
+++ b/txpool/test_utils.go
@@ -84,6 +84,32 @@ func transaction(nonce uint64, from, to common.Name, gaslimit uint64, key *ecdsa
return pricedTransaction(nonce, from, to, gaslimit, big.NewInt(1), key)
}
+func extendTransaction(nonce uint64, payer, from, to common.Name, gasLimit uint64, key, payerKey *ecdsa.PrivateKey) *types.Transaction {
+
+ fp := &types.FeePayer{
+ GasPrice: big.NewInt(100),
+ Payer: payer,
+ Sign: &types.Signature{0, make([]*types.SignData, 0)},
+ }
+
+ action := types.NewAction(types.Transfer, from, to, nonce, 0, gasLimit, big.NewInt(100), nil, nil)
+ tx := types.NewTransaction(0, big.NewInt(0), action)
+ signer := types.MakeSigner(params.DefaultChainconfig.ChainID)
+ keyPair := types.MakeKeyPair(key, []uint64{0})
+ err := types.SignActionWithMultiKey(action, tx, signer, 0, []*types.KeyPair{keyPair})
+ if err != nil {
+ panic(err)
+ }
+
+ payerKeyPair := types.MakeKeyPair(payerKey, []uint64{0})
+ err = types.SignPayerActionWithMultiKey(action, tx, signer, fp, 0, []*types.KeyPair{payerKeyPair})
+ if err != nil {
+ panic(err)
+ }
+
+ return tx
+}
+
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))
keyPair := types.MakeKeyPair(key, []uint64{0})
diff --git a/txpool/txpool.go b/txpool/txpool.go
index 9c98083c..e3ceb9c5 100644
--- a/txpool/txpool.go
+++ b/txpool/txpool.go
@@ -651,6 +651,7 @@ func (tp *TxPool) local() map[common.Name][]*types.Transaction {
func (tp *TxPool) validateTx(tx *types.Transaction, local bool) error {
validateAction := func(tx *types.Transaction, action *types.Action) error {
from := action.Sender()
+
// Drop non-local transactions under our own minimal accepted gas price
local = local || tp.locals.contains(from) // account may be local even if the transaction arrived from the network
if !local && tp.gasPrice.Cmp(tx.GasPrice()) > 0 {
@@ -666,10 +667,27 @@ func (tp *TxPool) validateTx(tx *types.Transaction, local bool) error {
return ErrNonceTooLow
}
- // Transactor should have enough funds to cover the gas costs
- balance, err := tp.curAccountManager.GetAccountBalanceByID(from, tx.GasAssetID(), 0)
- if err != nil {
- return err
+ // wait fork successed, remove it
+ if action.PayerIsExist() && tp.chain.CurrentBlock().CurForkID() < params.ForkID4 {
+ return fmt.Errorf("This type of transaction: %v is not currently supported", tx.Hash().Hex())
+ }
+
+ var balance *big.Int
+ if tx.PayerExist() {
+ // Transactor should have enough funds to cover the gas costs
+ balance, err = tp.curAccountManager.GetAccountBalanceByID(action.Payer(), tx.GasAssetID(), 0)
+ if err != nil {
+ return err
+ }
+ } else {
+ if action.PayerIsExist() {
+ return ErrPayerTx
+ }
+ // Transactor should have enough funds to cover the gas costs
+ balance, err = tp.curAccountManager.GetAccountBalanceByID(from, tx.GasAssetID(), 0)
+ if err != nil {
+ return err
+ }
}
gascost := new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(action.Gas()))
@@ -685,7 +703,9 @@ func (tp *TxPool) validateTx(tx *types.Transaction, local bool) error {
value := action.Value()
if tp.config.GasAssetID == action.AssetID() {
- value.Add(value, gascost)
+ if !tx.PayerExist() {
+ value.Add(value, gascost)
+ }
}
if balance.Cmp(value) < 0 {
@@ -751,7 +771,6 @@ func (tp *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
}
// If the transaction is replacing an already pending one, do directly
- // todo Change action
from := tx.GetActions()[0].Sender()
if list := tp.pending[from]; list != nil && list.Overlaps(tx) {
// Nonce already pending, check if required price bump is met
@@ -764,6 +783,7 @@ func (tp *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
tp.all.Remove(old.Hash())
tp.priced.Removed(1)
}
+
tp.all.Add(tx)
tp.priced.Put(tx)
tp.journalTx(from, tx)
@@ -913,13 +933,13 @@ func (tp *TxPool) addTxs(txs []*types.Transaction, local, sync bool) []error {
// Cache senders in transactions before obtaining lock (pool.signer is immutable)
for index, tx := range txs {
// If the transaction is already known, discard it
- if tp.all.Get(tx.Hash()) != nil {
+ if storgeTx := tp.all.Get(tx.Hash()); storgeTx != nil && tx.GasPrice().Cmp(storgeTx.GasPrice()) == 0 {
log.Trace("Discarding already known transaction", "hash", tx.Hash())
errs[index] = errors.New("already known transaction")
continue
}
- if err := tx.Check(tp.chain.Config()); err != nil {
+ if err := tx.Check(tp.chain.CurrentBlock().Header().CurForkID(), tp.chain.Config()); err != nil {
log.Trace("add txs check ", "err", err, "hash", tx.Hash())
errs[index] = fmt.Errorf("transaction check err: %v", err)
continue
diff --git a/txpool/txpool_test.go b/txpool/txpool_test.go
index d471fb38..69c554c5 100644
--- a/txpool/txpool_test.go
+++ b/txpool/txpool_test.go
@@ -44,6 +44,67 @@ func TestConfigCheck(t *testing.T) {
assert.Equal(t, cfg.check(), *DefaultTxPoolConfig)
}
+// func TestAddPayerTx(t *testing.T) {
+// var (
+// statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()))
+// manager, _ = am.NewAccountManager(statedb)
+// fname = common.Name("fromname")
+// tname = common.Name("totestname")
+// fkey = generateAccount(t, fname, manager)
+// tkey = generateAccount(t, tname, manager)
+// asset = asset.NewAsset(statedb)
+// )
+
+// // issue asset
+// if _, err := asset.IssueAsset("ft", 0, 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)
+// }
+
+// // add balance
+// if err := manager.AddAccountBalanceByName(fname, "ft", new(big.Int).SetUint64(params.Fractal)); err != nil {
+// t.Fatal(err)
+// }
+
+// if err := manager.AddAccountBalanceByName(tname, "ft", new(big.Int).SetUint64(params.Fractal)); err != nil {
+// t.Fatal(err)
+// }
+
+// blockchain := &testBlockChain{statedb, 1000000000, new(event.Feed)}
+// tx0 := pricedTransaction(0, fname, tname, 109000, big.NewInt(0), fkey)
+// tx1 := extendTransaction(0, tname, fname, tname, 109000, fkey, tkey)
+
+// params.DefaultChainconfig.SysTokenID = 0
+
+// pool := New(testTxPoolConfig, params.DefaultChainconfig, blockchain)
+// defer pool.Stop()
+
+// nonce, err := pool.State().GetNonce(fname)
+// if err != nil {
+// t.Fatal("Invalid getNonce ", err)
+// }
+// if nonce != 0 {
+// t.Fatalf("Invalid nonce, want 0, got %d", nonce)
+// }
+
+// errs := pool.addRemotesSync([]*types.Transaction{tx0, tx1})
+
+// t.Log(errs)
+// nonce, err = pool.State().GetNonce(fname)
+// if err != nil {
+// t.Fatal("Invalid getNonce ", err)
+// }
+
+// if nonce != 1 {
+// t.Fatalf("Invalid nonce, want 1, got %d", nonce)
+// }
+
+// result := pool.Get(tx1.Hash())
+
+// if !result.PayerExist() {
+// t.Fatal("add payer tx failed")
+// }
+// }
+
// This test simulates a scenario where a new block is imported during a
// state reset and tests whether the pending state is in sync with the
// block head event that initiated the resetState().
diff --git a/txpool/utils.go b/txpool/utils.go
index 0353d24c..fc14bdd6 100644
--- a/txpool/utils.go
+++ b/txpool/utils.go
@@ -101,6 +101,9 @@ func IntrinsicGas(accountDB *accountmanager.AccountManager, action *types.Action
gas += (uint64(len(action.GetSign()) - 1)) * gasTable.SignGas
}
+ payerSignLen := len(action.GetFeePayerSign())
+ gas += uint64(payerSignLen) * gasTable.SignGas
+
if action.Value().Sign() != 0 {
gas += receiptGasFunc(action)
}
diff --git a/types/action.go b/types/action.go
index 6d47fd3a..61276d76 100644
--- a/types/action.go
+++ b/types/action.go
@@ -80,6 +80,9 @@ const (
RefundCandidate
// VoteCandidate repesents voter vote candidate action.
VoteCandidate
+
+ // UpdateCandidatePubKey repesents update candidate action.
+ UpdateCandidatePubKey
)
const (
@@ -108,6 +111,12 @@ type SignData struct {
Index []uint64
}
+type FeePayer struct {
+ GasPrice *big.Int
+ Payer common.Name
+ Sign *Signature
+}
+
type actionData struct {
AType ActionType
Nonce uint64
@@ -118,16 +127,20 @@ type actionData struct {
Amount *big.Int
Payload []byte
Remark []byte
+ Sign *Signature
- Sign *Signature
+ Extend []rlp.RawValue `rlp:"tail"`
}
// Action represents an entire action in the transaction.
type Action struct {
data actionData
// cache
+ fp *FeePayer
hash atomic.Value
+ extendHash atomic.Value
senderPubkeys atomic.Value
+ payerPubkeys atomic.Value
author atomic.Value
}
@@ -147,6 +160,7 @@ func NewAction(actionType ActionType, from, to common.Name, nonce, assetID, gasL
Payload: payload,
Remark: remark,
Sign: &Signature{0, make([]*SignData, 0)},
+ Extend: make([]rlp.RawValue, 0),
}
if amount != nil {
data.Amount.Set(amount)
@@ -166,8 +180,40 @@ func (a *Action) GetSignParent() uint64 {
return a.data.Sign.ParentIndex
}
+func (a *Action) PayerIsExist() bool {
+ return a.fp != nil
+}
+
+func (a *Action) PayerGasPrice() *big.Int {
+ if a.fp != nil {
+ return a.fp.GasPrice
+ }
+ return nil
+}
+
+func (a *Action) Payer() common.Name {
+ if a.fp != nil {
+ return a.fp.Payer
+ }
+ return common.Name("")
+}
+
+func (a *Action) PayerSignature() *Signature {
+ if a.fp != nil {
+ return a.fp.Sign
+ }
+ return nil
+}
+
+func (a *Action) GetFeePayerSign() []*SignData {
+ if a.fp != nil {
+ return a.fp.Sign.SignData
+ }
+ return nil
+}
+
// Check the validity of all fields
-func (a *Action) Check(conf *params.ChainConfig) error {
+func (a *Action) Check(fid uint64, conf *params.ChainConfig) error {
//check To
switch a.Type() {
case CreateContract:
@@ -203,6 +249,11 @@ func (a *Action) Check(conf *params.ChainConfig) error {
}
case Transfer:
//dpos
+ case UpdateCandidatePubKey:
+ if fid < params.ForkID4 {
+ return fmt.Errorf("Receipt undefined")
+ }
+ fallthrough
case RegCandidate:
fallthrough
case UpdateCandidate:
@@ -279,14 +330,50 @@ func (a *Action) Gas() uint64 { return a.data.GasLimit }
// Value returns action's Value.
func (a *Action) Value() *big.Int { return new(big.Int).Set(a.data.Amount) }
+func (a *Action) Extend() []rlp.RawValue { return a.data.Extend }
+
+// IgnoreExtend returns ignore extend
+func (a *Action) IgnoreExtend() []interface{} {
+ return []interface{}{
+ a.data.AType,
+ a.data.Nonce,
+ a.data.AssetID,
+ a.data.From,
+ a.data.To,
+ a.data.GasLimit,
+ a.data.Amount,
+ a.data.Payload,
+ a.data.Remark,
+ a.data.Sign,
+ }
+}
+
// EncodeRLP implements rlp.Encoder
func (a *Action) EncodeRLP(w io.Writer) error {
+ if a.fp != nil {
+ value, err := rlp.EncodeToBytes(a.fp)
+ if err != nil {
+ return err
+ }
+ a.data.Extend = []rlp.RawValue{value}
+ }
+
return rlp.Encode(w, &a.data)
}
// DecodeRLP implements rlp.Decoder
func (a *Action) DecodeRLP(s *rlp.Stream) error {
- return s.Decode(&a.data)
+ if err := s.Decode(&a.data); err != nil {
+ return err
+ }
+
+ if len(a.data.Extend) != 0 {
+ a.fp = new(FeePayer)
+ return rlp.DecodeBytes(a.data.Extend[0], a.fp)
+
+ }
+
+ return nil
}
// ChainID returns which chain id this action was signed for (if at all)
@@ -299,11 +386,21 @@ func (a *Action) Hash() common.Hash {
if hash := a.hash.Load(); hash != nil {
return hash.(common.Hash)
}
- v := RlpHash(a)
+ v := RlpHash(a.IgnoreExtend())
a.hash.Store(v)
return v
}
+// ExtendHash hashes the RLP encoding of action.
+func (a *Action) ExtendHash() common.Hash {
+ if hash := a.extendHash.Load(); hash != nil {
+ return hash.(common.Hash)
+ }
+ v := RlpHash(a)
+ a.extendHash.Store(v)
+ return v
+}
+
// WithSignature returns a new transaction with the given signature.
func (a *Action) WithSignature(signer Signer, sig []byte, index []uint64) error {
r, s, v, err := signer.SignatureValues(sig)
@@ -314,11 +411,32 @@ func (a *Action) WithSignature(signer Signer, sig []byte, index []uint64) error
return nil
}
-// WithSignature returns a new transaction with the given signature.
+// WithParentIndex returns a new transaction with the given signature.
func (a *Action) WithParentIndex(parentIndex uint64) {
a.data.Sign.ParentIndex = parentIndex
}
+func (f *FeePayer) WithSignature(signer Signer, sig []byte, index []uint64) error {
+ r, s, v, err := signer.SignatureValues(sig)
+ if err != nil {
+ return err
+ }
+ f.Sign.SignData = append(f.Sign.SignData, &SignData{R: r, S: s, V: v, Index: index})
+ return nil
+}
+
+func (f *FeePayer) WithParentIndex(parentIndex uint64) {
+ f.Sign.ParentIndex = parentIndex
+}
+
+func (f *FeePayer) GetSignParent() uint64 {
+ return f.Sign.ParentIndex
+}
+
+func (f *FeePayer) GetSignIndex(i uint64) []uint64 {
+ return f.Sign.SignData[i].Index
+}
+
// RPCAction represents a action that will serialize to the RPC representation of a action.
type RPCAction struct {
Type uint64 `json:"type"`
@@ -355,6 +473,57 @@ func (a *Action) NewRPCAction(index uint64) *RPCAction {
}
}
+type RPCActionWithPayer struct {
+ Type uint64 `json:"type"`
+ Nonce uint64 `json:"nonce"`
+ From common.Name `json:"from"`
+ To common.Name `json:"to"`
+ AssetID uint64 `json:"assetID"`
+ GasLimit uint64 `json:"gas"`
+ Amount *big.Int `json:"value"`
+ Remark hexutil.Bytes `json:"remark"`
+ Payload hexutil.Bytes `json:"payload"`
+ Hash common.Hash `json:"actionHash"`
+ ActionIdex uint64 `json:"actionIndex"`
+ Payer common.Name `json:"payer"`
+ PayerGasPrice *big.Int `json:"payerGasPrice"`
+ ParentIndex uint64 `json:"parentIndex"`
+ PayerParentIndex uint64 `json:"payerParentIndex"`
+}
+
+func (a *RPCActionWithPayer) SetHash(hash common.Hash) {
+ a.Hash = hash
+}
+
+func (a *Action) NewRPCActionWithPayer(index uint64) *RPCActionWithPayer {
+ var payer common.Name
+ var price *big.Int
+ if a.fp != nil {
+ payer = a.fp.Payer
+ price = a.fp.GasPrice
+ }
+ ap := &RPCActionWithPayer{
+ Type: uint64(a.Type()),
+ Nonce: a.Nonce(),
+ From: a.Sender(),
+ To: a.Recipient(),
+ AssetID: a.AssetID(),
+ GasLimit: a.Gas(),
+ Amount: a.Value(),
+ Remark: hexutil.Bytes(a.Remark()),
+ Payload: hexutil.Bytes(a.Data()),
+ Hash: a.Hash(),
+ ActionIdex: index,
+ Payer: payer,
+ PayerGasPrice: price,
+ ParentIndex: a.GetSignParent(),
+ }
+ if a.fp != nil {
+ ap.PayerParentIndex = a.fp.GetSignParent()
+ }
+ return ap
+}
+
// deriveChainID derives the chain id from the given v parameter
func deriveChainID(v *big.Int) *big.Int {
v = new(big.Int).Sub(v, big.NewInt(35))
diff --git a/types/action_test.go b/types/action_test.go
index 87f26b59..a754486f 100644
--- a/types/action_test.go
+++ b/types/action_test.go
@@ -99,8 +99,8 @@ func TestActionEncodeAndDecode(t *testing.T) {
if err := rlp.Decode(bytes.NewReader(actionBytes), &actAction); err != nil {
t.Fatal(err)
}
-
assert.Equal(t, testAction, actAction)
+
}
func TestAction_Check(t *testing.T) {
@@ -114,7 +114,7 @@ func TestAction_Check(t *testing.T) {
t.Fatal(err)
}
- if err := actAction.Check(params.DefaultChainconfig); err != nil {
+ if err := actAction.Check(params.NextForkID, params.DefaultChainconfig); err != nil {
t.Errorf("TestAction_CheckValue err, wantErr %v", true)
}
@@ -129,7 +129,7 @@ func TestAction_Check(t *testing.T) {
t.Fatal(err)
}
- if err := actAction2.Check(params.DefaultChainconfig); err == nil {
+ if err := actAction2.Check(params.NextForkID, params.DefaultChainconfig); err == nil {
t.Errorf("TestAction2_CheckValue err, wantErr %v", false)
}
@@ -144,7 +144,7 @@ func TestAction_Check(t *testing.T) {
t.Fatal(err)
}
- if err := actAction3.Check(params.DefaultChainconfig); err != nil {
+ if err := actAction3.Check(params.NextForkID, params.DefaultChainconfig); err != nil {
t.Errorf("TestAction3_CheckValue err, wantErr %v", false)
}
@@ -159,7 +159,7 @@ func TestAction_Check(t *testing.T) {
t.Fatal(err)
}
- if err := actAction3.Check(params.DefaultChainconfig); err != nil {
+ if err := actAction3.Check(params.NextForkID, params.DefaultChainconfig); err != nil {
t.Errorf("TestAction3_CheckValue err, wantErr %v", false)
}
@@ -174,7 +174,7 @@ func TestAction_Check(t *testing.T) {
t.Fatal(err)
}
- if err := actAction3.Check(params.DefaultChainconfig); err != nil {
+ if err := actAction3.Check(params.NextForkID, params.DefaultChainconfig); err != nil {
t.Errorf("TestAction3_CheckValue err, wantErr %v", false)
}
}
@@ -252,23 +252,23 @@ func TestAction_Check2(t *testing.T) {
// t.Fatal(err)
// }
- if err := testAction10.Check(params.DefaultChainconfig); err != nil {
+ if err := testAction10.Check(params.NextForkID, params.DefaultChainconfig); err != nil {
t.Errorf("TestAction3_CheckValue err, wantErr %v", false)
}
- if err := testAction11.Check(params.DefaultChainconfig); err != nil {
+ if err := testAction11.Check(params.NextForkID, params.DefaultChainconfig); err != nil {
t.Errorf("TestAction3_CheckValue err, wantErr %v", false)
}
- if err := testAction12.Check(params.DefaultChainconfig); err != nil {
+ if err := testAction12.Check(params.NextForkID, params.DefaultChainconfig); err != nil {
t.Errorf("TestAction3_CheckValue err, wantErr %v", false)
}
- if err := testAction13.Check(params.DefaultChainconfig); err != nil {
+ if err := testAction13.Check(params.NextForkID, params.DefaultChainconfig); err != nil {
t.Errorf("TestAction3_CheckValue err, wantErr %v", false)
}
- if err := testAction14.Check(params.DefaultChainconfig); err != nil {
+ if err := testAction14.Check(params.NextForkID, params.DefaultChainconfig); err != nil {
t.Errorf("TestAction3_CheckValue err, wantErr %v", false)
}
@@ -347,23 +347,23 @@ func TestAction_Check3(t *testing.T) {
// t.Fatal(err)
// }
- if err := testAction20.Check(params.DefaultChainconfig); err == nil {
+ if err := testAction20.Check(params.NextForkID, params.DefaultChainconfig); err == nil {
t.Errorf("TestAction3_CheckValue err, wantErr %v", false)
}
- if err := testAction21.Check(params.DefaultChainconfig); err == nil {
+ if err := testAction21.Check(params.NextForkID, params.DefaultChainconfig); err == nil {
t.Errorf("TestAction3_CheckValue err, wantErr %v", false)
}
- if err := testAction22.Check(params.DefaultChainconfig); err == nil {
+ if err := testAction22.Check(params.NextForkID, params.DefaultChainconfig); err == nil {
t.Errorf("TestAction3_CheckValue err, wantErr %v", false)
}
- if err := testAction23.Check(params.DefaultChainconfig); err == nil {
+ if err := testAction23.Check(params.NextForkID, params.DefaultChainconfig); err == nil {
t.Errorf("TestAction3_CheckValue err, wantErr %v", false)
}
- if err := testAction24.Check(params.DefaultChainconfig); err == nil {
+ if err := testAction24.Check(params.NextForkID, params.DefaultChainconfig); err == nil {
t.Errorf("TestAction3_CheckValue err, wantErr %v", false)
}
diff --git a/types/block.go b/types/block.go
index 61af0727..3e69bf2f 100644
--- a/types/block.go
+++ b/types/block.go
@@ -26,6 +26,7 @@ import (
"sync/atomic"
"github.com/fractalplatform/fractal/common"
+ "github.com/fractalplatform/fractal/params"
"github.com/fractalplatform/fractal/utils/rlp"
)
@@ -93,17 +94,22 @@ func NewBlock(header *Header, txs []*Transaction, receipts []*Receipt) *Block {
}
b := &Block{Head: header}
- b.Head.TxsRoot = DeriveTxsMerkleRoot(txs)
+
+ if header.ForkID.Cur >= params.ForkID4 {
+ b.Head.TxsRoot = DeriveExtensTxsMerkleRoot(txs)
+ } else {
+ b.Head.TxsRoot = DeriveTxsMerkleRoot(txs)
+ }
+
b.Head.ReceiptsRoot = DeriveReceiptsMerkleRoot(receipts)
b.Txs = make([]*Transaction, len(txs))
copy(b.Txs, txs)
b.Head.Bloom = CreateBloom(receipts)
-
return b
}
-// NewBlockWithHeader creates a block with the given header data. The
+// NewBlockWithHeader create s a block with the given header data. The
// header data is copied, changes to header and to the field values
// will not affect the block.
func NewBlockWithHeader(header *Header) *Block {
@@ -288,6 +294,15 @@ func (bs blockSorter) Less(i, j int) bool { return bs.by(bs.blocks[i], bs.blocks
// Number represents block sort by number.
func Number(b1, b2 *Block) bool { return b1.Head.Number.Cmp(b2.Head.Number) < 0 }
+// DeriveExtensTxsMerkleRoot returns Extens txs merkle tree root hash.
+func DeriveExtensTxsMerkleRoot(txs []*Transaction) common.Hash {
+ var txHashs []common.Hash
+ for i := 0; i < len(txs); i++ {
+ txHashs = append(txHashs, txs[i].ExtensHash())
+ }
+ return common.MerkleRoot(txHashs)
+}
+
// DeriveTxsMerkleRoot returns txs merkle tree root hash.
func DeriveTxsMerkleRoot(txs []*Transaction) common.Hash {
var txHashs []common.Hash
diff --git a/types/receipt.go b/types/receipt.go
index fc0b0865..6f6a7cac 100644
--- a/types/receipt.go
+++ b/types/receipt.go
@@ -17,6 +17,8 @@
package types
import (
+ "math/big"
+
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/fractalplatform/fractal/common"
"github.com/fractalplatform/fractal/utils/rlp"
@@ -67,6 +69,36 @@ func (a *ActionResult) NewRPCActionResult(aType ActionType) *RPCActionResult {
}
}
+type RPCActionResultWithPayer struct {
+ ActionType uint64 `json:"actionType"`
+ Status uint64 `json:"status"`
+ Index uint64 `json:"index"`
+ GasUsed uint64 `json:"gasUsed"`
+ GasAllot []*GasDistribution `json:"gasAllot"`
+ Error string `json:"error"`
+ Payer common.Name `json:"payer"`
+ PayerGasPrice *big.Int `json:"payerGasPrice"`
+}
+
+// NewRPCActionResult returns a ActionResult that will serialize to the RPC.
+func (a *ActionResult) NewRPCActionResultWithPayer(action *Action, gasPrice *big.Int) *RPCActionResultWithPayer {
+ var payer = action.Sender()
+ var price = gasPrice
+ if action.fp != nil {
+ payer = action.fp.Payer
+ }
+ return &RPCActionResultWithPayer{
+ ActionType: uint64(action.Type()),
+ Status: a.Status,
+ Index: a.Index,
+ GasUsed: a.GasUsed,
+ GasAllot: a.GasAllot,
+ Error: a.Error,
+ Payer: payer,
+ PayerGasPrice: price,
+ }
+}
+
// Receipt represents the results of a transaction.
type Receipt struct {
PostState []byte
@@ -137,6 +169,48 @@ func (r *Receipt) NewRPCReceipt(blockHash common.Hash, blockNumber uint64, index
return result
}
+// RPCReceipt that will serialize to the RPC representation of a Receipt.
+type RPCReceiptWithPayer struct {
+ BlockHash common.Hash `json:"blockHash"`
+ BlockNumber uint64 `json:"blockNumber"`
+ Hash common.Hash `json:"txHash"`
+ TransactionIndex uint64 `json:"transactionIndex"`
+ PostState hexutil.Bytes `json:"postState"`
+ ActionResults []*RPCActionResultWithPayer `json:"actionResults"`
+ CumulativeGasUsed uint64 `json:"cumulativeGasUsed"`
+ TotalGasUsed uint64 `json:"totalGasUsed"`
+ Bloom Bloom `json:"logsBloom"`
+ Logs []*RPCLog `json:"logs"`
+}
+
+// NewRPCReceipt returns a Receipt that will serialize to the RPC.
+func (r *Receipt) NewRPCReceiptWithPayer(blockHash common.Hash, blockNumber uint64, index uint64, tx *Transaction) *RPCReceiptWithPayer {
+ result := &RPCReceiptWithPayer{
+ BlockHash: blockHash,
+ BlockNumber: blockNumber,
+ Hash: tx.Hash(),
+ TransactionIndex: index,
+ PostState: hexutil.Bytes(r.PostState),
+ CumulativeGasUsed: r.CumulativeGasUsed,
+ TotalGasUsed: r.TotalGasUsed,
+ Bloom: r.Bloom,
+ }
+
+ var rpcActionResults []*RPCActionResultWithPayer
+ for i, a := range tx.GetActions() {
+ rpcActionResults = append(rpcActionResults, r.ActionResults[i].NewRPCActionResultWithPayer(a, tx.GasPrice()))
+ }
+ result.ActionResults = rpcActionResults
+
+ var rlogs []*RPCLog
+ for _, l := range r.Logs {
+ rlogs = append(rlogs, l.NewRPCLog())
+ }
+ result.Logs = rlogs
+
+ return result
+}
+
// ConsensusReceipt returns consensus encoding of a receipt.
func (r *Receipt) ConsensusReceipt() *Receipt {
result := &Receipt{
diff --git a/types/signer.go b/types/signer.go
index c06550b2..616907d6 100644
--- a/types/signer.go
+++ b/types/signer.go
@@ -24,6 +24,7 @@ import (
"github.com/fractalplatform/fractal/common"
"github.com/fractalplatform/fractal/crypto"
+ "github.com/fractalplatform/fractal/utils/rlp"
)
var (
@@ -88,6 +89,47 @@ func RecoverMultiKey(signer Signer, a *Action, tx *Transaction) ([]common.PubKey
return pubKeys, nil
}
+func SignPayerActionWithMultiKey(a *Action, tx *Transaction, s Signer, feePayer *FeePayer, parentIndex uint64, keys []*KeyPair) error {
+ a.fp = feePayer
+ h := s.FeePayerHash(tx)
+ for _, key := range keys {
+ sig, err := crypto.Sign(h[:], key.priv)
+ if err != nil {
+ return err
+ }
+
+ err = feePayer.WithSignature(s, sig, key.index)
+ if err != nil {
+ return err
+ }
+ }
+ feePayer.WithParentIndex(parentIndex)
+
+ if value, err := rlp.EncodeToBytes(feePayer); err != nil {
+ return err
+ } else {
+ a.data.Extend = append(a.data.Extend, value)
+ }
+
+ return nil
+}
+
+func RecoverPayerMultiKey(signer Signer, a *Action, tx *Transaction) ([]common.PubKey, error) {
+ if sc := a.payerPubkeys.Load(); sc != nil {
+ sigCache := sc.(sigCache)
+ if sigCache.signer.Equal(signer) {
+ return sigCache.pubKeys, nil
+ }
+ }
+
+ pubKeys, err := signer.PayerPubKeys(a, tx)
+ if err != nil {
+ return []common.PubKey{}, err
+ }
+ a.payerPubkeys.Store(sigCache{signer: signer, pubKeys: pubKeys})
+ return pubKeys, nil
+}
+
func StoreAuthorCache(a *Action, authorVersion map[common.Name]common.Hash) {
a.author.Store(authorVersion)
}
@@ -147,6 +189,25 @@ func (s Signer) PubKeys(a *Action, tx *Transaction) ([]common.PubKey, error) {
return pubKeys, nil
}
+func (s Signer) PayerPubKeys(a *Action, tx *Transaction) ([]common.PubKey, error) {
+ if len(a.fp.Sign.SignData) == 0 {
+ return nil, ErrSignEmpty
+ }
+
+ var pubKeys []common.PubKey
+ for _, sign := range a.fp.Sign.SignData {
+ V := new(big.Int).Sub(sign.V, s.chainIDMul)
+ V.Sub(V, big8)
+ data, err := recoverPlain(s.FeePayerHash(tx), sign.R, sign.S, V)
+ 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
// needs to be in the [R || S || V] format where V is 0 or 1.
func (s Signer) SignatureValues(sig []byte) (R, S, V *big.Int, err error) {
@@ -190,6 +251,33 @@ func (s Signer) Hash(tx *Transaction) common.Hash {
})
}
+func (s Signer) FeePayerHash(tx *Transaction) common.Hash {
+ actionHashs := make([]common.Hash, len(tx.GetActions()))
+ for i, a := range tx.GetActions() {
+ hash := RlpHash([]interface{}{
+ a.data.From,
+ a.data.AType,
+ a.data.Nonce,
+ a.data.To,
+ a.data.GasLimit,
+ a.data.Amount,
+ a.data.Payload,
+ a.data.AssetID,
+ a.data.Remark,
+ a.fp.Payer,
+ a.fp.GasPrice,
+ s.chainID, uint(0), uint(0),
+ })
+ actionHashs[i] = hash
+ }
+
+ return RlpHash([]interface{}{
+ common.MerkleRoot(actionHashs),
+ tx.gasAssetID,
+ tx.gasPrice,
+ })
+}
+
func recoverPlain(sighash common.Hash, R, S, Vb *big.Int) ([]byte, error) {
if Vb.BitLen() > 8 {
return nil, ErrInvalidSig
diff --git a/types/signer_test.go b/types/signer_test.go
index 1c487e94..824f002f 100644
--- a/types/signer_test.go
+++ b/types/signer_test.go
@@ -62,6 +62,49 @@ func TestSigningMultiKey(t *testing.T) {
}
}
+func TestSigningPayerMultiKey(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))
+ fp := &FeePayer{
+ GasPrice: big.NewInt(0),
+ Payer: testTx.GetActions()[0].Recipient(),
+ Sign: &Signature{0, make([]*SignData, 0)},
+ }
+ if err := SignPayerActionWithMultiKey(testTx.GetActions()[0], testTx, signer, fp, 0, keys); err != nil {
+ t.Fatal(err)
+ }
+
+ pubkeys, err := RecoverPayerMultiKey(signer, testTx.GetActions()[0], testTx)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for i, pubkey := range pubkeys {
+ if pubkey.Compare(pubs[i]) != 0 {
+ t.Errorf("exected from and pubkey to be equal. Got %x want %x", pubkey, pubs[i])
+ }
+ }
+
+ //test cache
+ pubkeys, err = RecoverPayerMultiKey(signer, testTx.GetActions()[0], testTx)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for i, pubkey := range pubkeys {
+ if pubkey.Compare(pubs[i]) != 0 {
+ t.Errorf("exected from and pubkey to be equal. Got %x want %x", pubkey, pubs[i])
+ }
+ }
+}
+
func TestChainID(t *testing.T) {
key, _ := crypto.GenerateKey()
diff --git a/types/transaction.go b/types/transaction.go
index 3788eb61..dfc1085c 100644
--- a/types/transaction.go
+++ b/types/transaction.go
@@ -45,8 +45,9 @@ type Transaction struct {
gasAssetID uint64
gasPrice *big.Int
// caches
- hash atomic.Value
- size atomic.Value
+ hash atomic.Value
+ extendHash atomic.Value
+ size atomic.Value
}
// NewTransaction initialize a transaction.
@@ -65,8 +66,21 @@ func NewTransaction(assetID uint64, price *big.Int, actions ...*Action) *Transac
// GasAssetID returns transaction gas asset id.
func (tx *Transaction) GasAssetID() uint64 { return tx.gasAssetID }
-// GasPrice returns transaction gas price.
-func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.gasPrice) }
+func (tx *Transaction) PayerExist() bool {
+ return tx.gasPrice.Cmp(big.NewInt(0)) == 0 && tx.actions[0].fp != nil
+}
+
+// GasPrice returns transaction Higher gas price .
+func (tx *Transaction) GasPrice() *big.Int {
+ gasPrice := new(big.Int)
+ if tx.gasPrice.Cmp(big.NewInt(0)) == 0 {
+ if price := tx.actions[0].PayerGasPrice(); price == nil {
+ return big.NewInt(0)
+ }
+ return gasPrice.Set(tx.actions[0].PayerGasPrice())
+ }
+ return gasPrice.Set(tx.gasPrice)
+}
// Cost returns all actions gasprice * gaslimit.
func (tx *Transaction) Cost() *big.Int {
@@ -111,11 +125,26 @@ func (tx *Transaction) Hash() common.Hash {
if hash := tx.hash.Load(); hash != nil {
return hash.(common.Hash)
}
- v := RlpHash(tx)
+ var acts [][]interface{}
+ for _, a := range tx.actions {
+ acts = append(acts, a.IgnoreExtend())
+ }
+ v := RlpHash([]interface{}{tx.gasAssetID, tx.gasPrice, acts})
tx.hash.Store(v)
return v
}
+// ExtensHash hashes the RLP encoding of tx.
+func (tx *Transaction) ExtensHash() common.Hash {
+ if hash := tx.extendHash.Load(); hash != nil {
+ return hash.(common.Hash)
+ }
+
+ v := RlpHash(tx)
+ tx.extendHash.Store(v)
+ return v
+}
+
// Size returns the true RLP encoded storage size of the transaction,
func (tx *Transaction) Size() common.StorageSize {
if size := tx.size.Load(); size != nil {
@@ -128,7 +157,7 @@ func (tx *Transaction) Size() common.StorageSize {
}
// Check the validity of all fields
-func (tx *Transaction) Check(conf *params.ChainConfig) error {
+func (tx *Transaction) Check(fid uint64, conf *params.ChainConfig) error {
if len(tx.actions) == 0 {
return ErrEmptyActions
}
@@ -143,7 +172,7 @@ func (tx *Transaction) Check(conf *params.ChainConfig) error {
}
for _, action := range tx.actions {
- if err := action.Check(conf); err != nil {
+ if err := action.Check(fid, conf); err != nil {
return err
}
}
@@ -182,6 +211,36 @@ func (tx *Transaction) NewRPCTransaction(blockHash common.Hash, blockNumber uint
return result
}
+type RPCTransactionWithPayer struct {
+ BlockHash common.Hash `json:"blockHash"`
+ BlockNumber uint64 `json:"blockNumber"`
+ Hash common.Hash `json:"txHash"`
+ TransactionIndex uint64 `json:"transactionIndex"`
+ RPCActionsWithPayer []*RPCActionWithPayer `json:"actions"`
+ GasAssetID uint64 `json:"gasAssetID"`
+ GasPrice *big.Int `json:"gasPrice"`
+ GasCost *big.Int `json:"gasCost"`
+}
+
+func (tx *Transaction) NewRPCTransactionWithPayer(blockHash common.Hash, blockNumber uint64, index uint64) *RPCTransactionWithPayer {
+ result := new(RPCTransactionWithPayer)
+ if blockHash != (common.Hash{}) {
+ result.BlockHash = blockHash
+ result.BlockNumber = blockNumber
+ result.TransactionIndex = index
+ }
+ result.Hash = tx.Hash()
+ ras := make([]*RPCActionWithPayer, len(tx.GetActions()))
+ for index, action := range tx.GetActions() {
+ ras[index] = action.NewRPCActionWithPayer(uint64(index))
+ }
+ result.RPCActionsWithPayer = ras
+ result.GasAssetID = tx.gasAssetID
+ result.GasPrice = tx.gasPrice
+ result.GasCost = tx.Cost()
+ return result
+}
+
// TxByNonce sort by transaction first action nonce
type TxByNonce []*Transaction