aboutsummaryrefslogtreecommitdiffstats
path: root/monitor/backend.go
diff options
context:
space:
mode:
Diffstat (limited to 'monitor/backend.go')
-rw-r--r--monitor/backend.go185
1 files changed, 185 insertions, 0 deletions
diff --git a/monitor/backend.go b/monitor/backend.go
new file mode 100644
index 0000000..194ed84
--- /dev/null
+++ b/monitor/backend.go
@@ -0,0 +1,185 @@
+package monitor
+
+import (
+ "context"
+ "fmt"
+ "log"
+ "math/big"
+ "strings"
+ "time"
+
+ ethereum "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/accounts/abi"
+ "github.com/ethereum/go-ethereum/accounts/abi/bind"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethclient"
+)
+
+type backendIntf interface {
+ FetchFinedNodes(chan node)
+ NodeSet() []node
+ BalanceFromAddress(*ethclient.Client, common.Address) *big.Int
+}
+
+// BlockchainBackend is the blockchain backend object for fetching blockchain info.
+type BlockchainBackend struct {
+ networkID int
+}
+
+// NodeSet returns the registered nodes.
+func (b *BlockchainBackend) NodeSet() []node {
+ nc := NetworkConfigMap[int64(b.networkID)]
+ conn, err := ethclient.Dial(nc.HTTPEndpoint)
+ if err != nil {
+ log.Println("EthClient Dial failed: ", err)
+ return []node{}
+ }
+ g, err := NewGovernance(GovAddress, conn)
+ if err != nil {
+ panic(err)
+ }
+ txOps := &bind.CallOpts{}
+ nodeLen, err := g.NodesLength(txOps)
+ if err != nil {
+ panic(err)
+ }
+ minStake, err := g.MinStake(txOps)
+ if err != nil {
+ panic(err)
+ }
+ nodes := []node{}
+ for i := uint64(0); i < nodeLen.Uint64(); i++ {
+ n, err := g.Nodes(txOps, big.NewInt(int64(i)))
+ if n.Staked.Cmp(minStake) < 0 {
+ continue
+ }
+ if err != nil {
+ panic(err)
+ }
+ publicKey, err := crypto.UnmarshalPubkey(n.PublicKey)
+ if err != nil {
+ log.Println("Unmarshal publicKey fail: ", err)
+ continue
+ }
+ address := crypto.PubkeyToAddress(*publicKey)
+ nodes = append(nodes, node{
+ owner: n.Owner,
+ email: n.Email,
+ fined: n.Fined,
+ name: n.Name,
+ publicKey: n.PublicKey,
+ nodeKeyAddress: address,
+ })
+ }
+ return nodes
+}
+
+// BalanceFromAddress return address's balance.
+func (b *BlockchainBackend) BalanceFromAddress(conn *ethclient.Client,
+ address common.Address) *big.Int {
+ balance, err := conn.BalanceAt(context.Background(),
+ address, nil)
+ if err != nil {
+ log.Println("Get balance fail: ", err)
+ return big.NewInt(0)
+ }
+ return balance
+}
+
+func (b *BlockchainBackend) subscribe(
+ logs chan types.Log,
+ headers chan *types.Header,
+) (ethereum.Subscription, *ethclient.Client, error) {
+
+ nc := NetworkConfigMap[int64(b.networkID)]
+ conn, err := ethclient.Dial(nc.WSEndpoint)
+ if err != nil {
+ log.Println("EthClient Dial failed: ", err)
+ return nil, conn, err
+ }
+ abiObject, err := abi.JSON(strings.NewReader(GovernanceABI))
+ query := ethereum.FilterQuery{
+ Addresses: []common.Address{GovAddress},
+ Topics: [][]common.Hash{
+ []common.Hash{
+ abiObject.Events["Fined"].Id(),
+ },
+ },
+ }
+ sub, err := conn.SubscribeFilterLogs(context.Background(), query, logs)
+ if err != nil {
+ log.Println("Subscribe logs failed: ", err)
+ return nil, conn, err
+ }
+
+ // Subscribe chain head to keep ws from timeout
+ _, err = conn.SubscribeNewHead(context.Background(), headers)
+ if err != nil {
+ log.Println("Subscribe chain head failed: ", err)
+ }
+ return sub, conn, nil
+}
+
+// FetchFinedNodes fetches nodes' information from blockchain.
+func (b *BlockchainBackend) FetchFinedNodes(nodeChan chan node) {
+ logs := make(chan types.Log)
+ headers := make(chan *types.Header)
+ sub, conn, err := b.subscribe(logs, headers)
+ if err != nil {
+ panic(err)
+ }
+ for {
+ select {
+ case err := <-sub.Err():
+ log.Println("sub.Err()", err)
+ // retry
+ for err != nil {
+ sub, conn, err = b.subscribe(logs, headers)
+ time.Sleep(30000)
+ }
+ case vLog := <-logs:
+ nodeAddress := common.BytesToAddress(vLog.Topics[1].Bytes())
+ log.Println("Notify node: ", nodeAddress.Hex())
+ nodeChan <- b.getNodeByAddress(nodeAddress, conn)
+ }
+ }
+}
+
+func (b *BlockchainBackend) getNodeByAddress(
+ nodeAddress common.Address, conn *ethclient.Client) node {
+
+ g, err := NewGovernance(GovAddress, conn)
+ if err != nil {
+ panic(err)
+ }
+ txOps := &bind.CallOpts{}
+ offset, err := g.NodesOffsetByAddress(txOps, nodeAddress)
+ if err != nil {
+ panic(err)
+ }
+ n, err := g.Nodes(txOps, offset)
+ if err != nil {
+ panic(err)
+ }
+ return node{
+ owner: n.Owner,
+ email: n.Email,
+ fined: n.Fined,
+ name: n.Name,
+ }
+}
+
+func printNodes(nodes []node) {
+ for i := range nodes {
+ fmt.Println("Owner", nodes[i].owner.Hex())
+ fmt.Println("Email", nodes[i].email)
+ fmt.Println("Fined", nodes[i].fined.Uint64())
+ }
+}
+
+// NewBlockchainBackend is the blockchain backend constructor.
+func NewBlockchainBackend(networkID int) *BlockchainBackend {
+ return &BlockchainBackend{networkID: networkID}
+}