Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rpcclient+rpcserver+integration: GetNetworkHashPS3 must be float64 #1778

Merged
merged 2 commits into from Dec 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
156 changes: 156 additions & 0 deletions integration/rpcserver_test.go
Expand Up @@ -13,7 +13,9 @@ import (
"os"
"runtime/debug"
"testing"
"time"

"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/integration/rpctest"
Expand Down Expand Up @@ -135,11 +137,165 @@ func testBulkClient(r *rpctest.Harness, t *testing.T) {

}

func calculateHashesPerSecBetweenBlockHeights(r *rpctest.Harness, t *testing.T, startHeight, endHeight int64) float64 {
var totalWork int64 = 0
var minTimestamp, maxTimestamp time.Time

for curHeight := startHeight; curHeight <= endHeight; curHeight++ {
hash, err := r.Client.GetBlockHash(curHeight)

if err != nil {
t.Fatal(err)
}

blockHeader, err := r.Client.GetBlockHeader(hash)

if err != nil {
t.Fatal(err)
}

if curHeight == startHeight {
minTimestamp = blockHeader.Timestamp
continue
}

totalWork += blockchain.CalcWork(blockHeader.Bits).Int64()

if curHeight == endHeight {
maxTimestamp = blockHeader.Timestamp
}
}

timeDiff := maxTimestamp.Sub(minTimestamp).Seconds()

if timeDiff == 0 {
return 0
}

return float64(totalWork) / timeDiff
}

func testGetNetworkHashPS(r *rpctest.Harness, t *testing.T) {
networkHashPS, err := r.Client.GetNetworkHashPS()

if err != nil {
t.Fatal(err)
}

expectedNetworkHashPS := calculateHashesPerSecBetweenBlockHeights(r, t, 28, 148)

if networkHashPS != expectedNetworkHashPS {
t.Fatalf("Network hashes per second should be %f but received: %f", expectedNetworkHashPS, networkHashPS)
}
}

func testGetNetworkHashPS2(r *rpctest.Harness, t *testing.T) {
networkHashPS2BlockTests := []struct {
blocks int
expectedStartHeight int64
expectedEndHeight int64
}{
// Test receiving command for negative blocks
{blocks: -200, expectedStartHeight: 0, expectedEndHeight: 148},
// Test receiving command for 0 blocks
{blocks: 0, expectedStartHeight: 0, expectedEndHeight: 148},
// Test receiving command for less than total blocks -> expectedStartHeight = 148 - 100 = 48
{blocks: 100, expectedStartHeight: 48, expectedEndHeight: 148},
// Test receiving command for exact total blocks -> expectedStartHeight = 148 - 148 = 0
{blocks: 148, expectedStartHeight: 0, expectedEndHeight: 148},
// Test receiving command for greater than total blocks
{blocks: 200, expectedStartHeight: 0, expectedEndHeight: 148},
}

for _, networkHashPS2BlockTest := range networkHashPS2BlockTests {
blocks := networkHashPS2BlockTest.blocks
expectedStartHeight := networkHashPS2BlockTest.expectedStartHeight
expectedEndHeight := networkHashPS2BlockTest.expectedEndHeight

networkHashPS, err := r.Client.GetNetworkHashPS2(blocks)

if err != nil {
t.Fatal(err)
}

expectedNetworkHashPS := calculateHashesPerSecBetweenBlockHeights(r, t, expectedStartHeight, expectedEndHeight)

if networkHashPS != expectedNetworkHashPS {
t.Fatalf("Network hashes per second should be %f but received: %f", expectedNetworkHashPS, networkHashPS)
}
}
}

func testGetNetworkHashPS3(r *rpctest.Harness, t *testing.T) {
networkHashPS3BlockTests := []struct {
height int
blocks int
expectedStartHeight int64
expectedEndHeight int64
}{
// Test receiving command for negative height -> expectedEndHeight force to 148
// - And negative blocks -> expectedStartHeight = 148 - ((148 % 2016) + 1) = -1 -> forced to 0
{height: -200, blocks: -120, expectedStartHeight: 0, expectedEndHeight: 148},
// - And zero blocks -> expectedStartHeight = 148 - ((148 % 2016) + 1) = -1 -> forced to 0
{height: -200, blocks: 0, expectedStartHeight: 0, expectedEndHeight: 148},
// - And positive blocks less than total blocks -> expectedStartHeight = 148 - 100 = 48
{height: -200, blocks: 100, expectedStartHeight: 48, expectedEndHeight: 148},
// - And positive blocks equal to total blocks
{height: -200, blocks: 148, expectedStartHeight: 0, expectedEndHeight: 148},
// - And positive blocks greater than total blocks
{height: -200, blocks: 250, expectedStartHeight: 0, expectedEndHeight: 148},

// Test receiving command for zero height
// - Should return 0 similar to expected start height and expected end height both being 0
// (blocks is irrelevant to output)
{height: 0, blocks: 120, expectedStartHeight: 0, expectedEndHeight: 0},

// Tests for valid block height -> expectedEndHeight set as height
// - And negative blocks -> expectedStartHeight = 148 - ((148 % 2016) + 1) = -1 -> forced to 0
{height: 100, blocks: -120, expectedStartHeight: 0, expectedEndHeight: 100},
// - And zero blocks -> expectedStartHeight = 148 - ((148 % 2016) + 1) = -1 -> forced to 0
{height: 100, blocks: 0, expectedStartHeight: 0, expectedEndHeight: 100},
// - And positive blocks less than command blocks -> expectedStartHeight = 100 - 70 = 30
{height: 100, blocks: 70, expectedStartHeight: 30, expectedEndHeight: 100},
// - And positive blocks equal to command blocks -> expectedStartHeight = 100 - 100 = 0
{height: 100, blocks: 100, expectedStartHeight: 0, expectedEndHeight: 100},
// - And positive blocks greater than command blocks -> expectedStartHeight = 100 - 200 = -100 -> forced to 0
{height: 100, blocks: 200, expectedStartHeight: 0, expectedEndHeight: 100},

// Test receiving command for height greater than block height
// - Should return 0 similar to expected start height and expected end height both being 0
// (blocks is irrelevant to output)
{height: 200, blocks: 120, expectedStartHeight: 0, expectedEndHeight: 0},
}

for _, networkHashPS3BlockTest := range networkHashPS3BlockTests {
blocks := networkHashPS3BlockTest.blocks
height := networkHashPS3BlockTest.height
expectedStartHeight := networkHashPS3BlockTest.expectedStartHeight
expectedEndHeight := networkHashPS3BlockTest.expectedEndHeight

networkHashPS, err := r.Client.GetNetworkHashPS3(blocks, height)

if err != nil {
t.Fatal(err)
}

expectedNetworkHashPS := calculateHashesPerSecBetweenBlockHeights(r, t, expectedStartHeight, expectedEndHeight)

if networkHashPS != expectedNetworkHashPS {
t.Fatalf("Network hashes per second should be %f but received: %f", expectedNetworkHashPS, networkHashPS)
}
}
}

var rpcTestCases = []rpctest.HarnessTestCase{
testGetBestBlock,
testGetBlockCount,
testGetBlockHash,
testBulkClient,
testGetNetworkHashPS,
testGetNetworkHashPS2,
testGetNetworkHashPS3,
}

var primaryHarness *rpctest.Harness
Expand Down
12 changes: 6 additions & 6 deletions rpcclient/mining.go
Expand Up @@ -255,14 +255,14 @@ type FutureGetNetworkHashPS chan *Response
// Receive waits for the Response promised by the future and returns the
// estimated network hashes per second for the block heights provided by the
// parameters.
func (r FutureGetNetworkHashPS) Receive() (int64, error) {
func (r FutureGetNetworkHashPS) Receive() (float64, error) {
res, err := ReceiveFuture(r)
if err != nil {
return -1, err
}

// Unmarshal result as an int64.
var result int64
// Unmarshal result as an float64.
var result float64
err = json.Unmarshal(res, &result)
if err != nil {
return 0, err
Expand All @@ -286,7 +286,7 @@ func (c *Client) GetNetworkHashPSAsync() FutureGetNetworkHashPS {
//
// See GetNetworkHashPS2 to override the number of blocks to use and
// GetNetworkHashPS3 to override the height at which to calculate the estimate.
func (c *Client) GetNetworkHashPS() (int64, error) {
func (c *Client) GetNetworkHashPS() (float64, error) {
return c.GetNetworkHashPSAsync().Receive()
}

Expand All @@ -307,7 +307,7 @@ func (c *Client) GetNetworkHashPS2Async(blocks int) FutureGetNetworkHashPS {
//
// See GetNetworkHashPS to use defaults and GetNetworkHashPS3 to override the
// height at which to calculate the estimate.
func (c *Client) GetNetworkHashPS2(blocks int) (int64, error) {
func (c *Client) GetNetworkHashPS2(blocks int) (float64, error) {
return c.GetNetworkHashPS2Async(blocks).Receive()
}

Expand All @@ -327,7 +327,7 @@ func (c *Client) GetNetworkHashPS3Async(blocks, height int) FutureGetNetworkHash
// of blocks since the last difficulty change will be used.
//
// See GetNetworkHashPS and GetNetworkHashPS2 to use defaults.
func (c *Client) GetNetworkHashPS3(blocks, height int) (int64, error) {
func (c *Client) GetNetworkHashPS3(blocks, height int) (float64, error) {
return c.GetNetworkHashPS3Async(blocks, height).Receive()
}

Expand Down
20 changes: 10 additions & 10 deletions rpcserver.go
Expand Up @@ -2355,11 +2355,11 @@ func handleGetMiningInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{
if err != nil {
return nil, err
}
networkHashesPerSec, ok := networkHashesPerSecIface.(int64)
networkHashesPerSec, ok := networkHashesPerSecIface.(float64)
if !ok {
return nil, &btcjson.RPCError{
Code: btcjson.ErrRPCInternal.Code,
Message: "networkHashesPerSec is not an int64",
Message: "networkHashesPerSec is not a float64",
}
}

Expand All @@ -2373,7 +2373,7 @@ func handleGetMiningInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{
Generate: s.cfg.CPUMiner.IsMining(),
GenProcLimit: s.cfg.CPUMiner.NumWorkers(),
HashesPerSec: s.cfg.CPUMiner.HashesPerSecond(),
NetworkHashPS: float64(networkHashesPerSec),
NetworkHashPS: networkHashesPerSec,
PooledTx: uint64(s.cfg.TxMemPool.Count()),
TestNet: cfg.TestNet3,
}
Expand All @@ -2393,8 +2393,8 @@ func handleGetNetTotals(s *rpcServer, cmd interface{}, closeChan <-chan struct{}

// handleGetNetworkHashPS implements the getnetworkhashps command.
func handleGetNetworkHashPS(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
// Note: All valid error return paths should return an int64.
// Literal zeros are inferred as int, and won't coerce to int64
// Note: All valid error return paths should return a float64.
// Literal zeros are inferred as int, and won't coerce to float64
// because the return value is an interface{}.

c := cmd.(*btcjson.GetNetworkHashPSCmd)
Expand All @@ -2409,7 +2409,7 @@ func handleGetNetworkHashPS(s *rpcServer, cmd interface{}, closeChan <-chan stru
endHeight = int32(*c.Height)
}
if endHeight > best.Height || endHeight == 0 {
return int64(0), nil
return float64(0), nil
}
if endHeight < 0 {
endHeight = best.Height
Expand Down Expand Up @@ -2476,13 +2476,13 @@ func handleGetNetworkHashPS(s *rpcServer, cmd interface{}, closeChan <-chan stru
// Calculate the difference in seconds between the min and max block
// timestamps and avoid division by zero in the case where there is no
// time difference.
timeDiff := int64(maxTimestamp.Sub(minTimestamp) / time.Second)
timeDiff := maxTimestamp.Sub(minTimestamp).Seconds()
if timeDiff == 0 {
return int64(0), nil
return timeDiff, nil
}

hashesPerSec := new(big.Int).Div(totalWork, big.NewInt(timeDiff))
return hashesPerSec.Int64(), nil
hashesPerSec, _ := new(big.Float).Quo(new(big.Float).SetInt(totalWork), new(big.Float).SetFloat64(timeDiff)).Float64()
return hashesPerSec, nil
}

// handleGetNodeAddresses implements the getnodeaddresses command.
Expand Down
2 changes: 1 addition & 1 deletion rpcserverhelp.go
Expand Up @@ -740,7 +740,7 @@ var rpcResultTypes = map[string][]interface{}{
"getmempoolinfo": {(*btcjson.GetMempoolInfoResult)(nil)},
"getmininginfo": {(*btcjson.GetMiningInfoResult)(nil)},
"getnettotals": {(*btcjson.GetNetTotalsResult)(nil)},
"getnetworkhashps": {(*int64)(nil)},
"getnetworkhashps": {(*float64)(nil)},
"getnodeaddresses": {(*[]btcjson.GetNodeAddressesResult)(nil)},
"getpeerinfo": {(*[]btcjson.GetPeerInfoResult)(nil)},
"getrawmempool": {(*[]string)(nil), (*btcjson.GetRawMempoolVerboseResult)(nil)},
Expand Down