Newer
Older
package bfa
import (
. "../util"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rpc"
)
type Node rpc.Client
type Snapshot struct {
Number uint64 `json:"number"` // Block number where the snapshot was created
Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
Signers map[common.Address]struct{} `json:"signers"` // Set of authorized signers at this moment
Recents map[uint64]common.Address `json:"recents"` // Set of recent signers for spam protections
Votes []*clique.Vote `json:"votes"` // List of votes cast in chronological order
Tally map[common.Address]clique.Tally `json:"tally"` // Current vote tally to avoid recalculating
}
type Tally struct {
True int `json:"true"`
False int `json:"false"`
Null int `json:"null"`
}
type Proposals struct {
BlockNumber int64 `json:"number"` // Block number where the snapshot was created
Proposals []string `json:"proposals"` // List of proposals being voted
Signers []string `json:"signers"` // List of authorized signers at this moment
Tally map[string]*Tally `json:"tally"` // Count of positive, negative and empty votes for a proposal
Votes map[string]map[string]*bool `json:"votes"` // List of votes for each proposal
}
type SealerStatus struct {
LastBlock int64 `json:"lastBlockSigned"`
Time uint64 `json:"timestamp"`
Difficulty uint64 `json:"difficulty"`
Balance *big.Int `json:"balance"`
Latest = "latest"
Self = "self"
Network = 47525974938
Genesis = "0xe0e26f415af00fe32e1e8d94336355db43a78d9f59eb70910172b61fb670d99e"
func (node *Node) Call(result interface{}, method string, args ...interface{}) {
Check((*rpc.Client)(node).Call(result, method, args...))
}
func (node *Node) BlockNumber() int64 {
var bn rpc.BlockNumber
node.Call(&bn, "eth_blockNumber")
return bn.Int64()
}
func (node *Node) Accounts() (accounts []string) {
node.Call(&accounts, "eth_accounts")
return
}
func (node *Node) IsMining() (mining bool) {
node.Call(&mining, "eth_mining")
return
}
func (node *Node) PeerCount() uint64 {
var peerCount Uint64
node.Call(&peerCount, "net_peerCount")
func (node *Node) BalanceAtBlock(account common.Address, blockNumber int64) *big.Int {
var balance BigInt
node.Call(&balance, "eth_getBalance", account, hexBlockNumber(blockNumber))
return (*big.Int)(&balance)
func (node *Node) Balance(account common.Address) *big.Int {
return node.BalanceAtBlock(account, -1)
}
func (node *Node) Coinbase() (coinbase string, err error) {
switch s := e.(type) {
case string:
err = fmt.Errorf(s)
case error:
err = s
default:
err = fmt.Errorf("unknown error while getting coinbase: %v", e)
}
}
}()
var address common.Address
node.Call(&address, "eth_coinbase")
coinbase = BytesToHex(address[:])
return
}
func hexBlockNumber(number int64) (blockNumber string) {
if number == -1 {
blockNumber = Latest
blockNumber = "0x" + strconv.FormatInt(number, 16)
func (node *Node) BlockNumberInRange(number int64) (blockNumber int64) {
latest := node.BlockNumber()
switch {
case number == -1, number > latest:
blockNumber = latest
case number < 0:
blockNumber = Max(0, latest+1+number)
default:
blockNumber = number
return
}
func (node *Node) HeaderByNumber(blockNumber int64) (header Header) {
node.Call(&header, "eth_getBlockByNumber", hexBlockNumber(blockNumber), false)
return
}
func (node *Node) HeaderByHash(hash common.Hash) (header Header) {
node.Call(&header, "eth_getBlockByHash", hash, false)
return
}
func (node *Node) BlockByNumber(blockNumber int64) (block Block) {
node.Call(&block, "eth_getBlockByNumber", hexBlockNumber(blockNumber), true)
block.Signer = block.GetSigner()
block.Hash = block.GetHash()
return
}
func (node *Node) BlockByHash(hash common.Hash) (block Block) {
node.Call(&block, "eth_getBlockByHash", hash, true)
block.Signer = block.GetSigner()
block.Hash = block.GetHash()
func (node *Node) BlockSigner(blockNumber int64) (signer common.Address) {
if blockNumber == 0 { // we return an empty signer for genesis block
return
}
header := node.HeaderByNumber(blockNumber)
}
func (node *Node) GetSnapshot() (snapshot Snapshot) {
node.Call(&snapshot, "clique_getSnapshot", nil)
return
}
func (node *Node) SnapshotAtBlock(blockNumber int64) (snapshot Snapshot) {
node.Call(&snapshot, "clique_getSnapshot", hexBlockNumber(blockNumber))
func (node *Node) Sealers() (signers []string) {
node.Call(&signers, "clique_getSigners", nil)
func (node *Node) NodeInfo() (nodeInfo p2p.NodeInfo) {
node.Call(&nodeInfo, "admin_nodeInfo", nil)
return
}
func (node *Node) SealersAtBlock(blockNumber int64) (signers []string) {
return node.Sealers()
node.Call(&signers, "clique_getSigners", Int64ToHex(blockNumber))
func (node *Node) IsSealer(address string) bool {
if address == Self {
var err error
if address, err = node.Coinbase(); err != nil {
return false
}
}
return Contains(node.Sealers(), address)
func (node *Node) IsSealerAtBlock(address string, blockNumber int64) bool {
if address == Self {
var err error
if address, err = node.Coinbase(); err != nil {
return false
}
}
return Contains(node.SealersAtBlock(blockNumber), address)
}
func (node *Node) Propose(address string, vote bool) {
node.Call(nil, "clique_propose", address, vote)
return
}
func (node *Node) Votes(blockNumber int64) (votes Proposals) {
var (
snapshot Snapshot
)
if blockNumber < 0 {
snapshot = node.GetSnapshot()
} else {
snapshot = node.SnapshotAtBlock(blockNumber)
}
votes.BlockNumber = int64(snapshot.Number)
for signer := range snapshot.Signers {
votes.Signers = append(votes.Signers, BytesToHex(signer[:]))
sort.Strings(votes.Signers)
}
for proposal := range snapshot.Tally {
votes.Proposals = append(votes.Proposals, BytesToHex(proposal[:]))
}
votes.Votes = make(map[string]map[string]*bool)
votes.Tally = make(map[string]*Tally)
for _, v := range snapshot.Votes {
proposal := BytesToHex(v.Address[:])
signer := BytesToHex(v.Signer[:])
if votes.Votes[proposal] == nil {
votes.Votes[proposal] = make(map[string]*bool)
for _, signer := range votes.Signers {
votes.Votes[proposal][signer] = nil
}
votes.Tally[proposal] = &Tally{0, 0, len(votes.Signers)}
}
votes.Votes[proposal][signer] = &v.Authorize
if v.Authorize {
votes.Tally[proposal].True += 1
} else {
votes.Tally[proposal].False += 1
}
votes.Tally[proposal].Null -= 1
}
return
}
func (node *Node) SealersStatus(blockNumber int64) (status map[string]*SealerStatus) {
if blockNumber == 0 { // Genesis block doesn't have signer
return
}
status = make(map[string]*SealerStatus)
for _, address := range node.SealersAtBlock(blockNumber) {
status[address] = &SealerStatus{
Balance: node.BalanceAtBlock(common.HexToAddress(address), blockNumber),
}
notSeen := int64(len(status))
block := node.HeaderByNumber(blockNumber)
signer := BytesToHex(block.GetSigner().Bytes())
if status[signer].LastBlock == 0 {
status[signer].LastBlock = block.Number.Int64()
status[signer].Time = block.Time.Uint64()
status[signer].Difficulty = block.Difficulty.Uint64()
notSeen--
}
if blockNumber == until {
break
}
blockNumber--
block = node.HeaderByNumber(blockNumber)
}
return status
}
client, err := rpc.Dial(url)
}
func (node *Node) Close() {
(*rpc.Client)(node).Close()
}