Add support for mempool
This commit is contained in:
parent
842b3ccddd
commit
cd15647baf
7 changed files with 105 additions and 9 deletions
|
@ -78,6 +78,9 @@ const (
|
||||||
// https://developer.bitcoin.org/reference/rpc/estimatesmartfee.html
|
// https://developer.bitcoin.org/reference/rpc/estimatesmartfee.html
|
||||||
requestMethodEstimateSmartFee requestMethod = "estimatesmartfee"
|
requestMethodEstimateSmartFee requestMethod = "estimatesmartfee"
|
||||||
|
|
||||||
|
// https://developer.bitcoin.org/reference/rpc/getrawmempool.html
|
||||||
|
requestMethodRawMempool requestMethod = "getrawmempool"
|
||||||
|
|
||||||
// blockNotFoundErrCode is the RPC error code when a block cannot be found
|
// blockNotFoundErrCode is the RPC error code when a block cannot be found
|
||||||
blockNotFoundErrCode = -5
|
blockNotFoundErrCode = -5
|
||||||
)
|
)
|
||||||
|
@ -316,6 +319,23 @@ func (b *Client) PruneBlockchain(
|
||||||
return response.Result, nil
|
return response.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RawMempool returns an array of all transaction
|
||||||
|
// hashes currently in the mempool.
|
||||||
|
func (b *Client) RawMempool(
|
||||||
|
ctx context.Context,
|
||||||
|
) ([]string, error) {
|
||||||
|
// Parameters:
|
||||||
|
// 1. verbose
|
||||||
|
params := []interface{}{false}
|
||||||
|
|
||||||
|
response := &rawMempoolResponse{}
|
||||||
|
if err := b.post(ctx, requestMethodRawMempool, params, response); err != nil {
|
||||||
|
return nil, fmt.Errorf("%w: error getting raw mempool", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// getPeerInfo performs the `getpeerinfo` JSON-RPC request
|
// getPeerInfo performs the `getpeerinfo` JSON-RPC request
|
||||||
func (b *Client) getPeerInfo(
|
func (b *Client) getPeerInfo(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
|
|
@ -478,6 +478,25 @@ func (s suggestedFeeRateResponse) Err() error {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rawMempoolResponse is the response body for `getrawmempool` requests.
|
||||||
|
type rawMempoolResponse struct {
|
||||||
|
Result []string `json:"result"`
|
||||||
|
Error *responseError `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r rawMempoolResponse) Err() error {
|
||||||
|
if r.Error == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf(
|
||||||
|
"%w: error JSON RPC response, code: %d, message: %s",
|
||||||
|
ErrJSONRPCError,
|
||||||
|
r.Error.Code,
|
||||||
|
r.Error.Message,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// CoinIdentifier converts a tx hash and vout into
|
// CoinIdentifier converts a tx hash and vout into
|
||||||
// the canonical CoinIdentifier.Identifier used in
|
// the canonical CoinIdentifier.Identifier used in
|
||||||
// rosetta-bitcoin.
|
// rosetta-bitcoin.
|
||||||
|
|
|
@ -38,6 +38,29 @@ func (_m *Client) NetworkStatus(_a0 context.Context) (*types.NetworkStatusRespon
|
||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RawMempool provides a mock function with given fields: _a0
|
||||||
|
func (_m *Client) RawMempool(_a0 context.Context) ([]string, error) {
|
||||||
|
ret := _m.Called(_a0)
|
||||||
|
|
||||||
|
var r0 []string
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context) []string); ok {
|
||||||
|
r0 = rf(_a0)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
|
||||||
|
r1 = rf(_a0)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
// SendRawTransaction provides a mock function with given fields: _a0, _a1
|
// SendRawTransaction provides a mock function with given fields: _a0, _a1
|
||||||
func (_m *Client) SendRawTransaction(_a0 context.Context, _a1 string) (string, error) {
|
func (_m *Client) SendRawTransaction(_a0 context.Context, _a1 string) (string, error) {
|
||||||
ret := _m.Called(_a0, _a1)
|
ret := _m.Called(_a0, _a1)
|
||||||
|
|
|
@ -22,11 +22,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// MempoolAPIService implements the server.MempoolAPIServicer interface.
|
// MempoolAPIService implements the server.MempoolAPIServicer interface.
|
||||||
type MempoolAPIService struct{}
|
type MempoolAPIService struct {
|
||||||
|
client Client
|
||||||
|
}
|
||||||
|
|
||||||
// NewMempoolAPIService creates a new instance of a MempoolAPIService.
|
// NewMempoolAPIService creates a new instance of a MempoolAPIService.
|
||||||
func NewMempoolAPIService() server.MempoolAPIServicer {
|
func NewMempoolAPIService(client Client) server.MempoolAPIServicer {
|
||||||
return &MempoolAPIService{}
|
return &MempoolAPIService{
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mempool implements the /mempool endpoint.
|
// Mempool implements the /mempool endpoint.
|
||||||
|
@ -34,7 +38,19 @@ func (s *MempoolAPIService) Mempool(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *types.NetworkRequest,
|
request *types.NetworkRequest,
|
||||||
) (*types.MempoolResponse, *types.Error) {
|
) (*types.MempoolResponse, *types.Error) {
|
||||||
return nil, wrapErr(ErrUnimplemented, nil)
|
mempoolTransactions, err := s.client.RawMempool(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, wrapErr(ErrBitcoind, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
transactionIdentifiers := make([]*types.TransactionIdentifier, len(mempoolTransactions))
|
||||||
|
for i, mempoolTransaction := range mempoolTransactions {
|
||||||
|
transactionIdentifiers[i] = &types.TransactionIdentifier{Hash: mempoolTransaction}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.MempoolResponse{
|
||||||
|
TransactionIdentifiers: transactionIdentifiers,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MempoolTransaction implements the /mempool/transaction endpoint.
|
// MempoolTransaction implements the /mempool/transaction endpoint.
|
||||||
|
|
|
@ -18,20 +18,37 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
mocks "github.com/coinbase/rosetta-bitcoin/mocks/services"
|
||||||
|
|
||||||
|
"github.com/coinbase/rosetta-sdk-go/types"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMempoolEndpoints(t *testing.T) {
|
func TestMempoolEndpoints(t *testing.T) {
|
||||||
servicer := NewMempoolAPIService()
|
mockClient := &mocks.Client{}
|
||||||
|
servicer := NewMempoolAPIService(mockClient)
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
mockClient.On("RawMempool", ctx).Return([]string{
|
||||||
|
"tx1",
|
||||||
|
"tx2",
|
||||||
|
}, nil)
|
||||||
mem, err := servicer.Mempool(ctx, nil)
|
mem, err := servicer.Mempool(ctx, nil)
|
||||||
assert.Nil(t, mem)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, ErrUnimplemented.Code, err.Code)
|
assert.Equal(t, &types.MempoolResponse{
|
||||||
assert.Equal(t, ErrUnimplemented.Message, err.Message)
|
TransactionIdentifiers: []*types.TransactionIdentifier{
|
||||||
|
{
|
||||||
|
Hash: "tx1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Hash: "tx2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, mem)
|
||||||
|
|
||||||
memTransaction, err := servicer.MempoolTransaction(ctx, nil)
|
memTransaction, err := servicer.MempoolTransaction(ctx, nil)
|
||||||
assert.Nil(t, memTransaction)
|
assert.Nil(t, memTransaction)
|
||||||
assert.Equal(t, ErrUnimplemented.Code, err.Code)
|
assert.Equal(t, ErrUnimplemented.Code, err.Code)
|
||||||
assert.Equal(t, ErrUnimplemented.Message, err.Message)
|
assert.Equal(t, ErrUnimplemented.Message, err.Message)
|
||||||
|
mockClient.AssertExpectations(t)
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ func NewBlockchainRouter(
|
||||||
asserter,
|
asserter,
|
||||||
)
|
)
|
||||||
|
|
||||||
mempoolAPIService := NewMempoolAPIService()
|
mempoolAPIService := NewMempoolAPIService(client)
|
||||||
mempoolAPIController := server.NewMempoolAPIController(
|
mempoolAPIController := server.NewMempoolAPIController(
|
||||||
mempoolAPIService,
|
mempoolAPIService,
|
||||||
asserter,
|
asserter,
|
||||||
|
|
|
@ -28,6 +28,7 @@ type Client interface {
|
||||||
NetworkStatus(context.Context) (*types.NetworkStatusResponse, error)
|
NetworkStatus(context.Context) (*types.NetworkStatusResponse, error)
|
||||||
SendRawTransaction(context.Context, string) (string, error)
|
SendRawTransaction(context.Context, string) (string, error)
|
||||||
SuggestedFeeRate(context.Context, int64) (float64, error)
|
SuggestedFeeRate(context.Context, int64) (float64, error)
|
||||||
|
RawMempool(context.Context) ([]string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Indexer is used by the servicers to get block and account data.
|
// Indexer is used by the servicers to get block and account data.
|
||||||
|
|
Loading…
Reference in a new issue