package bfa import ( . "../clique" . "../util" "encoding/json" "fmt" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rpc" "math/big" "reflect" "sort" "strconv" ) type Node rpc.Client type BigInt big.Int func (b BigInt) MarshalJSON() ([]byte, error) { i := (big.Int)(b) return []byte(i.String()), nil } func (b *BigInt) String() string { return (*big.Int)(b).String() } // RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction type RPCTransaction struct { BlockHash string `json:"blockHash"` BlockNumber int64 `json:"blockNumber"` From string `json:"from"` Gas uint64 `json:"gas"` GasPrice uint64 `json:"gasPrice"` Hash string `json:"hash"` Input string `json:"input"` Nonce uint64 `json:"nonce"` To string `json:"to"` TransactionIndex uint `json:"transactionIndex"` Value string `json:"value"` V *BigInt `json:"v"` R *BigInt `json:"r"` S *BigInt `json:"s"` } type Block struct { ParentHash string `json:"parentHash"` Coinbase string `json:"miner"` Root string `json:"stateRoot"` TxHash string `json:"transactionsRoot"` ReceiptHash string `json:"receiptsRoot"` Bloom string `json:"logsBloom"` Difficulty uint64 `json:"difficulty"` Number uint64 `json:"number"` GasLimit uint64 `json:"gasLimit"` GasUsed uint64 `json:"gasUsed"` Time uint64 `json:"timestamp"` Extra string `json:"extraData"` MixDigest string `json:"mixHash"` Nonce uint64 `json:"nonce"` Transactions []RPCTransaction `json:"transactions"` TotalDifficulty uint64 `json:"totalDifficulty"` Signer string `json:"signer"` } func mapToStruct(m map[string]interface{}, s interface{}) { v := reflect.ValueOf(s).Elem() numFields := v.NumField() for i := 0; i < numFields; i++ { field := v.Field(i) jsonName := v.Type().Field(i).Tag.Get("json") if value, ok := m[jsonName]; ok { switch x := value.(type) { case string: switch field.Kind() { case reflect.String: field.SetString(x) case reflect.Uint64, reflect.Uint: if uintValue, err := strconv.ParseUint(x, 0, 64); err == nil { field.SetUint(uintValue) } case reflect.Int64, reflect.Int: if intValue, err := strconv.ParseInt(x, 0, 64); err == nil { field.SetInt(intValue) } } } } } } func (block *Block) UnmarshalJSON(b []byte) error { var m map[string]interface{} if err := json.Unmarshal(b, &m); err != nil { return err } mapToStruct(m, block) //v := reflect.ValueOf(block).Elem() //numFields := v.NumField() //for i := 0; i < numFields; i++ { // field := v.Field(i) // jsonName := v.Type().Field(i).Tag.Get("json") // if value, ok := m[jsonName]; ok { // switch x := value.(type) { // case string: // switch field.Kind() { // case reflect.String: // field.SetString(x) // case reflect.Uint64: // if uintValue, err := strconv.ParseUint(x, 0, 64); err == nil { // field.SetUint(uintValue) // } // } // } // } //} for _, x := range m["transactions"].([]interface{}) { t := new(RPCTransaction) m := x.(map[string]interface{}) mapToStruct(m, t) v, _ := new(big.Int).SetString(m["v"].(string), 0) r, _ := new(big.Int).SetString(m["r"].(string), 0) s, _ := new(big.Int).SetString(m["s"].(string), 0) t.V = (*BigInt)(v) t.R = (*BigInt)(r) t.S = (*BigInt)(s) fmt.Println(t.R, t.S, t.V) block.Transactions = append(block.Transactions, *t) } return nil } 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 SealerInfo struct { Address string CurrentBlock int64 Since int64 FirstBlock int64 LastBlock int64 } type SealerStatus struct { LastBlock int64 `json:"lastBlockSigned"` Time uint64 `json:"timestamp"` Difficulty uint64 `json:"difficulty"` Balance *big.Int `json:"balance"` } const ( 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() int64 { var peerCount string node.Call(&peerCount, "net_peerCount") p, _ := strconv.ParseInt(peerCount, 0, 64) return p } func (node *Node) BalanceAtBlock(account string, blockNumber int64) *big.Int { var ( balance string block = Latest ) if blockNumber >= 0 { block = Int64ToHex(blockNumber) } node.Call(&balance, "eth_getBalance", account, block) n, _ := new(big.Int).SetString(balance, 0) return n } func (node *Node) Balance(account string) *big.Int { return node.BalanceAtBlock(account, -1) } func (node *Node) Coinbase() (coinbase string, err error) { defer func() { if e := recover(); e != nil { 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 (node *Node) HeaderByNumber(blockNumber int64) types.Header { var ( number string resp types.Header ) if blockNumber < 0 { number = Latest } else { number = fmt.Sprintf("0x%x", blockNumber) } node.Call(&resp, "eth_getBlockByNumber", number, true) return resp } func (node *Node) BlockByNumber(blockNumber int64) (block Block) { var ( number string ) if blockNumber < 0 { number = Latest } else { number = fmt.Sprintf("0x%x", blockNumber) } node.Call(&block, "eth_getBlockByNumber", number, true) return } //func (node *Node) BlockxByNumber(blockNumber int64) (block Block) { // ethClient := ethclient.NewClient((*rpc.Client)(node)) // b, err := ethClient.BlockByNumber(context.Background(), big.NewInt(blockNumber)) // h := b.Header() // Check(err) // block.ParentHash = h.ParentHash // block.Coinbase = h.Coinbase // block.Root = h.Root // block.TxHash = h.TxHash // block.ReceiptHash = h.ReceiptHash // block.Bloom = h.Bloom // block.Difficulty = h.Difficulty // block.Number = h.Number // block.GasLimit = h.GasLimit // block.GasUsed = h.GasUsed // block.Time = h.Time // block.Extra = h.Extra // block.MixDigest = h.MixDigest // block.Nonce = h.Nonce // block.Transactions = b.Transactions() // block.Signer, err = GetSigner(h) // block.TotalDifficulty = b.DeprecatedTd() // return block //} func (node *Node) BlockSigner(blockNumber int64) (signer string) { header := node.HeaderByNumber(blockNumber) signer, err := GetSigner(&header) Check(err) return } func (node *Node) GetSnapshot() (snapshot Snapshot) { node.Call(&snapshot, "clique_getSnapshot", nil) return } func (node *Node) SnapshotAtHash(hash common.Hash) (snapshot Snapshot) { node.Call(&snapshot, "clique_getSnapshotAtHash", hash) return } func (node *Node) SnapshotAtBlock(blockNumber int64) (snapshot Snapshot) { if blockNumber < 0 { return node.GetSnapshot() } node.Call(&snapshot, "clique_getSnapshot", Int64ToHex(blockNumber)) return } func (node *Node) Sealers() (signers []string) { var s []common.Address node.Call(&s, "clique_getSigners", nil) for _, signer := range s { signers = append(signers, BytesToHex(signer.Bytes())) } return } func (node *Node) NodeInfo() (nodeInfo p2p.NodeInfo) { node.Call(&nodeInfo, "admin_nodeInfo", nil) return } func (node *Node) SealersAtHash(hash common.Hash) (signers []string) { var s []common.Address node.Call(&signers, "clique_getSignersAtHash", hash) for _, signer := range s { signers = append(signers, BytesToHex(signer.Bytes())) } return } func (node *Node) SealersAtBlock(blockNumber int64) (signers []string) { var s []common.Address if blockNumber < 0 { return node.Sealers() } node.Call(&s, "clique_getSigners", Int64ToHex(blockNumber)) for _, signer := range s { signers = append(signers, BytesToHex(signer.Bytes())) } return } 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) 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(address, blockNumber), } } notSeen := int64(len(status)) block := node.HeaderByNumber(blockNumber) blockNumber = block.Number.Int64() until := Max(1, blockNumber-5*notSeen) for notSeen > 0 { signer, _ := GetSigner(&block) 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 } func (node *Node) SealerInception(address string) (since int64) { if signers := node.Sealers(); !Contains(signers, address) { return -1 } lo := int64(0) hi := node.BlockNumber() for lo < hi { mid := lo + (hi-lo)/2 signers := node.SealersAtBlock(mid) if Contains(signers, address) { hi = mid } else { lo = mid + 1 } } return hi } func (node *Node) signerFirstBlock(signer string, since int64, until int64) (blockNumber int64) { if since < 0 { return -1 } //snapshot := node.SnapshotAtBlock(since) var ( count int64 = 20 found int64 = -1 ) // first, we look close to the inception if blockNumber, _ = node.searchSnapshotForward(since, signer, count); blockNumber > 0 { return } // next, we do a coarse search since += count for i := since + 10000; i < until && found == -1; i += 10000 { if found, _ = node.searchSnapshotBackward(i, signer, 0); found == -1 { // still not found since = i } } if found > 0 { until = found } n := Min(10, (until-since)/count+1) // number of goroutines ch := make(chan int64) for i := int64(0); i < n; i++ { go func() { for blockNumber := <-ch; blockNumber > 0; blockNumber = <-ch { cnt := Min(count, until-blockNumber+1) if found, _ := node.searchSnapshotForward(blockNumber, signer, cnt); found > 0 { ch <- found } } ch <- -1 }() } for { select { case ch <- since: switch { case since < 0: continue case since+count < until: since += count default: since = -1 //we have exhausted the search space } case found := <-ch: switch { case found < 0: n-- // a goroutine has ended if n == 0 { // all goroutines have ended return } case blockNumber < 0: blockNumber = found // first time we see this signer since = -1 // Notify everyone case found < blockNumber: blockNumber = found // found an earlier block } } } } func (node *Node) signerLastBlock(signer string, since int64, until int64) (blockNumber int64) { if since < 0 { return -1 } var ( count int64 = 20 visited int64 = 0 found int64 = -1 ) // first, we look close to the last block if blockNumber, visited = node.searchSnapshotBackward(until, signer, 0); blockNumber > 0 { return } // next, we do a coarse search until -= visited for i := until - 10000; i > since && found == -1; i -= 10000 { if found, _ = node.searchSnapshotBackward(i, signer, 0); found == -1 { // still not found until = i } } if found > 0 { since = found } n := Min(10, (until-since)/count+1) // number of goroutines ch := make(chan int64) for i := int64(0); i < n; i++ { go func() { for blockNumber := <-ch; blockNumber > 0; blockNumber = <-ch { cnt := Min(count, blockNumber-since+1) if found, _ := node.searchSnapshotBackward(blockNumber, signer, cnt); found > 0 { ch <- found } } ch <- -1 }() } for { select { case ch <- until: switch { case until < 0: continue case until-count > since: until -= count default: until = -1 //we have exhausted the search space } case found := <-ch: switch { case found < 0: n-- // a goroutine has ended if n == 0 { // all goroutines have ended return } case blockNumber < 0: blockNumber = found // first time we see this signer until = -1 // Notify everyone case found > blockNumber: blockNumber = found // found a later block } } } } func (node *Node) searchSnapshotForward(blockNumber int64, signer string, count int64) (found int64, visited int64) { found = -1 if blockNumber+count < 1 { return } for count > 0 { snapshot := node.SnapshotAtBlock(blockNumber + count - 1) recents := int64(len(snapshot.Recents)) visited += recents count -= recents for b, s := range snapshot.Recents { if BytesToHex(s[:]) == signer { found = int64(b) } } } return } func (node *Node) searchSnapshotBackward(blockNumber int64, signer string, count int64) (found int64, visited int64) { found = -1 if blockNumber < 1 { // Genesis block has no signers return } count = Min(count, blockNumber) // Never search below block 1 for { snapshot := node.SnapshotAtBlock(blockNumber - visited) visited += int64(len(snapshot.Recents)) if count == 0 { count = Min(blockNumber, int64(2*len(snapshot.Signers))) } for b, s := range snapshot.Recents { if BytesToHex(s[:]) == signer { found = int64(b) return } } if visited >= count { return } } } func (node *Node) SealerInfo(sealer string) (info SealerInfo, err error) { info.Address = sealer info.CurrentBlock = node.BlockNumber() info.Since = node.SealerInception(sealer) if info.Since == -1 { return info, fmt.Errorf("%q is not a sealer", sealer) } info.FirstBlock = node.signerFirstBlock(sealer, info.Since+1, info.CurrentBlock) info.LastBlock = node.signerLastBlock(sealer, info.FirstBlock, info.CurrentBlock) return } func Dial(url string) (*Node, error) { client, err := rpc.Dial(url) return (*Node)(client), err } func (node *Node) Close() { (*rpc.Client)(node).Close() }