diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ef0045df8..3a594ecfa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ on: workflow_dispatch: #allow manual trigger to workflow jobs: - test_build_and_push_to_docker_registry: + test_golang: name: Test, build and push image to registry runs-on: ubuntu-latest env: @@ -17,12 +17,48 @@ jobs: defaults: run: working-directory: ${{ env.GOPATH }}/src/XDC-Subnet + strategy: + fail-fast: false + matrix: + include: + - name: A-B tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDC-Subnet/[a-b].*") + - name: C-[a-m] tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDC-Subnet/c[a-m].*") + - name: C-[n-o] tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDC-Subnet/c[n-o].*") + - name: C-[p-z] tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDC-Subnet/c[p-z].*") + - name: D-I tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDC-Subnet/[d-i].*") + - name: J-N tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDC-Subnet/[j-n].*") + - name: O-R tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDC-Subnet/[o-r].*") + - name: S tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDC-Subnet/s.*") + - name: T-Z tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDC-Subnet/[t-z].*") steps: - name: Check out code uses: actions/checkout@v3 with: path: ${{ env.GOPATH }}/src/XDC-Subnet - + - name: Set up Go 1.21.x + uses: actions/setup-go@v4 + with: + go-version: "1.21.x" # The Go version to download (if necessary) and use. + - name: Run tests + run: ${{ matrix.script }} + env: + GO111MODULE: auto + + test_nodejs: + name: Run nodejs tests + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v3 - name: Use Node.js uses: actions/setup-node@v1 with: @@ -32,16 +68,12 @@ jobs: cd contracts/validator/src yarn npx hardhat test - - name: Set up Go 1.21.x - uses: actions/setup-go@v4 - with: - go-version: "1.21.x" # The Go version to download (if necessary) and use. - - name: Run tests - run: | - make test - env: - GO111MODULE: auto + build: + needs: [test_golang, test_nodejs] + name: Build and push image to registry + runs-on: ubuntu-latest + steps: - name: Docker login env: DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}} @@ -61,5 +93,4 @@ jobs: - name: Build and push image run: | docker build . --file docker/Dockerfile --tag ${{ steps.image.outputs.name }} - docker push ${{ steps.image.outputs.name }} - + docker push ${{ steps.image.outputs.name }} \ No newline at end of file diff --git a/.github/workflows/pr_test.yml b/.github/workflows/pr_test.yml index 485a9dff8..1e621b2d7 100644 --- a/.github/workflows/pr_test.yml +++ b/.github/workflows/pr_test.yml @@ -5,8 +5,8 @@ on: - master jobs: - test_on_pr: - name: Run tests on PR + test_golang: + name: Run golang tests runs-on: ubuntu-latest env: GOPATH: ${{ github.workspace }} @@ -14,11 +14,48 @@ jobs: defaults: run: working-directory: ${{ env.GOPATH }}/src/XDC-Subnet + strategy: + fail-fast: false + matrix: + include: + - name: A-B tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDC-Subnet/[a-b].*") + - name: C-[a-m] tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDC-Subnet/c[a-m].*") + - name: C-[n-o] tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDC-Subnet/c[n-o].*") + - name: C-[p-z] tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDC-Subnet/c[p-z].*") + - name: D-I tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDC-Subnet/[d-i].*") + - name: J-N tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDC-Subnet/[j-n].*") + - name: O-R tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDC-Subnet/[o-r].*") + - name: S tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDC-Subnet/s.*") + - name: T-Z tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDC-Subnet/[t-z].*") steps: - name: Check out code uses: actions/checkout@v3 with: path: ${{ env.GOPATH }}/src/XDC-Subnet + - name: Set up Go 1.21.x + uses: actions/setup-go@v4 + with: + go-version: "1.21.x" # The Go version to download (if necessary) and use. + - name: Run tests + run: ${{ matrix.script }} + env: + GO111MODULE: auto + + test_nodejs: + name: Run nodejs tests + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v3 - name: Use Node.js uses: actions/setup-node@v1 with: @@ -28,12 +65,3 @@ jobs: cd contracts/validator/src yarn npx hardhat test - - name: Set up Go 1.21.x - uses: actions/setup-go@v4 - with: - go-version: "1.21.x" # The Go version to download (if necessary) and use. - - name: Run tests - run: | - make test - env: - GO111MODULE: auto diff --git a/XDCxlending/lendingstate/lendingitem.go b/XDCxlending/lendingstate/lendingitem.go index e2c57e0d7..a2a26782f 100644 --- a/XDCxlending/lendingstate/lendingitem.go +++ b/XDCxlending/lendingstate/lendingitem.go @@ -2,14 +2,15 @@ package lendingstate import ( "fmt" + "math/big" + "strconv" + "time" + "github.com/XinFinOrg/XDC-Subnet/common" "github.com/XinFinOrg/XDC-Subnet/core/state" "github.com/XinFinOrg/XDC-Subnet/core/types" "github.com/XinFinOrg/XDC-Subnet/crypto/sha3" "github.com/globalsign/mgo/bson" - "math/big" - "strconv" - "time" ) const ( @@ -243,7 +244,7 @@ func (l *LendingItem) VerifyLendingSide() error { } func (l *LendingItem) VerifyCollateral(state *state.StateDB) error { - if l.CollateralToken.String() == EmptyAddress || l.CollateralToken.String() == l.LendingToken.String() { + if l.CollateralToken.IsZero() || l.CollateralToken == l.LendingToken { return fmt.Errorf("invalid collateral %s", l.CollateralToken.Hex()) } validCollateral := false @@ -329,7 +330,7 @@ func (l *LendingItem) EncodedSide() *big.Int { return big.NewInt(1) } -//verify signatures +// verify signatures func (l *LendingItem) VerifyLendingSignature() error { V := big.NewInt(int64(l.Signature.V)) R := l.Signature.R.Big() diff --git a/XDCxlending/order_processor.go b/XDCxlending/order_processor.go index c67a008f4..f695b30a8 100644 --- a/XDCxlending/order_processor.go +++ b/XDCxlending/order_processor.go @@ -262,10 +262,9 @@ func (l *Lending) processOrderList(header *types.Header, coinbase common.Address collateralToken = oldestOrder.CollateralToken borrowFee = lendingstate.GetFee(statedb, oldestOrder.Relayer) } - if collateralToken.String() == lendingstate.EmptyAddress { + if collateralToken.IsZero() { return nil, nil, nil, fmt.Errorf("empty collateral") } - collateralPrice := common.BasePrice depositRate, liquidationRate, recallRate := lendingstate.GetCollateralDetail(statedb, collateralToken) if depositRate == nil || depositRate.Sign() <= 0 { return nil, nil, nil, fmt.Errorf("invalid depositRate %v", depositRate) @@ -953,11 +952,11 @@ func (l *Lending) GetMediumTradePriceBeforeEpoch(chain consensus.ChainContext, s return nil, nil } -//LendToken and CollateralToken must meet at least one of following conditions -//- Have direct pair in XDCX: lendToken/CollateralToken or CollateralToken/LendToken -//- Have pairs with XDC: -//- lendToken/XDC and CollateralToken/XDC -//- XDC/lendToken and XDC/CollateralToken +// LendToken and CollateralToken must meet at least one of following conditions +// - Have direct pair in XDCX: lendToken/CollateralToken or CollateralToken/LendToken +// - Have pairs with XDC: +// - lendToken/XDC and CollateralToken/XDC +// - XDC/lendToken and XDC/CollateralToken func (l *Lending) GetCollateralPrices(header *types.Header, chain consensus.ChainContext, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, collateralToken common.Address, lendingToken common.Address) (*big.Int, *big.Int, error) { // lendTokenXDCPrice: price of ticker lendToken/XDC // collateralXDCPrice: price of ticker collateralToken/XDC diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index 3777ad7e3..c883b787b 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -128,7 +128,7 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string, return err } var ( - msg = XDPoSChain.CallMsg{From: opts.From, To: &c.address, Data: input, GasPrice: common.MinGasPrice, Gas: uint64(4200000)} + msg = XDPoSChain.CallMsg{From: opts.From, To: &c.address, Data: input, GasPrice: common.MinGasPrice50x, Gas: uint64(4200000)} ctx = ensureContext(opts.Context) code []byte output []byte diff --git a/cmd/XDC/config.go b/cmd/XDC/config.go index 93be526d7..2b349689e 100644 --- a/cmd/XDC/config.go +++ b/cmd/XDC/config.go @@ -182,6 +182,7 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, XDCConfig) { common.MinGasPrice = big.NewInt(gasPrice) } } + common.MinGasPrice50x = common.MinGasPrice50x.Mul(common.MinGasPrice, big.NewInt(50)) // read passwords from environment passwords := []string{} diff --git a/common/constants.go b/common/constants.go index 373bde5b1..9b9d74ff0 100644 --- a/common/constants.go +++ b/common/constants.go @@ -69,8 +69,8 @@ var TRC21GasPrice = big.NewInt(250000000) var RateTopUp = big.NewInt(90) // 90% var BaseTopUp = big.NewInt(100) var BaseRecall = big.NewInt(100) -var TIPTRC21Fee = big.NewInt(13523400) -var TIPTRC21FeeTestnet = big.NewInt(225000) +var TIPTRC21Fee = big.NewInt(0) +var TIPTRC21FeeTestnet = big.NewInt(0) var LimitTimeFinality = uint64(30) // limit in 30 block var IgnoreSignerCheckBlockArray = map[uint64]bool{ diff --git a/common/gas.go b/common/gas.go new file mode 100644 index 000000000..34ef13f2b --- /dev/null +++ b/common/gas.go @@ -0,0 +1,30 @@ +package common + +import ( + "math/big" +) + +var MinGasPrice50x = big.NewInt(12500000000) +var GasPrice50x = big.NewInt(12500000000) + +func GetGasFee(blockNumber, gas uint64) *big.Int { + fee := new(big.Int).SetUint64(gas) + if blockNumber >= uint64(10) { //temp fix trc21issuer test fail + fee = fee.Mul(fee, GasPrice50x) + } + return fee +} + +func GetGasPrice(number *big.Int) *big.Int { + if number == nil { + return new(big.Int).Set(TRC21GasPrice) + } + return new(big.Int).Set(GasPrice50x) +} + +func GetMinGasPrice(number *big.Int) *big.Int { + if number == nil { + return new(big.Int).Set(MinGasPrice) + } + return new(big.Int).Set(MinGasPrice50x) +} diff --git a/common/types.go b/common/types.go index 323fa9c08..68f909105 100644 --- a/common/types.go +++ b/common/types.go @@ -73,6 +73,9 @@ func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) } func Uint64ToHash(b uint64) Hash { return BytesToHash(new(big.Int).SetUint64(b).Bytes()) } func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) } +// IsZero returns if a Hash is empty +func (h Hash) IsZero() bool { return h == Hash{} } + // Get the string representation of the underlying hash func (h Hash) Str() string { return string(h[:]) } func (h Hash) Bytes() []byte { return h[:] } @@ -190,6 +193,9 @@ func IsHexAddress(s string) bool { return len(s) == 2*AddressLength && isHex(s) } +// IsZero returns if a address is empty +func (a Address) IsZero() bool { return a == Address{} } + // Get the string representation of the underlying address func (a Address) Str() string { return string(a[:]) } func (a Address) Bytes() []byte { return a[:] } diff --git a/consensus/XDPoS/XDPoS.go b/consensus/XDPoS/XDPoS.go index b0a49b2ee..b21ee80af 100644 --- a/consensus/XDPoS/XDPoS.go +++ b/consensus/XDPoS/XDPoS.go @@ -17,6 +17,7 @@ package XDPoS import ( + "fmt" "math/big" "github.com/XinFinOrg/XDC-Subnet/common" @@ -88,7 +89,11 @@ func New(chainConfig *params.ChainConfig, db ethdb.Database) *XDPoS { } } - log.Info("xdc config loading", "config", config) + if config.V2.SwitchBlock.Uint64()%config.Epoch != 0 { + panic(fmt.Sprintf("v2 switch number is not epoch switch block %d, epoch %d", config.V2.SwitchBlock.Uint64(), config.Epoch)) + } + + log.Info("xdc config loading", "v2 config", config.V2) minePeriodCh := make(chan int) diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index 6a6d9d121..838c5756e 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -171,7 +171,7 @@ func (x *XDPoS_v2) Initial(chain consensus.ChainReader, header *types.Header) er } func (x *XDPoS_v2) initial(chain consensus.ChainReader, header *types.Header) error { - log.Info("[initial] initial v2 related parameters") + log.Warn("[initial] initial v2 related parameters") if x.highestQuorumCert.ProposedBlockInfo.Hash != (common.Hash{}) { // already initialized log.Info("[initial] Already initialized", "x.highestQuorumCert.ProposedBlockInfo.Hash", x.highestQuorumCert.ProposedBlockInfo.Hash) @@ -234,6 +234,7 @@ func (x *XDPoS_v2) initial(chain consensus.ChainReader, header *types.Header) er log.Error("[initial] Error while get masternodes", "error", err) return err } + snap := newSnapshot(lastGapNum, lastGapHeader.Hash(), masternodes, []common.Address{}) x.snapshots.Add(snap.Hash, snap) err = storeSnapshot(snap, x.db) @@ -244,7 +245,7 @@ func (x *XDPoS_v2) initial(chain consensus.ChainReader, header *types.Header) er } // Initial timeout - log.Info("[initial] miner wait period", "period", x.config.V2.CurrentConfig.MinePeriod) + log.Warn("[initial] miner wait period", "period", x.config.V2.CurrentConfig.MinePeriod) // avoid deadlock go func() { x.minePeriodCh <- x.config.V2.CurrentConfig.MinePeriod @@ -254,7 +255,7 @@ func (x *XDPoS_v2) initial(chain consensus.ChainReader, header *types.Header) er x.timeoutWorker.Reset(chain) x.isInitilised = true - log.Info("[initial] finish initialisation") + log.Warn("[initial] finish initialisation") return nil } diff --git a/contracts/trc21issuer/simulation/test/main.go b/contracts/trc21issuer/simulation/test/main.go index 7633465c3..f764a8c85 100644 --- a/contracts/trc21issuer/simulation/test/main.go +++ b/contracts/trc21issuer/simulation/test/main.go @@ -49,11 +49,10 @@ func airDropTokenToAccountNoXDC() { if err != nil { log.Fatal("can't transaction's receipt ", err, "hash", tx.Hash().Hex()) } - fee := big.NewInt(0).SetUint64(hexutil.MustDecodeUint64(receipt["gasUsed"].(string))) - if hexutil.MustDecodeUint64(receipt["blockNumber"].(string)) > common.TIPTRC21Fee.Uint64() { - fee = fee.Mul(fee, common.TRC21GasPrice) - } - fmt.Println("fee", fee.Uint64(), "number", hexutil.MustDecodeUint64(receipt["blockNumber"].(string))) + gasUsed := hexutil.MustDecodeUint64(receipt["gasUsed"].(string)) + blockNumber := hexutil.MustDecodeUint64(receipt["blockNumber"].(string)) + fee := common.GetGasFee(blockNumber, gasUsed) + fmt.Println("fee", fee.Uint64(), "number", blockNumber) remainFee = big.NewInt(0).Sub(remainFee, fee) //check balance fee balanceIssuerFee, err := trc21IssuerInstance.GetTokenCapacity(trc21TokenAddr) @@ -112,11 +111,10 @@ func testTransferTRC21TokenWithAccountNoXDC() { if err != nil { log.Fatal("can't transaction's receipt ", err, "hash", tx.Hash().Hex()) } - fee := big.NewInt(0).SetUint64(hexutil.MustDecodeUint64(receipt["gasUsed"].(string))) - if hexutil.MustDecodeUint64(receipt["blockNumber"].(string)) > common.TIPTRC21Fee.Uint64() { - fee = fee.Mul(fee, common.TRC21GasPrice) - } - fmt.Println("fee", fee.Uint64(), "number", hexutil.MustDecodeUint64(receipt["blockNumber"].(string))) + gasUsed := hexutil.MustDecodeUint64(receipt["gasUsed"].(string)) + blockNumber := hexutil.MustDecodeUint64(receipt["blockNumber"].(string)) + fee := common.GetGasFee(blockNumber, gasUsed) + fmt.Println("fee", fee.Uint64(), "number", blockNumber) remainFee = big.NewInt(0).Sub(remainFee, fee) //check balance fee balanceIssuerFee, err := trc21IssuerInstance.GetTokenCapacity(trc21TokenAddr) @@ -179,11 +177,10 @@ func testTransferTrc21Fail() { if err != nil { log.Fatal("can't transaction's receipt ", err, "hash", tx.Hash().Hex()) } - fee := big.NewInt(0).SetUint64(hexutil.MustDecodeUint64(receipt["gasUsed"].(string))) - if hexutil.MustDecodeUint64(receipt["blockNumber"].(string)) > common.TIPTRC21Fee.Uint64() { - fee = fee.Mul(fee, common.TRC21GasPrice) - } - fmt.Println("fee", fee.Uint64(), "number", hexutil.MustDecodeUint64(receipt["blockNumber"].(string))) + gasUsed := hexutil.MustDecodeUint64(receipt["gasUsed"].(string)) + blockNumber := hexutil.MustDecodeUint64(receipt["blockNumber"].(string)) + fee := common.GetGasFee(blockNumber, gasUsed) + fmt.Println("fee", fee.Uint64(), "number", blockNumber) remainFee = big.NewInt(0).Sub(remainFee, fee) //check balance fee balanceIssuerFee, err = trc21IssuerInstance.GetTokenCapacity(trc21TokenAddr) diff --git a/contracts/trc21issuer/trc21issuer_test.go b/contracts/trc21issuer/trc21issuer_test.go index 85e44aecc..77087171b 100644 --- a/contracts/trc21issuer/trc21issuer_test.go +++ b/contracts/trc21issuer/trc21issuer_test.go @@ -82,10 +82,7 @@ func TestFeeTxWithTRC21Token(t *testing.T) { if err != nil { t.Fatal("can't transaction's receipt ", err, "hash", tx.Hash()) } - fee := big.NewInt(0).SetUint64(receipt.GasUsed) - if receipt.Logs[0].BlockNumber > common.TIPTRC21Fee.Uint64() { - fee = fee.Mul(fee, common.TRC21GasPrice) - } + fee := common.GetGasFee(receipt.Logs[0].BlockNumber, receipt.GasUsed) remainFee := big.NewInt(0).Sub(minApply, fee) // check balance trc21 again @@ -134,10 +131,7 @@ func TestFeeTxWithTRC21Token(t *testing.T) { if err != nil { t.Fatal("can't transaction's receipt ", err, "hash", tx.Hash()) } - fee = big.NewInt(0).SetUint64(receipt.GasUsed) - if receipt.Logs[0].BlockNumber > common.TIPTRC21Fee.Uint64() { - fee = fee.Mul(fee, common.TRC21GasPrice) - } + fee = common.GetGasFee(receipt.Logs[0].BlockNumber, receipt.GasUsed) remainFee = big.NewInt(0).Sub(remainFee, fee) //check balance fee balanceIssuerFee, err = trc21Issuer.GetTokenCapacity(trc21TokenAddr) diff --git a/core/blockchain.go b/core/blockchain.go index 0931f9976..33e123dbb 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -39,6 +39,7 @@ import ( "github.com/XinFinOrg/XDC-Subnet/consensus" "github.com/XinFinOrg/XDC-Subnet/consensus/XDPoS" "github.com/XinFinOrg/XDC-Subnet/consensus/XDPoS/utils" + contractValidator "github.com/XinFinOrg/XDC-Subnet/contracts/validator/contract" "github.com/XinFinOrg/XDC-Subnet/core/state" "github.com/XinFinOrg/XDC-Subnet/core/types" "github.com/XinFinOrg/XDC-Subnet/core/vm" @@ -986,8 +987,9 @@ func (bc *BlockChain) procFutureBlocks() { if i == len(blocks)-1 && err == nil { engine, ok := bc.Engine().(*XDPoS.XDPoS) if ok { + j := i go func() { - header := blocks[i].Header() + header := blocks[j].Header() err = engine.HandleProposedBlock(bc, header) if err != nil { log.Info("[procFutureBlocks] handle proposed block has error", "err", err, "block hash", header.Hash(), "number", header.Number) @@ -2199,6 +2201,31 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { return fmt.Errorf("Invalid new chain") } } + // Ensure XDPoS engine committed block will be not reverted + if xdpos, ok := bc.Engine().(*XDPoS.XDPoS); ok { + latestCommittedBlock := xdpos.EngineV2.GetLatestCommittedBlockInfo() + if latestCommittedBlock != nil { + currentBlock := bc.CurrentBlock() + currentBlock.Number().Cmp(latestCommittedBlock.Number) + cmp := commonBlock.Number().Cmp(latestCommittedBlock.Number) + if cmp < 0 { + for _, oldBlock := range oldChain { + if oldBlock.Number().Cmp(latestCommittedBlock.Number) == 0 { + if oldBlock.Hash() != latestCommittedBlock.Hash { + log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "committed hash", latestCommittedBlock.Hash) + } else { + log.Warn("Stop reorg, blockchain is under forking attack", "old committed num", oldBlock.Number(), "old committed hash", oldBlock.Hash()) + return fmt.Errorf("stop reorg, blockchain is under forking attack. old committed num %d, hash %x", oldBlock.Number(), oldBlock.Hash()) + } + } + } + } else if cmp == 0 { + if commonBlock.Hash() != latestCommittedBlock.Hash { + log.Error("Impossible reorg, please file an issue", "oldnum", commonBlock.Number(), "oldhash", commonBlock.Hash(), "committed hash", latestCommittedBlock.Hash) + } + } + } + } // Ensure the user sees large reorgs if len(oldChain) > 0 && len(newChain) > 0 { logFn := log.Warn @@ -2319,7 +2346,11 @@ func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, e var roundNumber = types.Round(0) engine, ok := bc.Engine().(*XDPoS.XDPoS) if ok { + var err error roundNumber, err = engine.EngineV2.GetRoundNumber(block.Header()) + if err != nil { + log.Error("reportBlock", "GetRoundNumber", err) + } } var receiptString string @@ -2428,6 +2459,11 @@ func (bc *BlockChain) HasHeader(hash common.Hash, number uint64) bool { return bc.hc.HasHeader(hash, number) } +// GetCanonicalHash returns the canonical hash for a given block number +func (bc *BlockChain) GetCanonicalHash(number uint64) common.Hash { + return bc.hc.GetCanonicalHash(number) +} + // GetBlockHashesFromHash retrieves a number of block hashes starting at a given // hash, fetching towards the genesis block. func (bc *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash { @@ -2498,22 +2534,38 @@ func (bc *BlockChain) UpdateM1() error { } log.Info("It's time to update new set of masternodes for the next epoch...") // get masternodes information from smart contract + client, err := bc.GetClient() + if err != nil { + return err + } + addr := common.HexToAddress(common.MasternodeVotingSMC) + validator, err := contractValidator.NewXDCValidator(addr, client) + if err != nil { + return err + } + opts := new(bind.CallOpts) var candidates []common.Address // get candidates from slot of stateDB // if can't get anything, request from contracts stateDB, err := bc.State() if err != nil { - log.Error("update masternodes has statedb error", "error", err) - return err + candidates, err = validator.GetCandidates(opts) + if err != nil { + return err + } + } else { + candidates = state.GetCandidates(stateDB) } - candidates = state.GetCandidates(stateDB) var ms []utils.Masternode for _, candidate := range candidates { - v := state.GetCandidateCap(stateDB, candidate) - //TODO: smart contract shouldn't return "0x0000000000000000000000000000000000000000" - if candidate.String() != "0x0000000000000000000000000000000000000000" { + v, err := validator.GetCandidateCap(opts, candidate) + if err != nil { + return err + } + // TODO: smart contract shouldn't return "0x0000000000000000000000000000000000000000" + if !candidate.IsZero() { ms = append(ms, utils.Masternode{Address: candidate, Stake: v}) } } diff --git a/core/chain_makers.go b/core/chain_makers.go index da8f9ff48..6903a0bb9 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -108,10 +108,7 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) { b.txs = append(b.txs, tx) b.receipts = append(b.receipts, receipt) if tokenFeeUsed { - fee := new(big.Int).SetUint64(gas) - if b.header.Number.Cmp(common.TIPTRC21Fee) > 0 { - fee = fee.Mul(fee, common.TRC21GasPrice) - } + fee := common.GetGasFee(b.header.Number.Uint64(), gas) state.UpdateTRC21Fee(b.statedb, map[common.Address]*big.Int{*tx.To(): new(big.Int).Sub(feeCapacity[*tx.To()], new(big.Int).SetUint64(gas))}, fee) } } diff --git a/core/database_util.go b/core/database_util.go index e0016ea32..d6c9a6ea6 100644 --- a/core/database_util.go +++ b/core/database_util.go @@ -260,6 +260,10 @@ func GetBlockReceipts(db DatabaseReader, hash common.Hash, number uint64) types. receipts := make(types.Receipts, len(storageReceipts)) for i, receipt := range storageReceipts { receipts[i] = (*types.Receipt)(receipt) + for _, log := range receipts[i].Logs { + // update BlockHash to fix #208 + log.BlockHash = hash + } } return receipts } diff --git a/core/headerchain.go b/core/headerchain.go index 9347cb730..b05c2cab1 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -383,6 +383,11 @@ func (hc *HeaderChain) GetHeaderByNumber(number uint64) *types.Header { return hc.GetHeader(hash, number) } +func (hc *HeaderChain) GetCanonicalHash(number uint64) common.Hash { + // TODO: return rawdb.ReadCanonicalHash(hc.chainDb, number) + return GetCanonicalHash(hc.chainDb, number) +} + // CurrentHeader retrieves the current head header of the canonical chain. The // header is retrieved from the HeaderChain's internal cache. func (hc *HeaderChain) CurrentHeader() *types.Header { diff --git a/core/lending_pool.go b/core/lending_pool.go index b32beb7c1..d9e047c6e 100644 --- a/core/lending_pool.go +++ b/core/lending_pool.go @@ -431,7 +431,7 @@ func (pool *LendingPool) validateNewLending(cloneStateDb *state.StateDB, cloneLe return ErrInvalidLendingType } if tx.Side() == lendingstate.Borrowing { - if tx.CollateralToken().String() == lendingstate.EmptyAddress || tx.CollateralToken().String() == tx.LendingToken().String() { + if tx.CollateralToken().IsZero() || tx.CollateralToken() == tx.LendingToken() { return ErrInvalidLendingCollateral } validCollateral := false @@ -541,7 +541,7 @@ func (pool *LendingPool) validateBalance(cloneStateDb *state.StateDB, cloneLendi // collateralPrice = BTC/USD (eg: 8000 USD) // lendTokenXDCPrice: price of lendingToken in XDC quote var lendTokenXDCPrice, collateralPrice, collateralTokenDecimal *big.Int - if collateralToken.String() != lendingstate.EmptyAddress { + if !collateralToken.IsZero() { collateralTokenDecimal, err = XDCXServ.GetTokenDecimal(pool.chain, cloneStateDb, collateralToken) if err != nil { return fmt.Errorf("validateOrder: failed to get collateralTokenDecimal. err: %v", err) diff --git a/core/state/statedb_utils.go b/core/state/statedb_utils.go index c3c2a191b..718e54ddf 100644 --- a/core/state/statedb_utils.go +++ b/core/state/statedb_utils.go @@ -93,16 +93,17 @@ func GetCandidates(statedb *StateDB) []common.Address { slot := slotValidatorMapping["candidates"] slotHash := common.BigToHash(new(big.Int).SetUint64(slot)) arrLength := statedb.GetState(common.HexToAddress(common.MasternodeVotingSMC), slotHash) - keys := []common.Hash{} - for i := uint64(0); i < arrLength.Big().Uint64(); i++ { + count := arrLength.Big().Uint64() + rets := make([]common.Address, 0, count) + + for i := uint64(0); i < count; i++ { key := GetLocDynamicArrAtElement(slotHash, i, 1) - keys = append(keys, key) - } - rets := []common.Address{} - for _, key := range keys { ret := statedb.GetState(common.HexToAddress(common.MasternodeVotingSMC), key) - rets = append(rets, common.HexToAddress(ret.Hex())) + if !ret.IsZero() { + rets = append(rets, common.HexToAddress(ret.Hex())) + } } + return rets } diff --git a/core/state_processor.go b/core/state_processor.go index 28248ba06..7b66f8d29 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -120,10 +120,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, tra receipts = append(receipts, receipt) allLogs = append(allLogs, receipt.Logs...) if tokenFeeUsed { - fee := new(big.Int).SetUint64(gas) - if block.Header().Number.Cmp(common.TIPTRC21Fee) > 0 { - fee = fee.Mul(fee, common.TRC21GasPrice) - } + fee := common.GetGasFee(block.Header().Number.Uint64(), gas) balanceFee[*tx.To()] = new(big.Int).Sub(balanceFee[*tx.To()], fee) balanceUpdated[*tx.To()] = balanceFee[*tx.To()] totalFeeUsed = totalFeeUsed.Add(totalFeeUsed, fee) @@ -201,10 +198,7 @@ func (p *StateProcessor) ProcessBlockNoValidator(cBlock *CalculatedBlock, stated receipts[i] = receipt allLogs = append(allLogs, receipt.Logs...) if tokenFeeUsed { - fee := new(big.Int).SetUint64(gas) - if block.Header().Number.Cmp(common.TIPTRC21Fee) > 0 { - fee = fee.Mul(fee, common.TRC21GasPrice) - } + fee := common.GetGasFee(block.Header().Number.Uint64(), gas) balanceFee[*tx.To()] = new(big.Int).Sub(balanceFee[*tx.To()], fee) balanceUpdated[*tx.To()] = balanceFee[*tx.To()] totalFeeUsed = totalFeeUsed.Add(totalFeeUsed, fee) diff --git a/core/tx_list.go b/core/tx_list.go index 35e6af59c..d180cdd30 100644 --- a/core/tx_list.go +++ b/core/tx_list.go @@ -290,7 +290,7 @@ func (l *txList) Forward(threshold uint64) types.Transactions { // a point in calculating all the costs or if the balance covers all. If the threshold // is lower than the costgas cap, the caps will be reset to a new high after removing // the newly invalidated transactions. -func (l *txList) Filter(costLimit *big.Int, gasLimit uint64, trc21Issuers map[common.Address]*big.Int) (types.Transactions, types.Transactions) { +func (l *txList) Filter(costLimit *big.Int, gasLimit uint64, trc21Issuers map[common.Address]*big.Int, number *big.Int) (types.Transactions, types.Transactions) { // If all transactions are below the threshold, short circuit if l.costcap.Cmp(costLimit) <= 0 && l.gascap <= gasLimit { return nil, nil @@ -303,7 +303,7 @@ func (l *txList) Filter(costLimit *big.Int, gasLimit uint64, trc21Issuers map[co maximum := costLimit if tx.To() != nil { if feeCapacity, ok := trc21Issuers[*tx.To()]; ok { - return new(big.Int).Add(costLimit, feeCapacity).Cmp(tx.TRC21Cost()) < 0 || tx.Gas() > gasLimit + return new(big.Int).Add(costLimit, feeCapacity).Cmp(tx.TxCost(number)) < 0 || tx.Gas() > gasLimit } } return tx.Cost().Cmp(maximum) > 0 || tx.Gas() > gasLimit diff --git a/core/tx_pool.go b/core/tx_pool.go index 83b78e5c2..137eda311 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -632,7 +632,11 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { // cost == V + GP * GL balance := pool.currentState.GetBalance(from) cost := tx.Cost() - minGasPrice := common.MinGasPrice + var number *big.Int = nil + if pool.chain.CurrentHeader() != nil { + number = pool.chain.CurrentHeader().Number + } + minGasPrice := common.GetMinGasPrice(number) feeCapacity := big.NewInt(0) if tx.To() != nil { @@ -641,8 +645,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { if !state.ValidateTRC21Tx(pool.pendingState.StateDB, from, *tx.To(), tx.Data()) { return ErrInsufficientFunds } - cost = tx.TRC21Cost() - minGasPrice = common.TRC21GasPrice + cost = tx.TxCost(number) } } if new(big.Int).Add(balance, feeCapacity).Cmp(cost) < 0 { @@ -1066,7 +1069,11 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) { pool.priced.Removed() } // Drop all transactions that are too costly (low balance or out of gas) - drops, _ := list.Filter(pool.currentState.GetBalance(addr), pool.currentMaxGas, pool.trc21FeeCapacity) + var number *big.Int = nil + if pool.chain.CurrentHeader() != nil { + number = pool.chain.CurrentHeader().Number + } + drops, _ := list.Filter(pool.currentState.GetBalance(addr), pool.currentMaxGas, pool.trc21FeeCapacity, number) for _, tx := range drops { hash := tx.Hash() log.Trace("Removed unpayable queued transaction", "hash", hash) @@ -1224,7 +1231,11 @@ func (pool *TxPool) demoteUnexecutables() { pool.priced.Removed() } // Drop all transactions that are too costly (low balance or out of gas), and queue any invalids back for later - drops, invalids := list.Filter(pool.currentState.GetBalance(addr), pool.currentMaxGas, pool.trc21FeeCapacity) + var number *big.Int = nil + if pool.chain.CurrentHeader() != nil { + number = pool.chain.CurrentHeader().Number + } + drops, invalids := list.Filter(pool.currentState.GetBalance(addr), pool.currentMaxGas, pool.trc21FeeCapacity, number) for _, tx := range drops { hash := tx.Hash() log.Trace("Removed unpayable pending transaction", "hash", hash) diff --git a/core/types/transaction.go b/core/types/transaction.go index 127ffed50..2accd6590 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -262,11 +262,7 @@ func (tx *Transaction) AsMessage(s Signer, balanceFee *big.Int, number *big.Int) var err error msg.from, err = Sender(s, tx) if balanceFee != nil { - if number.Cmp(common.TIPTRC21Fee) > 0 { - msg.gasPrice = common.TRC21GasPrice - } else { - msg.gasPrice = common.TRC21GasPriceBefore - } + msg.gasPrice = common.GasPrice50x } return msg, err } @@ -291,8 +287,8 @@ func (tx *Transaction) Cost() *big.Int { } // Cost returns amount + gasprice * gaslimit. -func (tx *Transaction) TRC21Cost() *big.Int { - total := new(big.Int).Mul(common.TRC21GasPrice, new(big.Int).SetUint64(tx.data.GasLimit)) +func (tx *Transaction) TxCost(number *big.Int) *big.Int { + total := new(big.Int).Mul(common.GetGasPrice(number), new(big.Int).SetUint64(tx.data.GasLimit)) total.Add(total, tx.data.Amount) return total } @@ -700,9 +696,9 @@ type Message struct { balanceTokenFee *big.Int } -func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, checkNonce bool, balanceTokenFee *big.Int) Message { +func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, checkNonce bool, balanceTokenFee *big.Int, number *big.Int) Message { if balanceTokenFee != nil { - gasPrice = common.TRC21GasPrice + gasPrice = common.GetGasPrice(number) } return Message{ from: from, diff --git a/eth/api_tracer.go b/eth/api_tracer.go index 2cacf45b8..a3bae2f1b 100644 --- a/eth/api_tracer.go +++ b/eth/api_tracer.go @@ -706,10 +706,7 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree } if tokenFeeUsed { - fee := new(big.Int).SetUint64(gas) - if block.Header().Number.Cmp(common.TIPTRC21Fee) > 0 { - fee = fee.Mul(fee, common.TRC21GasPrice) - } + fee := common.GetGasFee(block.Header().Number.Uint64(), gas) feeCapacity[*tx.To()] = new(big.Int).Sub(feeCapacity[*tx.To()], fee) balanceUpdated[*tx.To()] = feeCapacity[*tx.To()] totalFeeUsed = totalFeeUsed.Add(totalFeeUsed, fee) diff --git a/eth/filters/api.go b/eth/filters/api.go index 82c0c0727..3b08be81d 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -32,6 +32,7 @@ import ( "github.com/XinFinOrg/XDC-Subnet/ethdb" "github.com/XinFinOrg/XDC-Subnet/event" "github.com/XinFinOrg/XDC-Subnet/rpc" + "github.com/XinFinOrg/XDC-Subnet/core" ) var ( @@ -418,6 +419,10 @@ func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) { case LogsSubscription: logs := f.logs f.logs = nil + for _, log := range logs { + // update BlockHash to fix #208 + log.BlockHash = core.GetCanonicalHash(api.chainDb, log.BlockNumber) + } return returnLogs(logs), nil } } diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index 6c1d9df08..784f39145 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -141,7 +141,7 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) { } // Check gas price min. - minGasPrice := common.MinGasPrice + minGasPrice := common.GetMinGasPrice(head.Number) if price.Cmp(minGasPrice) < 0 { price = new(big.Int).Set(minGasPrice) } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index abceb9eee..2d295475f 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -31,8 +31,8 @@ import ( "github.com/XinFinOrg/XDC-Subnet/XDCx/tradingstate" "github.com/XinFinOrg/XDC-Subnet/accounts" - "github.com/XinFinOrg/XDC-Subnet/accounts/abi/bind" "github.com/XinFinOrg/XDC-Subnet/accounts/abi" + "github.com/XinFinOrg/XDC-Subnet/accounts/abi/bind" "github.com/XinFinOrg/XDC-Subnet/accounts/keystore" "github.com/XinFinOrg/XDC-Subnet/common" "github.com/XinFinOrg/XDC-Subnet/common/hexutil" @@ -797,11 +797,10 @@ func (s *PublicBlockChainAPI) GetCandidateStatus(ctx context.Context, coinbaseAd return result, err } candidatesAddresses := state.GetCandidates(statedb) + candidates = make([]utils.Masternode, 0, len(candidatesAddresses)) for _, address := range candidatesAddresses { v := state.GetCandidateCap(statedb, address) - if address.String() != "0x0000000000000000000000000000000000000000" { - candidates = append(candidates, utils.Masternode{Address: address, Stake: v}) - } + candidates = append(candidates, utils.Masternode{Address: address, Stake: v}) } } if err != nil || len(candidates) == 0 { @@ -811,24 +810,19 @@ func (s *PublicBlockChainAPI) GetCandidateStatus(ctx context.Context, coinbaseAd } maxMasternodes := common.MaxMasternodes - isTopCandidate := false - // check penalties from checkpoint headers and modify status of a node to SLASHED if it's in top 150 candidates - // if it's SLASHED but it's out of top 150, the status should be still PROPOSED + // check penalties from checkpoint headers and modify status of a node to SLASHED if it's in top maxMasternodes candidates. + // if it's SLASHED but it's out of top maxMasternodes, the status should be still PROPOSED. + isCandidate := false for i := 0; i < len(candidates); i++ { if coinbaseAddress == candidates[i].Address { - if i < maxMasternodes { - isTopCandidate = true - } + isCandidate = true result[fieldStatus] = statusProposed result[fieldCapacity] = candidates[i].Stake break } } - if !isTopCandidate { - return result, nil - } - // Second, Find candidates that have masternode status + // Get masternode list if engine, ok := s.b.GetEngine().(*XDPoS.XDPoS); ok { masternodes = engine.GetMasternodesFromCheckpointHeader(header) if len(masternodes) == 0 { @@ -839,43 +833,67 @@ func (s *PublicBlockChainAPI) GetCandidateStatus(ctx context.Context, coinbaseAd } else { log.Error("Undefined XDPoS consensus engine") } - // Set masternode status + + // Set to statusMasternode if it is masternode for _, masternode := range masternodes { if coinbaseAddress == masternode { result[fieldStatus] = statusMasternode + if !isCandidate { + result[fieldCapacity] = -1 + log.Warn("Find non-candidate masternode", "masternode", masternode.String(), "checkpointNumber", checkpointNumber, "epoch", epoch, "epochNumber", epochNumber) + } return result, nil } } + if !isCandidate || len(masternodes) >= maxMasternodes { + return result, nil + } + + if len(candidates) > maxMasternodes { + sort.Slice(candidates, func(i, j int) bool { + return candidates[i].Stake.Cmp(candidates[j].Stake) > 0 + }) + } + // Third, Get penalties list penaltyList = append(penalties, header.Penalties...) // map slashing status - for _, pen := range penaltyList { - if coinbaseAddress == pen { - result[fieldStatus] = statusSlashed - return result, nil + total := len(masternodes) + for _, candidate := range candidates { + for _, pen := range penaltyList { + if candidate.Address == pen { + if coinbaseAddress == pen { + result[fieldStatus] = statusSlashed + return result, nil + } + total++ + if total >= maxMasternodes { + return result, nil + } + } } } + return result, nil } // GetCandidates returns status of all candidates at a specified epochNumber func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.EpochNumber) (map[string]interface{}, error) { var ( - block *types.Block - header *types.Header - checkpointNumber rpc.BlockNumber - epochNumber rpc.EpochNumber - masternodes []common.Address - candidates []utils.Masternode - penalties []common.Address - err error + block *types.Block + header *types.Header + checkpointNumber rpc.BlockNumber + epochNumber rpc.EpochNumber + masternodes, penaltyList []common.Address + candidates []utils.Masternode + penalties []common.Address + err error ) result := map[string]interface{}{ fieldSuccess: true, } - candidatesStatusMap := map[string]map[string]interface{}{} checkpointNumber, epochNumber = s.GetPreviousCheckpointFromEpoch(ctx, epoch) result[fieldEpoch] = epochNumber.Int64() @@ -904,11 +922,10 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch return result, err } candidatesAddresses := state.GetCandidates(statedb) + candidates = make([]utils.Masternode, 0, len(candidatesAddresses)) for _, address := range candidatesAddresses { v := state.GetCandidateCap(statedb, address) - if address.String() != "0x0000000000000000000000000000000000000000" { - candidates = append(candidates, utils.Masternode{Address: address, Stake: v}) - } + candidates = append(candidates, utils.Masternode{Address: address, Stake: v}) } } @@ -917,15 +934,8 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch result[fieldSuccess] = false return result, err } - // First, set all candidate to propose - for _, candidate := range candidates { - candidatesStatusMap[candidate.Address.String()] = map[string]interface{}{ - fieldStatus: statusProposed, - fieldCapacity: candidate.Stake, - } - } - // Second, Find candidates that have masternode status + // Find candidates that have masternode status if engine, ok := s.b.GetEngine().(*XDPoS.XDPoS); ok { masternodes = engine.GetMasternodesFromCheckpointHeader(header) if len(masternodes) == 0 { @@ -936,36 +946,65 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch } else { log.Error("Undefined XDPoS consensus engine") } - // Set masternode status + + // Set all candidate to statusProposed + candidatesStatusMap := make(map[string]map[string]interface{}, len(candidates)) + for _, candidate := range candidates { + candidatesStatusMap[candidate.Address.String()] = map[string]interface{}{ + fieldStatus: statusProposed, + fieldCapacity: candidate.Stake, + } + } + + // Set masternodes to statusMasternode for _, masternode := range masternodes { - if candidatesStatusMap[masternode.String()] != nil { - candidatesStatusMap[masternode.String()][fieldStatus] = statusMasternode + key := masternode.String() + if candidatesStatusMap[key] != nil { + candidatesStatusMap[key][fieldStatus] = statusMasternode + } else { + candidatesStatusMap[key] = map[string]interface{}{ + fieldStatus: statusMasternode, + fieldCapacity: -1, + } + log.Warn("Masternode is not candidate", "masternode", key, "checkpointNumber", checkpointNumber, "epoch", epoch, "epochNumber", epochNumber) } } - // Third, Get penalties list - penalties = append(penalties, header.Penalties...) + maxMasternodes := common.MaxMasternodes - // map slashing status - if len(penalties) == 0 { + if len(masternodes) >= maxMasternodes { result[fieldCandidates] = candidatesStatusMap return result, nil } - var topCandidates []utils.Masternode - if len(candidates) > common.MaxMasternodes { - topCandidates = candidates[:common.MaxMasternodes] - } else { - topCandidates = candidates + if len(candidates) > maxMasternodes { + sort.Slice(candidates, func(i, j int) bool { + return candidates[i].Stake.Cmp(candidates[j].Stake) > 0 + }) } - // check penalties from checkpoint headers and modify status of a node to SLASHED if it's in top 150 candidates - // if it's SLASHED but it's out of top 150, the status should be still PROPOSED - for _, pen := range penalties { - for _, candidate := range topCandidates { - if candidate.Address == pen && candidatesStatusMap[pen.String()] != nil { + + // Get penalties list + penaltyList = append(penalties, header.Penalties...) + + // map slashing status + if len(penaltyList) == 0 { + result[fieldCandidates] = candidatesStatusMap + return result, nil + } + + // check penalties from checkpoint headers and modify status of a node to SLASHED if it's in top maxMasternodes candidates. + // if it's SLASHED but it's out of top maxMasternodes, the status should be still PROPOSED. + total := len(masternodes) + for _, candidate := range candidates { + for _, pen := range penaltyList { + if candidate.Address == pen { candidatesStatusMap[pen.String()][fieldStatus] = statusSlashed + total++ + if total >= maxMasternodes { + result[fieldCandidates] = candidatesStatusMap + return result, nil + } } - penalties = append(penalties, block.Penalties()...) } } @@ -977,24 +1016,23 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch // GetPreviousCheckpointFromEpoch returns header of the previous checkpoint func (s *PublicBlockChainAPI) GetPreviousCheckpointFromEpoch(ctx context.Context, epochNum rpc.EpochNumber) (rpc.BlockNumber, rpc.EpochNumber) { var checkpointNumber uint64 - - if engine, ok := s.b.GetEngine().(*XDPoS.XDPoS); ok { - if epochNum == rpc.LatestEpochNumber { - currentCheckpointNumber, epochNumber, err := engine.GetCurrentEpochSwitchBlock(s.chainReader, s.b.CurrentBlock().Number()) - if err != nil { - log.Error("[GetPreviousCheckpointFromEpoch] Error while trying to get current epoch switch block information", "Block", s.b.CurrentBlock(), "Error", err) - } - checkpointNumber = currentCheckpointNumber - epochNum = rpc.EpochNumber(epochNumber) - } else if epochNum < 2 { - checkpointNumber = 0 - } else { - checkpointNumber = s.b.ChainConfig().XDPoS.Epoch * (uint64(epochNum) - 1) - } - return rpc.BlockNumber(checkpointNumber), epochNum + epoch := s.b.ChainConfig().XDPoS.Epoch + + if epochNum == rpc.LatestEpochNumber { + blockNumer := s.b.CurrentBlock().Number().Uint64() + diff := blockNumer % epoch + // checkpoint number + checkpointNumber = blockNumer - diff + epochNum = rpc.EpochNumber(checkpointNumber / epoch) + if diff > 0 { + epochNum += 1 + } + } else if epochNum < 2 { + checkpointNumber = 0 } else { - panic("[GetPreviousCheckpointFromEpoch] Error while trying to get XDPoS consensus engine") + checkpointNumber = epoch * (uint64(epochNum) - 1) } + return rpc.BlockNumber(checkpointNumber), epochNum } // getCandidatesFromSmartContract returns all candidates with their capacities at the current time @@ -1016,23 +1054,18 @@ func (s *PublicBlockChainAPI) getCandidatesFromSmartContract() ([]utils.Masterno return []utils.Masternode{}, err } - var candidatesWithStakeInfo []utils.Masternode + candidatesWithStakeInfo := make([]utils.Masternode, 0, len(candidates)) for _, candidate := range candidates { - v, err := validator.GetCandidateCap(opts, candidate) - if err != nil { - return []utils.Masternode{}, err - } - if candidate.String() != "0x0000000000000000000000000000000000000000" { + if !candidate.IsZero() { + v, err := validator.GetCandidateCap(opts, candidate) + if err != nil { + return []utils.Masternode{}, err + } candidatesWithStakeInfo = append(candidatesWithStakeInfo, utils.Masternode{Address: candidate, Stake: v}) } - - if len(candidatesWithStakeInfo) > 0 { - sort.Slice(candidatesWithStakeInfo, func(i, j int) bool { - return candidatesWithStakeInfo[i].Stake.Cmp(candidatesWithStakeInfo[j].Stake) >= 0 - }) - } } + return candidatesWithStakeInfo, nil } @@ -1073,7 +1106,7 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr balanceTokenFee := big.NewInt(0).SetUint64(gas) balanceTokenFee = balanceTokenFee.Mul(balanceTokenFee, gasPrice) // Create new call message - msg := types.NewMessage(addr, args.To, 0, args.Value.ToInt(), gas, gasPrice, args.Data, false, balanceTokenFee) + msg := types.NewMessage(addr, args.To, 0, args.Value.ToInt(), gas, gasPrice, args.Data, false, balanceTokenFee, header.Number) // Setup context so it may be cancelled the call has completed // or, in case of unmetered gas, setup a context with a timeout. @@ -1248,7 +1281,7 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (h } // Otherwise, the specified gas cap is too low - return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap) + return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap) } } return hexutil.Uint64(hi), nil diff --git a/les/odr_test.go b/les/odr_test.go index 0dd5b4a33..b8dff0d5f 100644 --- a/les/odr_test.go +++ b/les/odr_test.go @@ -133,7 +133,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai if value, ok := feeCapacity[testContractAddr]; ok { balanceTokenFee = value } - msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, false, balanceTokenFee)} + msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, false, balanceTokenFee, header.Number)} context := core.NewEVMContext(msg, header, bc, nil) vmenv := vm.NewEVM(context, statedb, nil, config, vm.Config{}) @@ -153,7 +153,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, config *params.Chai if value, ok := feeCapacity[testContractAddr]; ok { balanceTokenFee = value } - msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, false, balanceTokenFee)} + msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, false, balanceTokenFee, header.Number)} context := core.NewEVMContext(msg, header, lc, nil) vmenv := vm.NewEVM(context, statedb, nil, config, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxUint64) diff --git a/light/odr_test.go b/light/odr_test.go index 057e7cb49..2a8d97e71 100644 --- a/light/odr_test.go +++ b/light/odr_test.go @@ -183,7 +183,7 @@ func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain if value, ok := feeCapacity[testContractAddr]; ok { balanceTokenFee = value } - msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, new(big.Int), data, false, balanceTokenFee)} + msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, new(big.Int), data, false, balanceTokenFee, header.Number)} context := core.NewEVMContext(msg, header, chain, nil) vmenv := vm.NewEVM(context, st, nil, config, vm.Config{}) gp := new(core.GasPool).AddGas(math.MaxUint64) diff --git a/miner/worker.go b/miner/worker.go index 9a125d011..c4ea1c1b8 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -931,10 +931,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad log.Debug("Add Special Transaction failed, account skipped", "hash", tx.Hash(), "sender", from, "nonce", tx.Nonce(), "to", tx.To(), "err", err) } if tokenFeeUsed { - fee := new(big.Int).SetUint64(gas) - if env.header.Number.Cmp(common.TIPTRC21Fee) > 0 { - fee = fee.Mul(fee, common.TRC21GasPrice) - } + fee := common.GetGasFee(env.header.Number.Uint64(), gas) balanceFee[*tx.To()] = new(big.Int).Sub(balanceFee[*tx.To()], fee) balanceUpdated[*tx.To()] = balanceFee[*tx.To()] totalFeeUsed = totalFeeUsed.Add(totalFeeUsed, fee) @@ -1049,10 +1046,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad txs.Shift() } if tokenFeeUsed { - fee := new(big.Int).SetUint64(gas) - if env.header.Number.Cmp(common.TIPTRC21Fee) > 0 { - fee = fee.Mul(fee, common.TRC21GasPrice) - } + fee := common.GetGasFee(env.header.Number.Uint64(), gas) balanceFee[*tx.To()] = new(big.Int).Sub(balanceFee[*tx.To()], fee) balanceUpdated[*tx.To()] = balanceFee[*tx.To()] totalFeeUsed = totalFeeUsed.Add(totalFeeUsed, fee) diff --git a/params/config.go b/params/config.go index bf335dbe4..20840106a 100644 --- a/params/config.go +++ b/params/config.go @@ -45,7 +45,7 @@ var ( CertThreshold: 5, // To be confirmed once mainnet is ready TimeoutSyncThreshold: 3, TimeoutPeriod: 60, - MinePeriod: 10, + MinePeriod: 2, }, } @@ -202,8 +202,8 @@ var ( // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllXDPoSProtocolChanges = &ChainConfig{big.NewInt(89), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &XDPoSConfig{Period: 0, Epoch: 30000}} - AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil} + AllXDPoSProtocolChanges = &ChainConfig{big.NewInt(89), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &XDPoSConfig{Period: 0, Epoch: 900}} + AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 900}, nil} // XDPoS config with v2 engine after block 0 TestXDPoSMockChainConfig = &ChainConfig{ @@ -339,7 +339,7 @@ func (v *V2) UpdateConfig(round uint64) { } } // update to current config - log.Info("[updateV2Config] Update config", "index", index, "round", round, "SwitchRound", v.AllConfigs[index].SwitchRound) + log.Warn("[updateV2Config] Update config", "index", index, "round", round, "SwitchRound", v.AllConfigs[index].SwitchRound) v.CurrentConfig = v.AllConfigs[index] } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index c1dfe6fa7..dbcb55ff1 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -131,7 +131,7 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD statedb := MakePreState(db, t.json.Pre) post := t.json.Post[subtest.Fork][subtest.Index] - msg, err := t.json.Tx.toMessage(post) + msg, err := t.json.Tx.toMessage(post, block.Number()) if err != nil { return nil, err } @@ -190,7 +190,7 @@ func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis { } } -func (tx *stTransaction) toMessage(ps stPostState) (core.Message, error) { +func (tx *stTransaction) toMessage(ps stPostState, number *big.Int) (core.Message, error) { // Derive sender from private key if present. var from common.Address if len(tx.PrivateKey) > 0 { @@ -235,7 +235,7 @@ func (tx *stTransaction) toMessage(ps stPostState) (core.Message, error) { if err != nil { return nil, fmt.Errorf("invalid tx data %q", dataHex) } - msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, data, true, nil) + msg := types.NewMessage(from, to, tx.Nonce, value, gasLimit, tx.GasPrice, data, true, nil, number) return msg, nil }