From 456fdc7750dbc63b8c0c692617dd0704e24c18ac Mon Sep 17 00:00:00 2001 From: Matthew Bajorek Date: Sun, 5 Dec 2021 15:15:15 -0500 Subject: [PATCH 1/2] rpcclient+rpcserver: Change getnetworkhashps return type to be a float to be in line with bitcoin core --- rpcclient/mining.go | 12 ++++++------ rpcserver.go | 20 ++++++++++---------- rpcserverhelp.go | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/rpcclient/mining.go b/rpcclient/mining.go index 15473e24be..717825eeaa 100644 --- a/rpcclient/mining.go +++ b/rpcclient/mining.go @@ -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 @@ -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() } @@ -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() } @@ -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() } diff --git a/rpcserver.go b/rpcserver.go index d184072942..6f60261a9c 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -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", } } @@ -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, } @@ -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) @@ -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 @@ -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. diff --git a/rpcserverhelp.go b/rpcserverhelp.go index 570b5b0500..16bbb62a2b 100644 --- a/rpcserverhelp.go +++ b/rpcserverhelp.go @@ -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)}, From b6898d7956f1a7198816b6dc00e61adc7b945ad5 Mon Sep 17 00:00:00 2001 From: Matthew Bajorek Date: Sun, 5 Dec 2021 15:15:41 -0500 Subject: [PATCH 2/2] integration: Add unit tests for all three GetNetworkHashPS client methods --- integration/rpcserver_test.go | 156 ++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/integration/rpcserver_test.go b/integration/rpcserver_test.go index 5875b35353..13325bc1d7 100644 --- a/integration/rpcserver_test.go +++ b/integration/rpcserver_test.go @@ -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" @@ -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