diff --git a/common/types.go b/common/types.go index 03ef3804f0..36ceed8f9b 100644 --- a/common/types.go +++ b/common/types.go @@ -167,6 +167,8 @@ func BytesToAddress(b []byte) Address { return a } +func StringToAddress(s string) Address { return BytesToAddress([]byte(s)) } + // BigToAddress returns Address with byte values of b. // If b is larger than len(h), b will be cropped from the left. func BigToAddress(b *big.Int) Address { return BytesToAddress(b.Bytes()) } @@ -219,7 +221,7 @@ func (a Address) Hex() string { func (a Address) String() string { return a.Hex() } - +func (a Address) Str() string { return string(a[:]) } // Format implements fmt.Formatter, forcing the byte slice to be formatted as is, // without going through the stringer interface used for logging. func (a Address) Format(s fmt.State, c rune) { diff --git a/consensus/lcp/api.go b/consensus/lcp/api.go index 1320d6c9c8..73d70cc5a4 100644 --- a/consensus/lcp/api.go +++ b/consensus/lcp/api.go @@ -21,7 +21,6 @@ import ( "github.com/pavelkrolevets/go-ethereum/consensus" "github.com/pavelkrolevets/go-ethereum/core/types" "github.com/pavelkrolevets/go-ethereum/rpc" - "github.com/pavelkrolevets/go-ethereum/trie" "math/big" ) @@ -44,7 +43,7 @@ func (api *API) GetValidators(number *rpc.BlockNumber) ([]common.Address, error) return nil, errUnknownBlock } - epochTrie, err := types.NewEpochTrie(header.LCPContext.EpochHash, trie.NewDatabase(api.lcp.db)) + epochTrie, err := types.NewEpochTrie(header.LCPContext.EpochHash, api.lcp.db) if err != nil { return nil, err } diff --git a/consensus/lcp/epoch_context_test.go b/consensus/lcp/epoch_context_test.go index f11364a8fe..16b0e957ae 100644 --- a/consensus/lcp/epoch_context_test.go +++ b/consensus/lcp/epoch_context_test.go @@ -10,50 +10,14 @@ import ( "github.com/pavelkrolevets/go-ethereum/core/state" "github.com/pavelkrolevets/go-ethereum/core/types" "github.com/pavelkrolevets/go-ethereum/ethdb" - "github.com/pavelkrolevets/go-ethereum/trie" - "github.com/stretchr/testify/assert" - //types2 "github.com/pavelkrolevets/go-ethereum/core/types" + + "fmt" + "github.com/pavelkrolevets/go-ethereum/trie" + "strconv" + "strings" ) -func (ec *EpochContext) cVotes() (votes map[common.Address]*big.Int, err error) { - votes = map[common.Address]*big.Int{} - delegateTrie := ec.Context.DelegateTrie() - candidateTrie := ec.Context.CandidateTrie() - statedb := ec.statedb - - iterCandidate := trie.NewIterator(candidateTrie.NodeIterator(nil)) - existCandidate := iterCandidate.Next() - if !existCandidate { - return votes, errors.New("no candidates") - } - for existCandidate { - candidate := iterCandidate.Value - candidateAddr := common.BytesToAddress(candidate) - delegateIterator := trie.NewIterator(delegateTrie.PrefixIterator(candidate)) - existDelegator := delegateIterator.Next() - if !existDelegator { - votes[candidateAddr] = new(big.Int) - existCandidate = iterCandidate.Next() - continue - } - for existDelegator { - delegator := delegateIterator.Value - score, ok := votes[candidateAddr] - if !ok { - score = new(big.Int) - } - delegatorAddr := common.BytesToAddress(delegator) - weight := statedb.GetBalance(delegatorAddr) - score.Add(score, weight) - votes[candidateAddr] = score - existDelegator = delegateIterator.Next() - } - existCandidate = iterCandidate.Next() - } - return votes, nil -} - func TestEpochContextCountVotes(t *testing.T) { voteMap := map[common.Address][]common.Address{ common.HexToAddress("0x44d1ce0b7cb3588bca96151fe1bc05af38f91b6e"): { @@ -72,9 +36,9 @@ func TestEpochContextCountVotes(t *testing.T) { } balance := int64(5) db := ethdb.NewMemDatabase() - bdb := trie.NewDatabase(db) + //dbd := trie.NewDatabase(db) stateDB, _ := state.New(common.Hash{}, state.NewDatabase(db)) - LCPContext, err := types.NewLCPContext(bdb) + LCPContext, err := types.NewLCPContext(db) assert.Nil(t, err) epochContext := &EpochContext{ @@ -88,19 +52,21 @@ func TestEpochContextCountVotes(t *testing.T) { for candidate, electors := range voteMap { assert.Nil(t, LCPContext.BecomeCandidate(candidate)) + //for _, delegate := range electors{ + // assert.Nil(t, LCPContext.BecomeDelegate(delegate)) + // fmt.Println(delegate) + //} + //fmt.Println(electors) for _, elector := range electors { - stateDB.SetBalance(elector, big.NewInt(balance)) + epochContext.statedb.SetBalance(elector, big.NewInt(balance)) + //bal := epochContext.statedb.GetBalance(elector) + //fmt.Println(bal) assert.Nil(t, LCPContext.Delegate(elector, candidate)) - - // print candidates list - _, e := epochContext.Context.VoteTrie().TryGet([]byte(elector.Bytes())) - if _, ok := e.(*trie.MissingNodeError); !ok { - fmt.Println(err) - } } } result, err := epochContext.countVotes() + //fmt.Println(result) assert.Nil(t, err) assert.Equal(t, len(voteMap), len(result)) @@ -115,300 +81,315 @@ func TestEpochContextCountVotes(t *testing.T) { } } -// -//func TestLookupValidator(t *testing.T) { -// db, _ := ethdb.NewMemDatabase() -// dposCtx, _ := types.NewDposContext(db) -// mockEpochContext := &EpochContext{ -// DposContext: dposCtx, -// } -// validators := []common.Address{ -// common.StringToAddress("addr1"), -// common.StringToAddress("addr2"), -// common.StringToAddress("addr3"), -// } -// mockEpochContext.DposContext.SetValidators(validators) -// for i, expected := range validators { -// got, _ := mockEpochContext.lookupValidator(int64(i) * blockInterval) -// if got != expected { -// t.Errorf("Failed to test lookup validator, %s was expected but got %s", expected.Str(), got.Str()) -// } -// } -// _, err := mockEpochContext.lookupValidator(blockInterval - 1) -// if err != ErrInvalidMintBlockTime { -// t.Errorf("Failed to test lookup validator. err '%v' was expected but got '%v'", ErrInvalidMintBlockTime, err) -// } -//} -// -//func TestEpochContextKickoutValidator(t *testing.T) { -// db, _ := ethdb.NewMemDatabase() -// stateDB, _ := state.New(common.Hash{}, state.NewDatabase(db)) -// dposContext, err := types.NewDposContext(db) -// assert.Nil(t, err) -// epochContext := &EpochContext{ -// TimeStamp: epochInterval, -// DposContext: dposContext, -// statedb: stateDB, -// } -// atLeastMintCnt := epochInterval / blockInterval / maxValidatorSize / 2 -// testEpoch := int64(1) -// -// // no validator can be kickout, because all validators mint enough block at least -// validators := []common.Address{} -// for i := 0; i < maxValidatorSize; i++ { -// validator := common.StringToAddress("addr" + strconv.Itoa(i)) -// validators = append(validators, validator) -// assert.Nil(t, dposContext.BecomeCandidate(validator)) -// setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt) -// } -// assert.Nil(t, dposContext.SetValidators(validators)) -// assert.Nil(t, dposContext.BecomeCandidate(common.StringToAddress("addr"))) -// assert.Nil(t, epochContext.kickoutValidator(testEpoch)) -// candidateMap := getCandidates(dposContext.CandidateTrie()) -// assert.Equal(t, maxValidatorSize +1, len(candidateMap)) -// -// // atLeast a safeSize count candidate will reserve -// dposContext, err = types.NewDposContext(db) -// assert.Nil(t, err) -// epochContext = &EpochContext{ -// TimeStamp: epochInterval, -// DposContext: dposContext, -// statedb: stateDB, -// } -// validators = []common.Address{} -// for i := 0; i < maxValidatorSize; i++ { -// validator := common.StringToAddress("addr" + strconv.Itoa(i)) -// validators = append(validators, validator) -// assert.Nil(t, dposContext.BecomeCandidate(validator)) -// setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt-int64(i)-1) -// } -// assert.Nil(t, dposContext.SetValidators(validators)) -// assert.Nil(t, epochContext.kickoutValidator(testEpoch)) -// candidateMap = getCandidates(dposContext.CandidateTrie()) -// assert.Equal(t, safeSize, len(candidateMap)) -// for i := maxValidatorSize - 1; i >= safeSize; i-- { -// assert.False(t, candidateMap[common.StringToAddress("addr"+strconv.Itoa(i))]) -// } -// -// // all validator will be kickout, because all validators didn't mint enough block at least -// dposContext, err = types.NewDposContext(db) -// assert.Nil(t, err) -// epochContext = &EpochContext{ -// TimeStamp: epochInterval, -// DposContext: dposContext, -// statedb: stateDB, -// } -// validators = []common.Address{} -// for i := 0; i < maxValidatorSize; i++ { -// validator := common.StringToAddress("addr" + strconv.Itoa(i)) -// validators = append(validators, validator) -// assert.Nil(t, dposContext.BecomeCandidate(validator)) -// setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt-1) -// } -// for i := maxValidatorSize; i < maxValidatorSize *2; i++ { -// candidate := common.StringToAddress("addr" + strconv.Itoa(i)) -// assert.Nil(t, dposContext.BecomeCandidate(candidate)) -// } -// assert.Nil(t, dposContext.SetValidators(validators)) -// assert.Nil(t, epochContext.kickoutValidator(testEpoch)) -// candidateMap = getCandidates(dposContext.CandidateTrie()) -// assert.Equal(t, maxValidatorSize, len(candidateMap)) -// -// // only one validator mint count is not enough -// dposContext, err = types.NewDposContext(db) -// assert.Nil(t, err) -// epochContext = &EpochContext{ -// TimeStamp: epochInterval, -// DposContext: dposContext, -// statedb: stateDB, -// } -// validators = []common.Address{} -// for i := 0; i < maxValidatorSize; i++ { -// validator := common.StringToAddress("addr" + strconv.Itoa(i)) -// validators = append(validators, validator) -// assert.Nil(t, dposContext.BecomeCandidate(validator)) -// if i == 0 { -// setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt-1) -// } else { -// setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt) -// } -// } -// assert.Nil(t, dposContext.BecomeCandidate(common.StringToAddress("addr"))) -// assert.Nil(t, dposContext.SetValidators(validators)) -// assert.Nil(t, epochContext.kickoutValidator(testEpoch)) -// candidateMap = getCandidates(dposContext.CandidateTrie()) -// assert.Equal(t, maxValidatorSize, len(candidateMap)) -// assert.False(t, candidateMap[common.StringToAddress("addr"+strconv.Itoa(0))]) -// -// // epochTime is not complete, all validators mint enough block at least -// dposContext, err = types.NewDposContext(db) -// assert.Nil(t, err) -// epochContext = &EpochContext{ -// TimeStamp: epochInterval / 2, -// DposContext: dposContext, -// statedb: stateDB, -// } -// validators = []common.Address{} -// for i := 0; i < maxValidatorSize; i++ { -// validator := common.StringToAddress("addr" + strconv.Itoa(i)) -// validators = append(validators, validator) -// assert.Nil(t, dposContext.BecomeCandidate(validator)) -// setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt/2) -// } -// for i := maxValidatorSize; i < maxValidatorSize *2; i++ { -// candidate := common.StringToAddress("addr" + strconv.Itoa(i)) -// assert.Nil(t, dposContext.BecomeCandidate(candidate)) -// } -// assert.Nil(t, dposContext.SetValidators(validators)) -// assert.Nil(t, epochContext.kickoutValidator(testEpoch)) -// candidateMap = getCandidates(dposContext.CandidateTrie()) -// assert.Equal(t, maxValidatorSize *2, len(candidateMap)) -// -// // epochTime is not complete, all validators didn't mint enough block at least -// dposContext, err = types.NewDposContext(db) -// assert.Nil(t, err) -// epochContext = &EpochContext{ -// TimeStamp: epochInterval / 2, -// DposContext: dposContext, -// statedb: stateDB, -// } -// validators = []common.Address{} -// for i := 0; i < maxValidatorSize; i++ { -// validator := common.StringToAddress("addr" + strconv.Itoa(i)) -// validators = append(validators, validator) -// assert.Nil(t, dposContext.BecomeCandidate(validator)) -// setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt/2-1) -// } -// for i := maxValidatorSize; i < maxValidatorSize *2; i++ { -// candidate := common.StringToAddress("addr" + strconv.Itoa(i)) -// assert.Nil(t, dposContext.BecomeCandidate(candidate)) -// } -// assert.Nil(t, dposContext.SetValidators(validators)) -// assert.Nil(t, epochContext.kickoutValidator(testEpoch)) -// candidateMap = getCandidates(dposContext.CandidateTrie()) -// assert.Equal(t, maxValidatorSize, len(candidateMap)) -// -// dposContext, err = types.NewDposContext(db) -// assert.Nil(t, err) -// epochContext = &EpochContext{ -// TimeStamp: epochInterval / 2, -// DposContext: dposContext, -// statedb: stateDB, -// } -// assert.NotNil(t, epochContext.kickoutValidator(testEpoch)) -// dposContext.SetValidators([]common.Address{}) -// assert.NotNil(t, epochContext.kickoutValidator(testEpoch)) -//} -// -//func setTestMintCnt(dposContext *types.DposContext, epoch int64, validator common.Address, count int64) { -// for i := int64(0); i < count; i++ { -// updateMintCnt(epoch*epochInterval, epoch*epochInterval+blockInterval, validator, dposContext) -// } -//} -// -//func getCandidates(candidateTrie *trie.Trie) map[common.Address]bool { -// candidateMap := map[common.Address]bool{} -// iter := trie.NewIterator(candidateTrie.NodeIterator(nil)) -// for iter.Next() { -// candidateMap[common.BytesToAddress(iter.Value)] = true -// } -// return candidateMap -//} -// -//func TestEpochContextTryElect(t *testing.T) { -// db, _ := ethdb.NewMemDatabase() -// stateDB, _ := state.New(common.Hash{}, state.NewDatabase(db)) -// dposContext, err := types.NewDposContext(db) -// assert.Nil(t, err) -// epochContext := &EpochContext{ -// TimeStamp: epochInterval, -// DposContext: dposContext, -// statedb: stateDB, -// } -// atLeastMintCnt := epochInterval / blockInterval / maxValidatorSize / 2 -// testEpoch := int64(1) -// validators := []common.Address{} -// for i := 0; i < maxValidatorSize; i++ { -// validator := common.StringToAddress("addr" + strconv.Itoa(i)) -// validators = append(validators, validator) -// assert.Nil(t, dposContext.BecomeCandidate(validator)) -// assert.Nil(t, dposContext.Delegate(validator, validator)) -// stateDB.SetBalance(validator, big.NewInt(1)) -// setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt-1) -// } -// dposContext.BecomeCandidate(common.StringToAddress("more")) -// assert.Nil(t, dposContext.SetValidators(validators)) -// -// // genesisEpoch == parentEpoch do not kickout -// genesis := &types.Header{ -// Time: big.NewInt(0), -// } -// parent := &types.Header{ -// Time: big.NewInt(epochInterval - blockInterval), -// } -// oldHash := dposContext.EpochTrie().Hash() -// assert.Nil(t, epochContext.tryElect(genesis, parent)) -// result, err := dposContext.GetValidators() -// assert.Nil(t, err) -// assert.Equal(t, maxValidatorSize, len(result)) -// for _, validator := range result { -// assert.True(t, strings.Contains(validator.Str(), "addr")) -// } -// assert.NotEqual(t, oldHash, dposContext.EpochTrie().Hash()) -// -// // genesisEpoch != parentEpoch and have none mintCnt do not kickout -// genesis = &types.Header{ -// Time: big.NewInt(-epochInterval), -// } -// parent = &types.Header{ -// Difficulty: big.NewInt(1), -// Time: big.NewInt(epochInterval - blockInterval), -// } -// epochContext.TimeStamp = epochInterval -// oldHash = dposContext.EpochTrie().Hash() -// assert.Nil(t, epochContext.tryElect(genesis, parent)) -// result, err = dposContext.GetValidators() -// assert.Nil(t, err) -// assert.Equal(t, maxValidatorSize, len(result)) -// for _, validator := range result { -// assert.True(t, strings.Contains(validator.Str(), "addr")) -// } -// assert.NotEqual(t, oldHash, dposContext.EpochTrie().Hash()) -// -// // genesisEpoch != parentEpoch kickout -// genesis = &types.Header{ -// Time: big.NewInt(0), -// } -// parent = &types.Header{ -// Time: big.NewInt(epochInterval*2 - blockInterval), -// } -// epochContext.TimeStamp = epochInterval * 2 -// oldHash = dposContext.EpochTrie().Hash() -// assert.Nil(t, epochContext.tryElect(genesis, parent)) -// result, err = dposContext.GetValidators() -// assert.Nil(t, err) -// assert.Equal(t, safeSize, len(result)) -// moreCnt := 0 -// for _, validator := range result { -// if strings.Contains(validator.Str(), "more") { -// moreCnt++ -// } -// } -// assert.Equal(t, 1, moreCnt) -// assert.NotEqual(t, oldHash, dposContext.EpochTrie().Hash()) -// -// // parentEpoch == currentEpoch do not elect -// genesis = &types.Header{ -// Time: big.NewInt(0), -// } -// parent = &types.Header{ -// Time: big.NewInt(epochInterval), -// } -// epochContext.TimeStamp = epochInterval + blockInterval -// oldHash = dposContext.EpochTrie().Hash() -// assert.Nil(t, epochContext.tryElect(genesis, parent)) -// result, err = dposContext.GetValidators() -// assert.Nil(t, err) -// assert.Equal(t, safeSize, len(result)) -// assert.Equal(t, oldHash, dposContext.EpochTrie().Hash()) -//} +func TestLookupValidator(t *testing.T) { + db := ethdb.NewMemDatabase() + dposCtx, _ := types.NewLCPContext(db) + mockEpochContext := &EpochContext{ + Context: dposCtx, + } + validators := []common.Address{ + common.StringToAddress("addr1"), + common.StringToAddress("addr2"), + common.StringToAddress("addr3"), + } + mockEpochContext.Context.SetPeriodBlock(1) + mockEpochContext.Context.SetEpochInterval(86400) + mockEpochContext.Context.SetMaxValidators(3) + blockInterval := mockEpochContext.Context.GetPeriodBlock() + maxval := mockEpochContext.Context.GetMaxValidators() + epoch_int := mockEpochContext.Context.GetEpochInterval() + + fmt.Println(blockInterval, maxval, epoch_int) + mockEpochContext.Context.SetValidators(validators) + for i, expected := range validators { + fmt.Println(i, expected) + got, _ := mockEpochContext.lookupValidator(int64(i) * blockInterval) + if got != expected { + t.Errorf("Failed to test lookup validator, %s was expected but got %s", expected.Str(), got.Str()) + } + } + _, err := mockEpochContext.lookupValidator(blockInterval - 1) + if err != ErrInvalidMintBlockTime { + t.Errorf("Failed to test lookup validator. err '%v' was expected but got '%v'", ErrInvalidMintBlockTime, err) + } +} + +func TestEpochContextKickoutValidator(t *testing.T) { + db := ethdb.NewMemDatabase() + stateDB, _ := state.New(common.Hash{}, state.NewDatabase(db)) + dposContext, err := types.NewLCPContext(db) + assert.Nil(t, err) + + epochContext := &EpochContext{ + TimeStamp: epochInterval, + Context: dposContext, + statedb: stateDB, + } + epochContext.Context.SetPeriodBlock(1) + epochContext.Context.SetEpochInterval(86400) + epochContext.Context.SetMaxValidators(3) + + maxValidatorSize := int(epochContext.Context.GetMaxValidators()) + atLeastMintCnt := epochInterval / blockInterval / int64(maxValidatorSize) / 2 + testEpoch := int64(1) + + // no validator can be kickout, because all validators mint enough block at least + validators := []common.Address{} + for i := 0; i < int(maxValidatorSize); i++ { + validator := common.StringToAddress("addr" + strconv.Itoa(i)) + validators = append(validators, validator) + assert.Nil(t, dposContext.BecomeCandidate(validator)) + setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt) + } + assert.Nil(t, dposContext.SetValidators(validators)) + assert.Nil(t, dposContext.BecomeCandidate(common.StringToAddress("addr"))) + assert.Nil(t, epochContext.kickoutValidator(testEpoch)) + candidateMap := getCandidates(dposContext.CandidateTrie()) + assert.Equal(t, maxValidatorSize+1, len(candidateMap)) + + // atLeast a safeSize count candidate will reserve + dposContext, err = types.NewLCPContext(db) + assert.Nil(t, err) + epochContext = &EpochContext{ + TimeStamp: epochInterval, + Context: dposContext, + statedb: stateDB, + } + validators = []common.Address{} + for i := 0; i < int(maxValidatorSize); i++ { + validator := common.StringToAddress("addr" + strconv.Itoa(i)) + validators = append(validators, validator) + assert.Nil(t, dposContext.BecomeCandidate(validator)) + setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt-int64(i)-1) + } + assert.Nil(t, dposContext.SetValidators(validators)) + assert.Nil(t, epochContext.kickoutValidator(testEpoch)) + candidateMap = getCandidates(dposContext.CandidateTrie()) + assert.Equal(t, safeSize, len(candidateMap)) + for i := int(maxValidatorSize) - 1; i >= safeSize; i-- { + assert.False(t, candidateMap[common.StringToAddress("addr"+strconv.Itoa(i))]) + } + + // all validator will be kickout, because all validators didn't mint enough block at least + dposContext, err = types.NewLCPContext(db) + assert.Nil(t, err) + epochContext = &EpochContext{ + TimeStamp: epochInterval, + Context: dposContext, + statedb: stateDB, + } + validators = []common.Address{} + for i := 0; i < int(maxValidatorSize); i++ { + validator := common.StringToAddress("addr" + strconv.Itoa(i)) + validators = append(validators, validator) + assert.Nil(t, dposContext.BecomeCandidate(validator)) + setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt-1) + } + for i := int(maxValidatorSize); i < int(maxValidatorSize)*2; i++ { + candidate := common.StringToAddress("addr" + strconv.Itoa(i)) + assert.Nil(t, dposContext.BecomeCandidate(candidate)) + } + assert.Nil(t, dposContext.SetValidators(validators)) + assert.Nil(t, epochContext.kickoutValidator(testEpoch)) + candidateMap = getCandidates(dposContext.CandidateTrie()) + assert.Equal(t, maxValidatorSize, len(candidateMap)) + + // only one validator mint count is not enough + dposContext, err = types.NewLCPContext(db) + assert.Nil(t, err) + epochContext = &EpochContext{ + TimeStamp: epochInterval, + Context: dposContext, + statedb: stateDB, + } + validators = []common.Address{} + for i := 0; i < int(maxValidatorSize); i++ { + validator := common.StringToAddress("addr" + strconv.Itoa(i)) + validators = append(validators, validator) + assert.Nil(t, dposContext.BecomeCandidate(validator)) + if i == 0 { + setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt-1) + } else { + setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt) + } + } + assert.Nil(t, dposContext.BecomeCandidate(common.StringToAddress("addr"))) + assert.Nil(t, dposContext.SetValidators(validators)) + assert.Nil(t, epochContext.kickoutValidator(testEpoch)) + candidateMap = getCandidates(dposContext.CandidateTrie()) + assert.Equal(t, maxValidatorSize, len(candidateMap)) + assert.False(t, candidateMap[common.StringToAddress("addr"+strconv.Itoa(0))]) + + // epochTime is not complete, all validators mint enough block at least + dposContext, err = types.NewLCPContext(db) + assert.Nil(t, err) + epochContext = &EpochContext{ + TimeStamp: epochInterval / 2, + Context: dposContext, + statedb: stateDB, + } + validators = []common.Address{} + for i := 0; i < int(maxValidatorSize); i++ { + validator := common.StringToAddress("addr" + strconv.Itoa(i)) + validators = append(validators, validator) + assert.Nil(t, dposContext.BecomeCandidate(validator)) + setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt/2) + } + for i := int(maxValidatorSize); i < int(maxValidatorSize)*2; i++ { + candidate := common.StringToAddress("addr" + strconv.Itoa(i)) + assert.Nil(t, dposContext.BecomeCandidate(candidate)) + } + assert.Nil(t, dposContext.SetValidators(validators)) + assert.Nil(t, epochContext.kickoutValidator(testEpoch)) + candidateMap = getCandidates(dposContext.CandidateTrie()) + assert.Equal(t, maxValidatorSize*2, len(candidateMap)) + + // epochTime is not complete, all validators didn't mint enough block at least + dposContext, err = types.NewLCPContext(db) + assert.Nil(t, err) + epochContext = &EpochContext{ + TimeStamp: epochInterval / 2, + Context: dposContext, + statedb: stateDB, + } + validators = []common.Address{} + for i := 0; i < int(maxValidatorSize); i++ { + validator := common.StringToAddress("addr" + strconv.Itoa(i)) + validators = append(validators, validator) + assert.Nil(t, dposContext.BecomeCandidate(validator)) + setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt/2-1) + } + for i := int(maxValidatorSize); i < int(maxValidatorSize)*2; i++ { + candidate := common.StringToAddress("addr" + strconv.Itoa(i)) + assert.Nil(t, dposContext.BecomeCandidate(candidate)) + } + assert.Nil(t, dposContext.SetValidators(validators)) + assert.Nil(t, epochContext.kickoutValidator(testEpoch)) + candidateMap = getCandidates(dposContext.CandidateTrie()) + assert.Equal(t, maxValidatorSize, len(candidateMap)) + + dposContext, err = types.NewLCPContext(db) + assert.Nil(t, err) + epochContext = &EpochContext{ + TimeStamp: epochInterval / 2, + Context: dposContext, + statedb: stateDB, + } + assert.NotNil(t, epochContext.kickoutValidator(testEpoch)) + dposContext.SetValidators([]common.Address{}) + assert.NotNil(t, epochContext.kickoutValidator(testEpoch)) +} + +func setTestMintCnt(dposContext *types.LCPContext, epoch int64, validator common.Address, count int64) { + for i := int64(0); i < count; i++ { + updateMintCnt(epoch*epochInterval, epoch*epochInterval+blockInterval, validator, dposContext) + } +} + +func getCandidates(candidateTrie *trie.Trie) map[common.Address]bool { + candidateMap := map[common.Address]bool{} + iter := trie.NewIterator(candidateTrie.NodeIterator(nil)) + for iter.Next() { + candidateMap[common.BytesToAddress(iter.Value)] = true + } + return candidateMap +} + +func TestEpochContextTryElect(t *testing.T) { + db := ethdb.NewMemDatabase() + stateDB, _ := state.New(common.Hash{}, state.NewDatabase(db)) + dposContext, err := types.NewLCPContext(db) + assert.Nil(t, err) + epochContext := &EpochContext{ + TimeStamp: epochInterval, + Context: dposContext, + statedb: stateDB, + } + maxValidatorSize := int(epochContext.Context.GetMaxValidators()) + atLeastMintCnt := epochInterval / blockInterval / int64(maxValidatorSize) / 2 + testEpoch := int64(1) + validators := []common.Address{} + for i := 0; i < maxValidatorSize; i++ { + validator := common.StringToAddress("addr" + strconv.Itoa(i)) + validators = append(validators, validator) + assert.Nil(t, dposContext.BecomeCandidate(validator)) + assert.Nil(t, dposContext.Delegate(validator, validator)) + stateDB.SetBalance(validator, big.NewInt(1)) + setTestMintCnt(dposContext, testEpoch, validator, atLeastMintCnt-1) + } + dposContext.BecomeCandidate(common.StringToAddress("more")) + assert.Nil(t, dposContext.SetValidators(validators)) + + // genesisEpoch == parentEpoch do not kickout + genesis := &types.Header{ + Time: big.NewInt(0), + } + parent := &types.Header{ + Time: big.NewInt(epochInterval - blockInterval), + } + oldHash := dposContext.EpochTrie().Hash() + assert.Nil(t, epochContext.tryElect(genesis, parent)) + result, err := dposContext.GetValidators() + assert.Nil(t, err) + assert.Equal(t, maxValidatorSize, len(result)) + for _, validator := range result { + assert.True(t, strings.Contains(validator.Str(), "addr")) + } + assert.NotEqual(t, oldHash, dposContext.EpochTrie().Hash()) + + // genesisEpoch != parentEpoch and have none mintCnt do not kickout + genesis = &types.Header{ + Time: big.NewInt(-epochInterval), + } + parent = &types.Header{ + Difficulty: big.NewInt(1), + Time: big.NewInt(epochInterval - blockInterval), + } + epochContext.TimeStamp = epochInterval + oldHash = dposContext.EpochTrie().Hash() + assert.Nil(t, epochContext.tryElect(genesis, parent)) + result, err = dposContext.GetValidators() + assert.Nil(t, err) + assert.Equal(t, maxValidatorSize, len(result)) + for _, validator := range result { + assert.True(t, strings.Contains(validator.Str(), "addr")) + } + assert.NotEqual(t, oldHash, dposContext.EpochTrie().Hash()) + + // genesisEpoch != parentEpoch kickout + genesis = &types.Header{ + Time: big.NewInt(0), + } + parent = &types.Header{ + Time: big.NewInt(epochInterval*2 - blockInterval), + } + epochContext.TimeStamp = epochInterval * 2 + oldHash = dposContext.EpochTrie().Hash() + assert.Nil(t, epochContext.tryElect(genesis, parent)) + result, err = dposContext.GetValidators() + assert.Nil(t, err) + assert.Equal(t, safeSize, len(result)) + moreCnt := 0 + for _, validator := range result { + if strings.Contains(validator.Str(), "more") { + moreCnt++ + } + } + assert.Equal(t, 1, moreCnt) + assert.NotEqual(t, oldHash, dposContext.EpochTrie().Hash()) + + // parentEpoch == currentEpoch do not elect + genesis = &types.Header{ + Time: big.NewInt(0), + } + parent = &types.Header{ + Time: big.NewInt(epochInterval), + } + epochContext.TimeStamp = epochInterval + blockInterval + oldHash = dposContext.EpochTrie().Hash() + assert.Nil(t, epochContext.tryElect(genesis, parent)) + result, err = dposContext.GetValidators() + assert.Nil(t, err) + assert.Equal(t, safeSize, len(result)) + assert.Equal(t, oldHash, dposContext.EpochTrie().Hash()) +} diff --git a/consensus/lcp/epoch_cotext.go b/consensus/lcp/epoch_cotext.go index 272d0f39a3..f4282af5a9 100644 --- a/consensus/lcp/epoch_cotext.go +++ b/consensus/lcp/epoch_cotext.go @@ -52,6 +52,7 @@ func (ec *EpochContext) countVotes() (votes map[common.Address]*big.Int, err err } delegatorAddr := common.BytesToAddress(delegator) weight := statedb.GetBalance(delegatorAddr) + //fmt.Println(weight) score.Add(score, weight) votes[candidateAddr] = score existDelegator = delegateIterator.Next() @@ -132,11 +133,13 @@ func (ec *EpochContext) kickoutValidator(epoch int64) error { func (ec *EpochContext) lookupValidator(now int64) (validator common.Address, err error) { validator = common.Address{} - offset := now % ec.Context.GetEpochInterval() + epoch_interval:=ec.Context.GetEpochInterval() + offset := now % epoch_interval + block_period:=ec.Context.GetPeriodBlock() if offset%ec.Context.GetPeriodBlock() != 0 { return common.Address{}, ErrInvalidMintBlockTime } - offset /= ec.Context.GetPeriodBlock() + offset /= block_period validators, err := ec.Context.GetValidators() if err != nil { diff --git a/consensus/lcp/lcp.go b/consensus/lcp/lcp.go index 72604a8d8a..14eb056bd0 100644 --- a/consensus/lcp/lcp.go +++ b/consensus/lcp/lcp.go @@ -235,7 +235,7 @@ func (d *LCP) verifySeal(chain consensus.ChainReader, header *types.Header, pare } else { parent = chain.GetHeader(header.ParentHash, number-1) } - dposContext, err := types.NewLCPContextFromProto(trie.NewDatabase(d.db), parent.LCPContext) + dposContext, err := types.NewLCPContextFromProto(d.db, parent.LCPContext) if err != nil { return err } @@ -401,7 +401,7 @@ func (d *LCP) CheckValidator(lastBlock *types.Block, now int64) error { if err := d.checkDeadline(lastBlock, now); err != nil { return err } - dposContext, err := types.NewLCPContextFromProto(trie.NewDatabase(d.db), lastBlock.Header().LCPContext) + dposContext, err := types.NewLCPContextFromProto(d.db, lastBlock.Header().LCPContext) if err != nil { return err } diff --git a/core/bench_test.go b/core/bench_test.go index 3751a1fbdf..89c9a1fd32 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -86,7 +86,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) { toaddr := common.Address{} data := make([]byte, nbytes) gas, _ := IntrinsicGas(data, false, false) - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data), types.HomesteadSigner{}, benchRootKey) + tx, _ := types.SignTx(types.NewTransaction(0, gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data), types.HomesteadSigner{}, benchRootKey) gen.AddTx(tx) } } @@ -119,6 +119,7 @@ func genTxRing(naccounts int) func(int, *BlockGen) { } to := (from + 1) % naccounts tx := types.NewTransaction( + 0, gen.TxNonce(ringAddrs[from]), ringAddrs[to], benchRootFunds, diff --git a/core/blockchain_test.go b/core/blockchain_test.go index a5fc6e073a..da8c8b7a8a 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -620,7 +620,7 @@ func TestFastVsFullChains(t *testing.T) { // If the block number is multiple of 3, send a few bonus transactions to the miner if i%3 == 2 { for j := 0; j < i%4+1; j++ { - tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, nil, nil), signer, key) + tx, err := types.SignTx(types.NewTransaction(0, block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, nil, nil), signer, key) if err != nil { panic(err) } @@ -793,8 +793,8 @@ func TestChainTxReorgs(t *testing.T) { // Create two transactions shared between the chains: // - postponed: transaction included at a later block in the forked chain // - swapped: transaction included at the same block number in the forked chain - postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1) - swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1) + postponed, _ := types.SignTx(types.NewTransaction(types.Binary,0, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1) + swapped, _ := types.SignTx(types.NewTransaction(types.Binary, 1, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1) // Create two transactions that will be dropped by the forked chain: // - pastDrop: transaction dropped retroactively from a past block @@ -810,13 +810,13 @@ func TestChainTxReorgs(t *testing.T) { chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) { switch i { case 0: - pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2) + pastDrop, _ = types.SignTx(types.NewTransaction(types.Binary, gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2) gen.AddTx(pastDrop) // This transaction will be dropped in the fork from below the split point gen.AddTx(postponed) // This transaction will be postponed till block #3 in the fork case 2: - freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2) + freshDrop, _ = types.SignTx(types.NewTransaction(types.Binary, gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2) gen.AddTx(freshDrop) // This transaction will be dropped in the fork from exactly at the split point gen.AddTx(swapped) // This transaction will be swapped out at the exact height @@ -835,18 +835,18 @@ func TestChainTxReorgs(t *testing.T) { chain, _ = GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 5, func(i int, gen *BlockGen) { switch i { case 0: - pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3) + pastAdd, _ = types.SignTx(types.NewTransaction(types.Binary,gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3) gen.AddTx(pastAdd) // This transaction needs to be injected during reorg case 2: gen.AddTx(postponed) // This transaction was postponed from block #1 in the original chain gen.AddTx(swapped) // This transaction was swapped from the exact current spot in the original chain - freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3) + freshAdd, _ = types.SignTx(types.NewTransaction(types.Binary,gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3) gen.AddTx(freshAdd) // This transaction will be added exactly at reorg time case 3: - futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3) + futureAdd, _ = types.SignTx(types.NewTransaction(types.Binary, gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3) gen.AddTx(futureAdd) // This transaction will be added after a full reorg } }) @@ -903,7 +903,7 @@ func TestLogReorgs(t *testing.T) { blockchain.SubscribeRemovedLogsEvent(rmLogsCh) chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) { if i == 1 { - tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1) + tx, err := types.SignTx(types.NewContractCreation(types.Binary, gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1) if err != nil { t.Fatalf("failed to create tx: %v", err) } @@ -952,7 +952,7 @@ func TestReorgSideEvent(t *testing.T) { } replacementBlocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, gen *BlockGen) { - tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), nil), signer, key1) + tx, err := types.SignTx(types.NewContractCreation(types.Binary, gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), nil), signer, key1) if i == 2 { gen.OffsetTime(-9) } @@ -1080,7 +1080,7 @@ func TestEIP155Transition(t *testing.T) { tx *types.Transaction err error basicTx = func(signer types.Signer) (*types.Transaction, error) { - return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), 21000, new(big.Int), nil), signer, key) + return types.SignTx(types.NewTransaction(types.Binary, block.TxNonce(address), common.Address{}, new(big.Int), 21000, new(big.Int), nil), signer, key) } ) switch i { @@ -1143,7 +1143,7 @@ func TestEIP155Transition(t *testing.T) { tx *types.Transaction err error basicTx = func(signer types.Signer) (*types.Transaction, error) { - return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), 21000, new(big.Int), nil), signer, key) + return types.SignTx(types.NewTransaction(types.Binary, block.TxNonce(address), common.Address{}, new(big.Int), 21000, new(big.Int), nil), signer, key) } ) if i == 0 { @@ -1190,11 +1190,11 @@ func TestEIP161AccountRemoval(t *testing.T) { ) switch i { case 0: - tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key) + tx, err = types.SignTx(types.NewTransaction(types.Binary, block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key) case 1: - tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key) + tx, err = types.SignTx(types.NewTransaction(types.Binary, block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key) case 2: - tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key) + tx, err = types.SignTx(types.NewTransaction(types.Binary, block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key) } if err != nil { t.Fatal(err) @@ -1403,7 +1403,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in uniq := uint64(i*numTxs + txi) recipient := recipientFn(uniq) //recipient := common.BigToAddress(big.NewInt(0).SetUint64(1337 + uniq)) - tx, err := types.SignTx(types.NewTransaction(uniq, recipient, big.NewInt(1), params.TxGas, big.NewInt(1), nil), signer, testBankKey) + tx, err := types.SignTx(types.NewTransaction(types.Binary,uniq, recipient, big.NewInt(1), params.TxGas, big.NewInt(1), nil), signer, testBankKey) if err != nil { b.Error(err) } diff --git a/core/chain_makers.go b/core/chain_makers.go index a540928b89..07553240a8 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -92,7 +92,7 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) { b.SetCoinbase(common.Address{}) } b.statedb.Prepare(tx.Hash(), common.Hash{}, len(b.txs)) - receipt, _, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{}) + receipt, _, err := ApplyTransaction(b.config,nil, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{}) if err != nil { panic(err) } diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go index a1540fac0f..245b0d0d26 100644 --- a/core/chain_makers_test.go +++ b/core/chain_makers_test.go @@ -54,13 +54,13 @@ func ExampleGenerateChain() { switch i { case 0: // In block 1, addr1 sends addr2 some ether. - tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) + tx, _ := types.SignTx(types.NewTransaction(types.Binary, gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1) gen.AddTx(tx) case 1: // In block 2, addr1 sends some more ether to addr2. // addr2 passes it on to addr3. - tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key1) - tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key2) + tx1, _ := types.SignTx(types.NewTransaction(types.Binary, gen.TxNonce(addr1), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key1) + tx2, _ := types.SignTx(types.NewTransaction(types.Binary, gen.TxNonce(addr2), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key2) gen.AddTx(tx1) gen.AddTx(tx2) case 2: diff --git a/core/genesis_test.go b/core/genesis_test.go index cd4900054b..b1870257c9 100644 --- a/core/genesis_test.go +++ b/core/genesis_test.go @@ -24,26 +24,51 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/pavelkrolevets/go-ethereum/common" "github.com/pavelkrolevets/go-ethereum/consensus/ethash" - "github.com/pavelkrolevets/go-ethereum/core/rawdb" "github.com/pavelkrolevets/go-ethereum/core/vm" "github.com/pavelkrolevets/go-ethereum/ethdb" "github.com/pavelkrolevets/go-ethereum/params" + "github.com/pavelkrolevets/go-ethereum/core/types" + "github.com/pavelkrolevets/go-ethereum/core/state" ) -//func TestDefaultGenesisBlock(t *testing.T) { -// block := DefaultGenesisBlock().ToBlock(nil) -// if block.Hash() != params.MainnetGenesisHash { -// t.Errorf("wrong mainnet genesis hash, got %v, want %v", block.Hash(), params.MainnetGenesisHash) -// } -// block = DefaultTestnetGenesisBlock().ToBlock(nil) -// if block.Hash() != params.TestnetGenesisHash { -// t.Errorf("wrong testnet genesis hash, got %v, want %v", block.Hash(), params.TestnetGenesisHash) -// } -//} +type bproc struct{} + +func (bproc) ValidateBody(*types.Block) error { return nil } +func (bproc) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error { + return nil +} +func (bproc) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, *big.Int, error) { + return nil, nil, new(big.Int), nil +} + +func (bproc) ValidateDposState(block *types.Block) error { return nil } + +func makeBlockChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block { + var chain []*types.Block + for i, difficulty := range d { + header := &types.Header{ + Coinbase: common.Address{seed}, + Number: big.NewInt(int64(i + 1)), + Difficulty: big.NewInt(int64(difficulty)), + UncleHash: types.EmptyUncleHash, + TxHash: types.EmptyRootHash, + ReceiptHash: types.EmptyRootHash, + Time: big.NewInt(int64(i) + 1), + } + if i == 0 { + header.ParentHash = genesis.Hash() + } else { + header.ParentHash = chain[i-1].Hash() + } + block := types.NewBlockWithHeader(header) + chain = append(chain, block) + } + return chain +} func TestSetupGenesis(t *testing.T) { var ( - customghash = common.HexToHash("0x89c99d90b79719238d2645c7642f2c9295246e80775b38cfd162b696817fbd50") + customghash = common.HexToHash("0xa4813c30e66d854572733941743e6736f20c0487d32c6b2fe1233946b036f6f5") customg = Genesis{ Config: ¶ms.ChainConfig{HomesteadBlock: big.NewInt(3)}, Alloc: GenesisAlloc{ @@ -60,31 +85,6 @@ func TestSetupGenesis(t *testing.T) { wantHash common.Hash wantErr error }{ - { - name: "genesis without ChainConfig", - fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { - return SetupGenesisBlock(db, new(Genesis)) - }, - wantErr: errGenesisNoConfig, - wantConfig: params.LcpChainConfig, - }, - { - name: "no block in DB, genesis == nil", - fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { - return SetupGenesisBlock(db, nil) - }, - wantHash: params.MainnetGenesisHash, - wantConfig: params.LcpChainConfig, - }, - { - name: "mainnet block in DB, genesis == nil", - fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { - DefaultGenesisBlock().MustCommit(db) - return SetupGenesisBlock(db, nil) - }, - wantHash: params.MainnetGenesisHash, - wantConfig: params.LcpChainConfig, - }, { name: "custom block in DB, genesis == nil", fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { @@ -94,16 +94,6 @@ func TestSetupGenesis(t *testing.T) { wantHash: customghash, wantConfig: customg.Config, }, - { - name: "custom block in DB, genesis == testnet", - fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { - customg.MustCommit(db) - return SetupGenesisBlock(db, DefaultTestnetGenesisBlock()) - }, - wantErr: &GenesisMismatchError{Stored: customghash, New: params.TestnetGenesisHash}, - wantHash: params.TestnetGenesisHash, - wantConfig: params.TestnetChainConfig, - }, { name: "compatible config in DB", fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { @@ -119,12 +109,10 @@ func TestSetupGenesis(t *testing.T) { // Commit the 'old' genesis block with Homestead transition at #2. // Advance to block #4, past the homestead transition block of customg. genesis := oldcustomg.MustCommit(db) - - bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{}) + bc, _ := NewBlockChain(db,nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{}) defer bc.Stop() - - blocks, _ := GenerateChain(oldcustomg.Config, genesis, ethash.NewFaker(), db, 4, nil) - bc.InsertChain(blocks) + bc.SetValidator(bproc{}) + bc.InsertChain(makeBlockChainWithDiff(genesis, []int{2, 3, 4, 5}, 0)) bc.CurrentBlock() // This should return a compatibility error. return SetupGenesisBlock(db, &customg) @@ -141,7 +129,7 @@ func TestSetupGenesis(t *testing.T) { } for _, test := range tests { - db := ethdb.NewMemDatabase() + db:= ethdb.NewMemDatabase() config, hash, err := test.fn(db) // Check the return values. if !reflect.DeepEqual(err, test.wantErr) { @@ -155,7 +143,7 @@ func TestSetupGenesis(t *testing.T) { t.Errorf("%s: returned hash %s, want %s", test.name, hash.Hex(), test.wantHash.Hex()) } else if err == nil { // Check database content. - stored := rawdb.ReadBlock(db, test.wantHash, 0) + stored := GetBlock(db, test.wantHash, 0) if stored.Hash() != test.wantHash { t.Errorf("%s: block in DB has hash %s, want %s", test.name, stored.Hash(), test.wantHash) } diff --git a/core/types/block.go b/core/types/block.go index 1b92587383..32ad088eae 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -338,7 +338,7 @@ func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles} } func (b *Block) HashNoNonce() common.Hash { return b.header.HashNoNonce() } - +func (b *Block) LcpCtx() *LCPContext { return b.LCPContext } // Size returns the true RLP encoded storage size of the block, either by encoding // and returning it, or returning a previsouly cached value. func (b *Block) Size() common.StorageSize { diff --git a/core/types/lcp_context.go b/core/types/lcp_context.go index 880a76a87a..2182e5358c 100644 --- a/core/types/lcp_context.go +++ b/core/types/lcp_context.go @@ -333,7 +333,10 @@ func (d *LCPContext) BecomeCandidate(candidateAddr common.Address) error { candidate := candidateAddr.Bytes() return d.candidateTrie.TryUpdate(candidate, candidate) } - +func (d *LCPContext) BecomeDelegate(delegateAdr common.Address) error { + delegate := delegateAdr.Bytes() + return d.delegateTrie.TryUpdate(delegate, delegate) +} func (d *LCPContext) Delegate(delegatorAddr, candidateAddr common.Address) error { delegator, candidate := delegatorAddr.Bytes(), candidateAddr.Bytes() diff --git a/core/types/transaction.go b/core/types/transaction.go index 6ff491dc7a..ea75d1d668 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -23,6 +23,7 @@ import ( "math/big" "sync/atomic" + "fmt" "github.com/pavelkrolevets/go-ethereum/common" "github.com/pavelkrolevets/go-ethereum/common/hexutil" "github.com/pavelkrolevets/go-ethereum/crypto" @@ -34,10 +35,31 @@ import ( // transaction type type TxType uint8 -var ( - ErrInvalidSig = errors.New("invalid transaction v, r, s values") +const ( + Binary TxType = iota + LoginCandidate + LogoutCandidate + Delegate + UnDelegate ) +var ( + ErrInvalidSig = errors.New("invalid transaction v, r, s values") + errNoSigner = errors.New("missing signing methods") + ErrInvalidType = errors.New("invalid transaction type") + ErrInvalidAddress = errors.New("invalid transaction payload address") + ErrInvalidAction = errors.New("invalid transaction payload action") +) + +// deriveSigner makes a *best* guess about which signer to use. +func deriveSigner(V *big.Int) Signer { + if V.Sign() != 0 && isProtectedV(V) { + return NewEIP155Signer(deriveChainId(V)) + } else { + return HomesteadSigner{} + } +} + type Transaction struct { data txdata // caches @@ -47,6 +69,7 @@ type Transaction struct { } type txdata struct { + Type TxType `json:"type" gencodec:"required"` AccountNonce uint64 `json:"nonce" gencodec:"required"` Price *big.Int `json:"gasPrice" gencodec:"required"` GasLimit uint64 `json:"gas" gencodec:"required"` @@ -69,20 +92,25 @@ type txdataMarshaling struct { GasLimit hexutil.Uint64 Amount *hexutil.Big Payload hexutil.Bytes + Type TxType V *hexutil.Big R *hexutil.Big S *hexutil.Big } -func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { - return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data) +func NewTransaction(txType TxType, nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { + // compatible with raw transaction with to address is empty + if txType != Binary && (to == common.Address{}) { + return newTransaction(txType, nonce, nil, amount, gasLimit, gasPrice, data) + } + return newTransaction(txType, nonce, &to, amount, gasLimit, gasPrice, data) } -func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { - return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data) +func NewContractCreation(txType TxType, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { + return newTransaction(txType, nonce, nil, amount, gasLimit, gasPrice, data) } -func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { +func newTransaction(txType TxType, nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { if len(data) > 0 { data = common.CopyBytes(data) } @@ -93,6 +121,7 @@ func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit Amount: new(big.Int), GasLimit: gasLimit, Price: new(big.Int), + Type: txType, V: new(big.Int), R: new(big.Int), S: new(big.Int), @@ -112,6 +141,22 @@ func (tx *Transaction) ChainId() *big.Int { return deriveChainId(tx.data.V) } +// Valid the transaction when the type isn't the binary +func (tx *Transaction) Validate() error { + if tx.Type() != Binary { + if tx.Value().Uint64() != 0 { + return errors.New("transaction value should be 0") + } + if tx.To() == nil && tx.Type() != LoginCandidate && tx.Type() != LogoutCandidate { + return errors.New("receipient was required") + } + if tx.Data() != nil { + return errors.New("payload should be empty") + } + } + return nil +} + // Protected returns whether the transaction is protected from replay protection. func (tx *Transaction) Protected() bool { return isProtectedV(tx.data.V) @@ -176,6 +221,7 @@ func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Pri func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) } func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce } func (tx *Transaction) CheckNonce() bool { return true } +func (tx *Transaction) Type() TxType { return tx.data.Type } // To returns the recipient address of the transaction. // It returns nil if the transaction is a contract creation. @@ -223,6 +269,7 @@ func (tx *Transaction) AsMessage(s Signer) (Message, error) { to: tx.data.Recipient, amount: tx.data.Amount, data: tx.data.Payload, + txType: tx.data.Type, checkNonce: true, } @@ -254,6 +301,60 @@ func (tx *Transaction) RawSignatureValues() (*big.Int, *big.Int, *big.Int) { return tx.data.V, tx.data.R, tx.data.S } +func (tx *Transaction) String() string { + var from, to string + if tx.data.V != nil { + // make a best guess about the signer and use that to derive + // the sender. + signer := deriveSigner(tx.data.V) + if f, err := Sender(signer, tx); err != nil { // derive but don't cache + from = "[invalid sender: invalid sig]" + } else { + from = fmt.Sprintf("%x", f[:]) + } + } else { + from = "[invalid sender: nil V field]" + } + + if tx.data.Recipient == nil { + to = "[contract creation]" + } else { + to = fmt.Sprintf("%x", tx.data.Recipient[:]) + } + enc, _ := rlp.EncodeToBytes(&tx.data) + return fmt.Sprintf(` + TX(%x) + Type: %d + Contract: %v + From: %s + To: %s + Nonce: %v + GasPrice: %#x + GasLimit %#x + Value: %#x + Data: 0x%x + V: %#x + R: %#x + S: %#x + Hex: %x +`, + tx.Hash(), + tx.Type(), + tx.data.Recipient == nil, + from, + to, + tx.data.AccountNonce, + tx.data.Price, + tx.data.GasLimit, + tx.data.Amount, + tx.data.Payload, + tx.data.V, + tx.data.R, + tx.data.S, + enc, + ) +} + // Transactions is a Transaction slice type for basic sorting. type Transactions []*Transaction diff --git a/trie/trie.go b/trie/trie.go index d060d0b4bc..7200c72bcc 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -142,6 +142,9 @@ func (t *Trie) PrefixIterator(prefix []byte) NodeIterator { // NodeIterator returns an iterator that returns nodes of the trie. Iteration starts at // the key after the given start key. func (t *Trie) NodeIterator(start []byte) NodeIterator { + if t.prefix != nil { + start = append(t.prefix, start...) + } return newNodeIterator(t, start) } @@ -159,6 +162,9 @@ func (t *Trie) Get(key []byte) []byte { // The value bytes must not be modified by the caller. // If a node was not found in the database, a MissingNodeError is returned. func (t *Trie) TryGet(key []byte) ([]byte, error) { + if t.prefix != nil { + key = append(t.prefix, key...) + } key = keybytesToHex(key) value, newroot, didResolve, err := t.tryGet(t.root, key, 0) if err == nil && didResolve { @@ -226,6 +232,9 @@ func (t *Trie) Update(key, value []byte) { // // If a node was not found in the database, a MissingNodeError is returned. func (t *Trie) TryUpdate(key, value []byte) error { + if t.prefix != nil { + key = append(t.prefix, key...) + } k := keybytesToHex(key) if len(value) != 0 { _, n, err := t.insert(t.root, nil, k, valueNode(value)) @@ -322,6 +331,9 @@ func (t *Trie) Delete(key []byte) { // TryDelete removes any existing value for key from the trie. // If a node was not found in the database, a MissingNodeError is returned. func (t *Trie) TryDelete(key []byte) error { + if t.prefix != nil { + key = append(t.prefix, key...) + } k := keybytesToHex(key) _, n, err := t.delete(t.root, nil, k) if err != nil {