diff --git a/bfa_client/src/bfa/node.go b/bfa_client/src/bfa/node.go index 4eba41587d817c3c1c5186601bb897e22c185795..264594fbbb990ec769b02a55f6d67e116dbb6682 100644 --- a/bfa_client/src/bfa/node.go +++ b/bfa_client/src/bfa/node.go @@ -3,6 +3,8 @@ package bfa import ( . "../clique" . "../util" + "bytes" + "encoding/hex" "errors" "fmt" "github.com/ethereum/go-ethereum/common" @@ -20,8 +22,7 @@ type Node rpc.Client type BigInt big.Int func (bigInt *BigInt) MarshalJSON() ([]byte, error) { - i := (*big.Int)(bigInt) - return []byte(i.String()), nil + return (*big.Int)(bigInt).MarshalJSON() } func (bigInt *BigInt) UnmarshalJSON(b []byte) error { @@ -39,16 +40,32 @@ func (bigInt *BigInt) String() string { type Uint64 uint64 -func (u *Uint64) MarshalJSON() ([]byte, error) { - return []byte(strconv.FormatUint(*(*uint64)(u), 10)), nil -} - 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 type RPCTransaction struct { BlockHash common.Hash `json:"blockHash"` @@ -69,24 +86,49 @@ type RPCTransaction struct { type Block 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 Uint64 `json:"difficulty"` - Number Uint64 `json:"number"` + Difficulty *BigInt `json:"difficulty"` + Number *BigInt `json:"number"` GasLimit Uint64 `json:"gasLimit"` GasUsed Uint64 `json:"gasUsed"` - Time Uint64 `json:"timestamp"` - Extra []byte `json:"extraData"` + Time *BigInt `json:"timestamp"` + Extra Bytes `json:"extraData"` MixDigest common.Hash `json:"mixHash"` - Nonce Uint64 `json:"nonce"` + Nonce types.BlockNonce `json:"nonce"` Transactions []RPCTransaction `json:"transactions"` TotalDifficulty Uint64 `json:"totalDifficulty"` Signer common.Address `json:"signer"` } +func (block *Block) Header() (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() { + header := block.Header() + block.Signer = GetSigner(&header) +} + 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 @@ -195,31 +237,38 @@ func (node *Node) Coinbase() (coinbase string, err error) { return } -func (node *Node) HeaderByNumber(blockNumber int64) types.Header { - var ( - number string - resp types.Header - ) - if blockNumber < 0 || blockNumber > node.BlockNumber() { - number = Latest +func hexBlockNumber(number int64) (blockNumber string) { + if number == -1 { + blockNumber = Latest } else { - number = fmt.Sprintf("0x%x", blockNumber) + blockNumber = "0x" + strconv.FormatInt(number, 16) } - node.Call(&resp, "eth_getBlockByNumber", number, true) - return resp + return } -func (node *Node) BlockByNumber(blockNumber int64) (block Block) { - var ( - number string - ) - if blockNumber < 0 || blockNumber > node.BlockNumber() { - number = Latest - } else { - number = fmt.Sprintf("0x%x", blockNumber) +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 } - node.Call(&block, "eth_getBlockByNumber", number, true) - block.Signer = node.BlockSigner(blockNumber) + return +} + +func (node *Node) HeaderByNumber(blockNumber int64) (header types.Header) { + Require(blockNumber == -1 || blockNumber == node.BlockNumberInRange(blockNumber), "block number out of range") + node.Call(&header, "eth_getBlockByNumber", hexBlockNumber(blockNumber), true) + return +} + +func (node *Node) BlockByNumber(blockNumber int64) (block Block) { + Require(blockNumber == -1 || blockNumber == node.BlockNumberInRange(blockNumber), "block number out of range") + node.Call(&block, "eth_getBlockByNumber", hexBlockNumber(blockNumber), true) + block.setSigner() return } @@ -243,10 +292,8 @@ func (node *Node) SnapshotAtHash(hash common.Hash) (snapshot Snapshot) { } func (node *Node) SnapshotAtBlock(blockNumber int64) (snapshot Snapshot) { - if blockNumber < 0 || blockNumber >= node.BlockNumber() { - return node.GetSnapshot() - } - node.Call(&snapshot, "clique_getSnapshot", Int64ToHex(blockNumber)) + Require(blockNumber == -1 || blockNumber == node.BlockNumberInRange(blockNumber), "block number out of range") + node.Call(&snapshot, "clique_getSnapshot", hexBlockNumber(blockNumber)) return } diff --git a/bfa_client/src/client/bfa_client.go b/bfa_client/src/client/bfa_client.go index be426749cb22687f73a065ab5cc29cd04983cecb..926df25fd72bb20525f8e05f11cc4fbdefc9ae60 100644 --- a/bfa_client/src/client/bfa_client.go +++ b/bfa_client/src/client/bfa_client.go @@ -88,6 +88,7 @@ func proposals() { node, err := bfa.Dial(url) util.Check(err) defer node.Close() + blockNumber = node.BlockNumberInRange(blockNumber) votes := node.Votes(blockNumber) if json { util.PrintJson(votes) @@ -163,12 +164,13 @@ func sealers() { flags.StringVar(&format, "format", "", "Formato del timestamp. Ignorado en formato json. Opciones: 'unix', 'rfc3339', 'long' ('YYYY-MM-DD hh:mm:ss'), 'short' ('hh:mm:ss') o un formato especÃfico. Ejemplo 'DD/MM/YY hh.mm.ss'. También se admite el formato del paquete 'time' de go.") parseFlags(false) if blockNumber == 0 { - panic("El bloque génesis no tiene firmantes") + util.Error("El bloque génesis no tiene firmantes") } url = updateURL(url) node, err := bfa.Dial(url) util.Check(err) defer node.Close() + blockNumber = node.BlockNumberInRange(blockNumber) extended := lastBlock || timestamp || difficulty || balance if extended { sealers := node.SealersStatus(blockNumber) @@ -268,12 +270,12 @@ func autovote() { flags.IntVar(&threshold, "threshold", voteThreshold, "Cantidad mÃnima de votos en una propuesta para habilitar el voto automático.") setFlags() parseFlags(false) - util.DieIf(threshold < voteThreshold, "No se puede especificar una cantidad de votos inferior a %v.", voteThreshold) + util.Ensure(threshold >= voteThreshold, "No se puede especificar una cantidad de votos inferior a %v.", voteThreshold) url = updateURL(url) node, err := bfa.Dial(url) util.Check(err) defer node.Close() - util.DieIf(!node.IsSealer(bfa.Self), "Solo los selladores pueden votar") + util.Ensure(node.IsSealer(bfa.Self), "Solo los selladores pueden votar") votes := node.Votes(latest) genesisSigners := node.SealersAtBlock(0) self, err := node.Coinbase() @@ -283,7 +285,7 @@ func autovote() { removedSealers += 1 } } - util.DieIf(len(votes.Signers)-removedSealers < minSigners, "No se puede emitir un voto automático que reduzca la cantidad de selladores por debajo de %v.", minSigners) + util.Ensure(len(votes.Signers)-removedSealers >= minSigners, "No se puede emitir un voto automático que reduzca la cantidad de selladores por debajo de %v.", minSigners) for _, proposal := range votes.Proposals { isSealer := util.Contains(votes.Signers, proposal) switch { @@ -326,25 +328,21 @@ func propose() { node, err := bfa.Dial(url) util.Check(err) defer node.Close() - util.DieIf(!node.IsSealer(bfa.Self), "Solo los selladores pueden votar") + util.Ensure(node.IsSealer(bfa.Self), "Solo los selladores pueden votar") votes := node.Votes(latest) - if flags.NArg() == 0 { - panic("No se especificaron candidatos por los cuales votar") - } + util.Ensure(flags.NArg() > 0, "No se especificaron candidatos por los cuales votar") for i := 0; i < flags.NArg(); i++ { address := flags.Arg(i) - if !util.IsAddress(address) { - panic(fmt.Sprintf("'%v' no es una dirección válida", address)) - } + util.Ensure(util.IsAddress(address), "'%v' no es una dirección válida", address) if _, ok := votes.Tally[address]; ok { continue // address is in a proposal, so we allow voting either way } isSealer := util.Contains(votes.Signers, address) switch { // address is not in a proposal, we allow removing signers or adding non signers case isSealer && authorize: - panic(fmt.Sprintf("'%v' ya es un sellador", address)) + util.Error("'%v' ya es un sellador", address) case !isSealer && !authorize: - panic(fmt.Sprintf("'%v' no es un sellador", address)) + util.Error("'%v' no es un sellador", address) } node.Propose(address, authorize) if json { @@ -411,6 +409,7 @@ func block() { node, err := bfa.Dial(url) util.Check(err) defer node.Close() + blockNumber = node.BlockNumberInRange(blockNumber) block := node.BlockByNumber(blockNumber) util.PrintJson(block) } @@ -425,6 +424,7 @@ func snapshot() { node, err := bfa.Dial(url) util.Check(err) defer node.Close() + blockNumber = node.BlockNumberInRange(blockNumber) snapshot := node.SnapshotAtBlock(blockNumber) util.PrintJson(snapshot) } diff --git a/bfa_client/src/clique/clique.go b/bfa_client/src/clique/clique.go index 79cc23a7d8103ff521e74f9fae7537e642797545..2e91d9b6eeb9576019429625b8a2edd21870db59 100644 --- a/bfa_client/src/clique/clique.go +++ b/bfa_client/src/clique/clique.go @@ -10,8 +10,7 @@ import ( func sigHash(header *types.Header) (hash common.Hash) { hasher := sha3.NewKeccak256() - - rlp.Encode(hasher, []interface{}{ + _ = rlp.Encode(hasher, []interface{}{ header.ParentHash, header.UncleHash, header.Coinbase, diff --git a/bfa_client/src/util/util.go b/bfa_client/src/util/util.go index fab25581370439fab2b9825fc585ecab297b7c91..dfff2b691aca548cf2513f2e1cda7d059787923c 100644 --- a/bfa_client/src/util/util.go +++ b/bfa_client/src/util/util.go @@ -80,7 +80,3 @@ func IsAddress(address string) bool { bytes, err := hex.DecodeString(address[2:]) return err == nil && len(bytes) == common.AddressLength } - -func DieIf(cond bool, format string, args ...interface{}) { - Ensure(!cond, format, args) -}