diff options
Diffstat (limited to 'monitor/backend.go')
-rw-r--r-- | monitor/backend.go | 185 |
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} +} |