diff --git a/cmd/tomo/main.go b/cmd/tomo/main.go index cd075800da..d51bb8b979 100644 --- a/cmd/tomo/main.go +++ b/cmd/tomo/main.go @@ -357,7 +357,7 @@ func startNode(ctx *cli.Context, stack *node.Node) { // get masternodes information from smart contract client, err := ethclient.Dial(stack.IPCEndpoint()) if err != nil { - utils.Fatalf("Fail to connect RPC: %v", err) + utils.Fatalf("Fail to connect IPC: %v", err) } addr := common.HexToAddress(common.MasternodeVotingSMC) validator, err := validatorContract.NewTomoValidator(addr, client) diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 8546b8cd0f..39beaa4d0c 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -397,11 +397,7 @@ func position(list []common.Address, x common.Address) int { func (c *Clique) GetMasternodes(chain consensus.ChainReader, header *types.Header) []common.Address { lastCheckpointNumber := header.Number.Uint64() - (header.Number.Uint64() % c.config.Epoch) preCheckpointHeader := chain.GetHeaderByNumber(lastCheckpointNumber) - masternodes := make([]common.Address, (len(preCheckpointHeader.Extra)-extraVanity-extraSeal)/common.AddressLength) - for i := 0; i < len(masternodes); i++ { - copy(masternodes[i][:], preCheckpointHeader.Extra[extraVanity+i*common.AddressLength:]) - } - return masternodes + return c.GetMasternodesFromCheckpointHeader(preCheckpointHeader) } func (c *Clique) GetPeriod() uint64 { return c.config.Period } @@ -803,3 +799,12 @@ func (c *Clique) APIs(chain consensus.ChainReader) []rpc.API { func (c *Clique) RecoverSigner(header *types.Header) (common.Address, error) { return ecrecover(header, c.signatures) } + +// Get master nodes over extra data of previous checkpoint block. +func (c *Clique) GetMasternodesFromCheckpointHeader(preCheckpointHeader *types.Header) []common.Address { + masternodes := make([]common.Address, (len(preCheckpointHeader.Extra)-extraVanity-extraSeal)/common.AddressLength) + for i := 0; i < len(masternodes); i++ { + copy(masternodes[i][:], preCheckpointHeader.Extra[extraVanity+i*common.AddressLength:]) + } + return masternodes +} diff --git a/contracts/blocksigner/contract/blocksigner.go b/contracts/blocksigner/contract/blocksigner.go index 385c1f525c..34bc1061b2 100644 --- a/contracts/blocksigner/contract/blocksigner.go +++ b/contracts/blocksigner/contract/blocksigner.go @@ -377,7 +377,7 @@ func (_BlockSigner *BlockSignerFilterer) WatchSign(opts *bind.WatchOpts, sink ch const SafeMathABI = "[]" // SafeMathBin is the compiled bytecode used for deploying new contracts. -const SafeMathBin = `0x604c602c600b82828239805160001a60731460008114601c57601e565bfe5b5030600052607381538281f30073000000000000000000000000000000000000000030146060604052600080fd00a165627a7a72305820b9407d48ebc7efee5c9f08b3b3a957df2939281f5913225e8c1291f069b900490029` +const SafeMathBin = `0x604c602c600b82828239805160001a60731460008114601c57601e565bfe5b5030600052607381538281f30073000000000000000000000000000000000000000030146080604052600080fd00a165627a7a72305820a3f63b465e1cf25f306b1eb1efefc8dac3c38993a7340f69d8b470c3bf599ff30029` // DeploySafeMath deploys a new Ethereum contract, binding an instance of SafeMath to it. func DeploySafeMath(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *SafeMath, error) { diff --git a/contracts/utils_test.go b/contracts/utils_test.go index 78b068dcf6..44ce479b35 100644 --- a/contracts/utils_test.go +++ b/contracts/utils_test.go @@ -103,37 +103,6 @@ func TestSendTxSign(t *testing.T) { t.Error("Tx sign for block validators not match") } } - - // Unit test for reward checkpoint. - //rCheckpoint := uint64(5) - //chainReward := new(big.Int).SetUint64(15 * params.Ether) - //total := new(uint64) - //for i := uint64(0); i < 100; i++ { - // if i > 0 && i%rCheckpoint == 0 && i-rCheckpoint > 0 { - // _, err := GetRewardForCheckpoint(blockSignerAddr, i, rCheckpoint, backend, total) - // if err != nil { - // t.Errorf("Fail to get signers for reward checkpoint: %v", err) - // } - // } - //} - // - //signers := make(map[common.Address]*rewardLog) - //totalSigner := uint64(17) - //signers[common.HexToAddress("0x12f588d7d03bb269b382b842fc15d874e8c055a7")] = &rewardLog{5, new(big.Int).SetUint64(0)} - //signers[common.HexToAddress("0x1f9e122c0921a4504fc116d967baf7a7bf2604ef")] = &rewardLog{6, new(big.Int).SetUint64(0)} - //signers[common.HexToAddress("0xea489e4e673c25ff0614617ebe88efd853efe00c")] = &rewardLog{6, new(big.Int).SetUint64(0)} - //rewardSigners, err := CalculateRewardForSigner(chainReward, signers, totalSigner) - //if err != nil { - // t.Errorf("Fail to calculate reward for signers: %v", err) - //} - ////t.Error("Reward", rewardSigners) - //rewards := new(big.Int) - //for _, reward := range rewardSigners { - // rewards.Add(rewards, reward) - //} - //if rewards.Cmp(new(big.Int).SetUint64(14999999999999999996)) != 0 { - // t.Errorf("Total reward not same reward checkpoint: %v - %v", chainReward, rewards) - //} } // Generate random string. diff --git a/core/blockchain.go b/core/blockchain.go index 0f0b5f569a..abff618bf7 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -34,6 +34,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" @@ -128,7 +129,9 @@ type BlockChain struct { validator Validator // block and state validator interface vmConfig vm.Config - badBlocks *lru.Cache // Bad block cache + badBlocks *lru.Cache // Bad block cache + IPCEndpoint string + Client *ethclient.Client // Global ipc client instance. } // NewBlockChain returns a fully initialised block chain using information @@ -1572,3 +1575,18 @@ func (bc *BlockChain) SubscribeChainSideEvent(ch chan<- ChainSideEvent) event.Su func (bc *BlockChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { return bc.scope.Track(bc.logsFeed.Subscribe(ch)) } + +// Get current IPC Client. +func (bc *BlockChain) GetClient() (*ethclient.Client, error) { + if bc.Client == nil { + // Inject ipc client global instance. + client, err := ethclient.Dial(bc.IPCEndpoint) + if err != nil { + log.Error("Fail to connect IPC", "error", err) + return nil, err + } + bc.Client = client + } + + return bc.Client, nil +} diff --git a/eth/api_backend.go b/eth/api_backend.go index ecd5488a24..10a3e3f537 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/state" @@ -30,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/gasprice" + "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" @@ -218,3 +220,16 @@ func (b *EthApiBackend) ServiceFilter(ctx context.Context, session *bloombits.Ma go session.Multiplex(bloomRetrievalBatch, bloomRetrievalWait, b.eth.bloomRequests) } } + +func (b *EthApiBackend) GetIPCClient() (*ethclient.Client, error) { + client, err := b.eth.blockchain.GetClient() + if err != nil { + return nil, err + } + + return client, nil +} + +func (b *EthApiBackend) GetEngine() consensus.Engine { + return b.eth.engine +} diff --git a/eth/backend.go b/eth/backend.go index e3df33e09a..1949bc301c 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -41,7 +41,6 @@ import ( "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/gasprice" - "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/ethapi" @@ -97,9 +96,7 @@ type Ethereum struct { networkId uint64 netRPCService *ethapi.PublicNetAPI - lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase) - IPCEndpoint string - Client *ethclient.Client // Global ipc client instance. + lock sync.RWMutex // Protects the variadic fields (e.g. gas price and etherbase) } func (s *Ethereum) AddLesServer(ls LesServer) { @@ -186,17 +183,21 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { } eth.ApiBackend.gpo = gasprice.NewOracle(eth.ApiBackend, gpoParams) + // Set global ipc endpoint. + eth.blockchain.IPCEndpoint = ctx.GetConfig().IPCEndpoint() + if eth.chainConfig.Clique != nil { c := eth.engine.(*clique.Clique) - // Set global ipc endpoint. - eth.IPCEndpoint = ctx.GetConfig().IPCEndpoint() - // Inject hook for send tx sign to smartcontract after insert block into chain. importedHook := func(block *types.Block) { snap, err := c.GetSnapshot(eth.blockchain, block.Header()) if err != nil { - log.Error("Fail to get snapshot for sign tx validator.", "error", err) + if err == consensus.ErrUnknownAncestor { + log.Warn("Block chain forked.", "error", err) + } else { + log.Error("Fail to get snapshot for sign tx validator.", "error", err) + } return } if _, authorized := snap.Signers[eth.etherbase]; authorized { @@ -210,7 +211,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { // Hook reward for clique validator. c.HookReward = func(chain consensus.ChainReader, state *state.StateDB, header *types.Header) error { - client, err := eth.GetClient() + client, err := eth.blockchain.GetClient() if err != nil { log.Error("Fail to connect IPC client for blockSigner", "error", err) } @@ -534,18 +535,3 @@ func (s *Ethereum) Stop() error { return nil } - -// Get current IPC Client. -func (s *Ethereum) GetClient() (*ethclient.Client, error) { - if s.Client == nil { - // Inject ipc client global instance. - client, err := ethclient.Dial(s.IPCEndpoint) - if err != nil { - log.Error("Fail to connect RPC", "error", err) - return nil, err - } - s.Client = client - } - - return s.Client, nil -} diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 6525aa212c..92267fc3e4 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -30,7 +30,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/contracts" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -507,7 +509,7 @@ func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Add func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) { block, err := s.b.BlockByNumber(ctx, blockNr) if block != nil { - response, err := s.rpcOutputBlock(block, true, fullTx) + response, err := s.rpcOutputBlock(block, true, fullTx, ctx) if err == nil && blockNr == rpc.PendingBlockNumber { // Pending blocks need to nil out a few fields for _, field := range []string{"hash", "nonce", "miner"} { @@ -524,7 +526,7 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc. func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) { block, err := s.b.GetBlock(ctx, blockHash) if block != nil { - return s.rpcOutputBlock(block, true, fullTx) + return s.rpcOutputBlock(block, true, fullTx, ctx) } return nil, err } @@ -540,7 +542,7 @@ func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, return nil, nil } block = types.NewBlockWithHeader(uncles[index]) - return s.rpcOutputBlock(block, false, false) + return s.rpcOutputBlock(block, false, false, ctx) } return nil, err } @@ -556,7 +558,7 @@ func (s *PublicBlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, b return nil, nil } block = types.NewBlockWithHeader(uncles[index]) - return s.rpcOutputBlock(block, false, false) + return s.rpcOutputBlock(block, false, false, ctx) } return nil, err } @@ -793,7 +795,7 @@ func FormatLogs(logs []vm.StructLog) []StructLogRes { // rpcOutputBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are // returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain // transaction hashes. -func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { +func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool, ctx context.Context) (map[string]interface{}, error) { head := b.Header() // copies the header once fields := map[string]interface{}{ "number": (*hexutil.Big)(head.Number), @@ -845,6 +847,45 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx } fields["uncles"] = uncleHashes + // Get signers for block. + client, err := s.b.GetIPCClient() + if err != nil { + log.Error("Fail to connect IPC client for block status", "error", err) + } + var signers []common.Address + var filterSigners []common.Address + finality := false + if b.Number().Int64() > 0 { + addrBlockSigner := common.HexToAddress(common.BlockSigners) + signers, err = contracts.GetSignersFromContract(addrBlockSigner, client, b.Hash()) + if err != nil { + log.Error("Fail to get signers from block signer SC.", "error", err) + } + // Get block epoc latest. + if s.b.ChainConfig().Clique != nil { + engine := s.b.GetEngine() + lastCheckpointNumber := rpc.BlockNumber(b.Number().Uint64() - (b.Number().Uint64() % s.b.ChainConfig().Clique.Epoch)) + prevCheckpointBlock, _ := s.b.BlockByNumber(ctx, lastCheckpointNumber) + if prevCheckpointBlock != nil { + masternodes := engine.(*clique.Clique).GetMasternodesFromCheckpointHeader(prevCheckpointBlock.Header()) + countFinality := 0 + for _, masternode := range masternodes { + for _, signer := range signers { + if signer == masternode { + countFinality++ + filterSigners = append(filterSigners, masternode) + } + } + } + if countFinality >= len(masternodes)*75/100 { + finality = true + } + } + } + } + fields["signers"] = filterSigners + fields["finality"] = finality + return fields, nil } diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index af95d7906f..deb6d89a3c 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -23,11 +23,13 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" @@ -69,6 +71,8 @@ type Backend interface { ChainConfig() *params.ChainConfig CurrentBlock() *types.Block + GetIPCClient() (*ethclient.Client, error) + GetEngine() consensus.Engine } func GetAPIs(apiBackend Backend) []rpc.API { diff --git a/les/api_backend.go b/les/api_backend.go index 3fc5c33a44..35050ee929 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" + "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/state" @@ -30,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/gasprice" + "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/light" @@ -190,3 +192,11 @@ func (b *LesApiBackend) ServiceFilter(ctx context.Context, session *bloombits.Ma go session.Multiplex(bloomRetrievalBatch, bloomRetrievalWait, b.eth.bloomRequests) } } + +func (b *LesApiBackend) GetIPCClient() (*ethclient.Client, error) { + return nil, nil +} + +func (b *LesApiBackend) GetEngine() consensus.Engine { + return b.eth.engine +}