diff --git a/client.go b/client.go index a601dd4..cdea2ee 100755 --- a/client.go +++ b/client.go @@ -64,7 +64,7 @@ func (client *Client) Close() error { } func (client *Client) BroadcastTransactionSynchronous(ctx context.Context, chainID []byte, operations []types.Operation, keys ...*key.PrivateKey) (*network_broadcast.BroadcastResponse, error) { - stx, err := client.createSignedTransaction(ctx, chainID, operations, keys...) + stx, err := client.CreateSignedTransaction(ctx, chainID, operations, keys...) if err != nil { return nil, err } @@ -72,7 +72,7 @@ func (client *Client) BroadcastTransactionSynchronous(ctx context.Context, chain } func (client *Client) BroadcastTransaction(ctx context.Context, chainID []byte, operations []types.Operation, keys ...*key.PrivateKey) (string, error) { - stx, err := client.createSignedTransaction(ctx, chainID, operations, keys...) + stx, err := client.CreateSignedTransaction(ctx, chainID, operations, keys...) if err != nil { return "", err } @@ -82,7 +82,7 @@ func (client *Client) BroadcastTransaction(ctx context.Context, chainID []byte, return hex.EncodeToString(id), client.NetworkBroadcast.BroadcastTransaction(ctx, stx.Transaction) } -func (client *Client) createSignedTransaction(ctx context.Context, chainID []byte, operations []types.Operation, keys ...*key.PrivateKey) (*sign.SignedTransaction, error) { +func (client *Client) CreateSignedTransaction(ctx context.Context, chainID []byte, operations []types.Operation, keys ...*key.PrivateKey) (*sign.SignedTransaction, error) { props, err := client.Chain.GetChainProperties(ctx) if err != nil { return nil, fmt.Errorf("get chainID properties: %w", err) diff --git a/sign/transactions.go b/sign/transactions.go index 44b9a5f..e373a1e 100644 --- a/sign/transactions.go +++ b/sign/transactions.go @@ -4,8 +4,8 @@ import ( "bytes" "encoding/binary" "encoding/hex" - - "github.com/pkg/errors" + "errors" + "fmt" ) func RefBlockNum(blockNumber uint32) uint16 { @@ -16,20 +16,20 @@ func RefBlockPrefix(blockID string) (uint32, error) { // Block ID is hex-encoded. rawBlockID, err := hex.DecodeString(blockID) if err != nil { - return 0, errors.Wrapf(err, "network_broadcast: failed to decode block ID: %v", blockID) + return 0, fmt.Errorf("failed to decode block ID: %w", err) } // Raw prefix = raw block ID [4:8]. // Make sure we don't trigger a slice bounds out of range panic. if len(rawBlockID) < 8 { - return 0, errors.Errorf("network_broadcast: invalid block ID: %v", blockID) + return 0, errors.New("invalid blockID") } rawPrefix := rawBlockID[4:8] // Decode the prefix. var prefix uint32 if err := binary.Read(bytes.NewReader(rawPrefix), binary.LittleEndian, &prefix); err != nil { - return 0, errors.Wrapf(err, "network_broadcast: failed to read block prefix: %v", rawPrefix) + return 0, fmt.Errorf("failed to read block prefix: %w", err) } // Done, return the prefix. diff --git a/transport/generator.go b/transport/generator.go new file mode 100644 index 0000000..8c401b1 --- /dev/null +++ b/transport/generator.go @@ -0,0 +1,24 @@ +package transport + +import "sync" + +type SequenceGenerator struct { + requestID uint64 + mtx sync.Mutex +} + +func NewSequenceGenerator(initial uint64) *SequenceGenerator { + return &SequenceGenerator{ + requestID: initial, + mtx: sync.Mutex{}, + } +} + +func (r *SequenceGenerator) Generate() uint64 { + r.mtx.Lock() + defer r.mtx.Unlock() + + r.requestID++ + + return r.requestID +} diff --git a/transport/http/transport.go b/transport/http/transport.go index 33b5c0c..c7cc9aa 100644 --- a/transport/http/transport.go +++ b/transport/http/transport.go @@ -5,27 +5,29 @@ import ( "context" "encoding/json" "fmt" + "io" "io/ioutil" - "math" "net/http" - "sync" "time" - "github.com/pkg/errors" "github.com/scorum/scorum-go/transport" ) +type requestIDGenerator interface { + Generate() uint64 +} + type Transport struct { Url string client *http.Client - requestID uint64 - reqMutex sync.Mutex + requestID requestIDGenerator } func NewTransport(url string, options ...func(*Transport)) *Transport { t := Transport{ - Url: url, + Url: url, + requestID: transport.NewSequenceGenerator(0), } for _, o := range options { @@ -48,35 +50,26 @@ func WithHttpClient(client *http.Client) func(*Transport) { } func (caller *Transport) Call(ctx context.Context, api string, method string, args []interface{}, reply interface{}) error { - caller.reqMutex.Lock() - defer caller.reqMutex.Unlock() - - // increase request id - if caller.requestID == math.MaxUint64 { - caller.requestID = 0 - } - caller.requestID++ - request := transport.RPCRequest{ Method: "call", - ID: caller.requestID, + ID: caller.requestID.Generate(), Params: []interface{}{api, method, args}, } reqBody, err := json.Marshal(request) if err != nil { - return err + return fmt.Errorf("json marshall: %w", err) } req, err := http.NewRequestWithContext(ctx, "POST", caller.Url, bytes.NewBuffer(reqBody)) if err != nil { - return err + return fmt.Errorf("http new request: %w", err) } req.Header.Set("Content-Type", "application/json") resp, err := caller.client.Do(req) if err != nil { - return err + return fmt.Errorf("http client do: %w", err) } defer resp.Body.Close() @@ -84,14 +77,9 @@ func (caller *Transport) Call(ctx context.Context, api string, method string, ar return fmt.Errorf("unexpected status code: %d", resp.StatusCode) } - respBody, err := ioutil.ReadAll(resp.Body) - if err != nil { - return errors.Wrap(err, "failed to read body") - } - var rpcResponse transport.RPCResponse - if err = json.Unmarshal(respBody, &rpcResponse); err != nil { - return errors.Wrapf(err, "failed to unmarshal response: %+v", string(respBody)) + if err := decodeJSON(resp.Body, &rpcResponse); err != nil { + return fmt.Errorf("failed decode rpc response: %w", err) } if rpcResponse.Error != nil { @@ -99,8 +87,8 @@ func (caller *Transport) Call(ctx context.Context, api string, method string, ar } if rpcResponse.Result != nil { - if err := json.Unmarshal(*rpcResponse.Result, reply); err != nil { - return errors.Wrapf(err, "failed to unmarshal rpc result: %+v", string(*rpcResponse.Result)) + if err := decodeJSON(bytes.NewReader(*rpcResponse.Result), reply); err != nil { + return fmt.Errorf("failed decode rpc result: %w", err) } } @@ -114,3 +102,21 @@ func (caller *Transport) SetCallback(api string, method string, notice func(args func (caller *Transport) Close() error { return nil } + +func decodeJSON(reader io.Reader, out interface{}) error { + buf := new(bytes.Buffer) + tr := io.TeeReader(reader, buf) + + data, err := ioutil.ReadAll(tr) + if err != nil { + return fmt.Errorf("failed to read body: %w", err) + } + if !json.Valid(data) { + return fmt.Errorf("invalid json: %q", buf.String()) + } + + if err := json.NewDecoder(buf).Decode(out); err != nil { + return fmt.Errorf("failed to decode json: %w", err) + } + return nil +} diff --git a/types/game.go b/types/game.go index 8d75fe5..47edf36 100644 --- a/types/game.go +++ b/types/game.go @@ -2,7 +2,7 @@ package types import ( "encoding/json" - "github.com/pkg/errors" + "errors" "reflect" )