Newer
Older
"bytes"
"encoding/hex"
"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"
)
type Node rpc.Client
func (bigInt *BigInt) MarshalJSON() ([]byte, error) {
return (*big.Int)(bigInt).MarshalJSON()
func (bigInt *BigInt) UnmarshalJSON(b []byte) error {
if i, ok := new(big.Int).SetString(string(b[1:len(b)-1]), 0); ok {
*bigInt = BigInt(*i)
return nil
}
return errors.New("can't unmarshal BigInt")
}
func (bigInt *BigInt) String() string {
return (*big.Int)(bigInt).String()
}
type Uint64 uint64
func (u *Uint64) UnmarshalJSON(b []byte) (err error) {
i, err := strconv.ParseUint(string(b[1:len(b)-1]), 0, 64)
*u = Uint64(i)
return
type Bytes []byte
func (b Bytes) MarshalJSON() ([]byte, error) {
dest := make([]byte, 2+hex.EncodedLen(len(b)))
copy(dest, []byte("\"0x"))
hex.Encode(dest[2:], b)
dest[len(dest)-1] = '"'
return dest, nil
}
func (b *Bytes) UnmarshalJSON(src []byte) (err error) {
Require(len(src) >= 4 && bytes.Equal(src[1:3], []byte("0x")), "invalid json string")
dest := make([]byte, hex.DecodedLen(len(src)-4))
_, err = hex.Decode(dest, src[3:len(src)-1])
if err == nil {
*b = dest
}
return
}
// RPCTransaction represents a transaction
BlockHash common.Hash `json:"blockHash"`
BlockNumber Uint64 `json:"blockNumber"`
From common.Address `json:"from"`
Gas Uint64 `json:"gas"`
GasPrice *BigInt `json:"gasPrice"`
Hash common.Hash `json:"hash"`
Input string `json:"input"`
Nonce Uint64 `json:"nonce"`
To common.Address `json:"to"`
TransactionIndex Uint64 `json:"transactionIndex"`
Value *BigInt `json:"value"`
V *BigInt `json:"v"`
R *BigInt `json:"r"`
S *BigInt `json:"s"`
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
type Header struct {
ParentHash common.Hash `json:"parentHash"`
UncleHash common.Hash `json:"sha3Uncles"`
Coinbase common.Address `json:"miner"`
Root common.Hash `json:"stateRoot"`
TxHash common.Hash `json:"transactionsRoot"`
ReceiptHash common.Hash `json:"receiptsRoot"`
Bloom types.Bloom `json:"logsBloom"`
Difficulty *BigInt `json:"difficulty"`
Number *BigInt `json:"number"`
GasLimit Uint64 `json:"gasLimit"`
GasUsed Uint64 `json:"gasUsed"`
Time *BigInt `json:"timestamp"`
Extra Bytes `json:"extraData"`
MixDigest common.Hash `json:"mixHash"`
Nonce types.BlockNonce `json:"nonce"`
}
func (header *Header) ParentHsh() common.Hash {
return header.ParentHash
}
func (header *Header) UncleHsh() common.Hash {
return header.UncleHash
}
func (header *Header) RootHsh() common.Hash {
return header.Root
}
func (header *Header) TxHsh() common.Hash {
return header.TxHash
}
func (header *Header) ReceiptHsh() common.Hash {
return header.ReceiptHash
}
func (header *Header) MixDigestHsh() common.Hash {
return header.MixDigest
}
func (header *Header) LogsBloom() types.Bloom {
return header.Bloom
}
func (header *Header) BlockNonce() types.BlockNonce {
return header.Nonce
}
func (header *Header) BlockDifficulty() *big.Int {
return (*big.Int)(header.Difficulty)
}
func (header *Header) BlockNumber() *big.Int {
return (*big.Int)(header.Number)
}
func (header *Header) Timestamp() *big.Int {
return (*big.Int)(header.Time)
}
func (header *Header) MaxGas() uint64 {
return uint64(header.GasLimit)
}
func (header *Header) Gas() uint64 {
return uint64(header.GasUsed)
}
func (header *Header) ExtraWithoutSignature() []byte {
return header.Extra[:len(header.Extra)-65]
}
func (header *Header) Signature() []byte {
return header.Extra[len(header.Extra)-65:]
}
func (header *Header) Miner() common.Address {
return header.Coinbase
}
type Block struct {
Transactions []RPCTransaction `json:"transactions"`
TotalDifficulty Uint64 `json:"totalDifficulty"`
Signer common.Address `json:"signer"`
func (block *Block) GetHeader() (header types.Header) {
header.ParentHash = block.ParentHash
header.UncleHash = block.UncleHash
header.Coinbase = block.Coinbase
header.Root = block.Root
header.TxHash = block.TxHash
header.ReceiptHash = block.TxHash
header.Bloom = block.Bloom
header.Difficulty = (*big.Int)(block.Difficulty)
header.Number = (*big.Int)(block.Number)
header.GasLimit = uint64(block.GasLimit)
header.GasUsed = uint64(block.GasUsed)
header.Time = (*big.Int)(block.Time)
header.Extra = block.Extra
header.MixDigest = block.MixDigest
header.Nonce = block.Nonce
return
}
func (block *Block) setSigner() {
block.Signer = GetSigner(block)
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 string, blockNumber int64) *big.Int {
var balance BigInt
node.Call(&balance, "eth_getBalance", account, hexBlockNumber(blockNumber))
return (*big.Int)(&balance)
}
func (node *Node) Balance(account string) *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), true)
return
}
func (node *Node) BlockByNumber(blockNumber int64) (block Block) {
node.Call(&block, "eth_getBlockByNumber", hexBlockNumber(blockNumber), true)
block.setSigner()
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)
return
}
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(address, blockNumber),
}
}
notSeen := int64(len(status))
block := node.HeaderByNumber(blockNumber)
blockNumber = block.BlockNumber().Int64()
signer := BytesToHex(GetSigner(&block).Bytes())
if status[signer].LastBlock == 0 {
status[signer].LastBlock = block.BlockNumber().Int64()
status[signer].Time = block.Timestamp().Uint64()
status[signer].Difficulty = block.BlockDifficulty().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()
}