Skip to content
Snippets Groups Projects
Select Git revision
  • d51fe3b2b598967335ea97d3ec930fe74a906c14
  • without_tipselection default
  • develop protected
  • fix/grafana-local-dashboard
  • wasp
  • fix/dashboard-explorer-freeze
  • master
  • feat/timerqueue
  • test/sync_debug_and_650
  • feat/sync_revamp_inv
  • wip/sync
  • tool/db-recovery
  • portcheck/fix
  • fix/synchronization
  • feat/new-dashboard-analysis
  • feat/refactored-analysis-dashboard
  • feat/new-analysis-dashboard
  • test/demo-prometheus-fpc
  • prometheus_metrics
  • wip/analysis-server
  • merge/fpc-test-value-transfer
  • v0.2.2
  • v0.2.1
  • v0.2.0
  • v0.1.3
  • v0.1.2
  • v0.1.1
  • v0.1.0
28 results

lib.go

Blame
  • lib.go 8.15 KiB
    // Implements a very simple wrapper for GoShimmer's web API .
    package goshimmer
    
    import (
    	"bytes"
    	"encoding/json"
    	"errors"
    	"fmt"
    	"io"
    	"io/ioutil"
    	"net/http"
    
    	webapi_broadcastData "github.com/iotaledger/goshimmer/plugins/webapi/broadcastData"
    	webapi_findTransactionHashes "github.com/iotaledger/goshimmer/plugins/webapi/findTransactionHashes"
    	webapi_getNeighbors "github.com/iotaledger/goshimmer/plugins/webapi/getNeighbors"
    	webapi_getTransactionObjectsByHash "github.com/iotaledger/goshimmer/plugins/webapi/getTransactionObjectsByHash"
    	webapi_getTransactionTrytesByHash "github.com/iotaledger/goshimmer/plugins/webapi/getTransactionTrytesByHash"
    	webapi_gtta "github.com/iotaledger/goshimmer/plugins/webapi/gtta"
    	webapi_spammer "github.com/iotaledger/goshimmer/plugins/webapi/spammer"
    	webapi_auth "github.com/iotaledger/goshimmer/plugins/webauth"
    	"github.com/iotaledger/iota.go/consts"
    	"github.com/iotaledger/iota.go/guards"
    	"github.com/iotaledger/iota.go/trinary"
    )
    
    var (
    	ErrBadRequest          = errors.New("bad request")
    	ErrInternalServerError = errors.New("internal server error")
    	ErrNotFound            = errors.New("not found")
    	ErrUnauthorized        = errors.New("unauthorized")
    	ErrUnknownError        = errors.New("unknown error")
    	ErrNotImplemented      = errors.New("operation not implemented/supported/available")
    )
    
    const (
    	routeBroadcastData               = "broadcastData"
    	routeGetTransactionTrytesByHash  = "getTransactionTrytesByHash"
    	routeGetTransactionObjectsByHash = "getTransactionObjectsByHash"
    	routeFindTransactionsHashes      = "findTransactionHashes"
    	routeGetNeighbors                = "getNeighbors"
    	routeGetTransactionsToApprove    = "getTransactionsToApprove"
    	routeSpammer                     = "spammer"
    	routeLogin                       = "login"
    
    	contentTypeJSON = "application/json"
    )
    
    func NewGoShimmerAPI(node string, httpClient ...http.Client) *GoShimmerAPI {
    	if len(httpClient) > 0 {
    		return &GoShimmerAPI{node: node, httpClient: httpClient[0]}
    	}
    	return &GoShimmerAPI{node: node}
    }
    
    // GoShimmerAPI is an API wrapper over the web API of GoShimmer.
    type GoShimmerAPI struct {
    	httpClient http.Client
    	node       string
    	jwt        string
    }
    
    type errorresponse struct {
    	Error string `json:"error"`
    }
    
    func interpretBody(res *http.Response, decodeTo interface{}) error {
    	resBody, err := ioutil.ReadAll(res.Body)
    	if err != nil {
    		return fmt.Errorf("unable to read response body: %w", err)
    	}
    	defer res.Body.Close()
    
    	if res.StatusCode == http.StatusOK || res.StatusCode == http.StatusCreated {
    		return json.Unmarshal(resBody, decodeTo)
    	}
    
    	errRes := &errorresponse{}
    	if err := json.Unmarshal(resBody, errRes); err != nil {
    		return fmt.Errorf("unable to read error from response body: %w", err)
    	}
    
    	switch res.StatusCode {
    	case http.StatusInternalServerError:
    		return fmt.Errorf("%w: %s", ErrInternalServerError, errRes.Error)
    	case http.StatusNotFound:
    		return fmt.Errorf("%w: %s", ErrNotFound, errRes.Error)
    	case http.StatusBadRequest:
    		return fmt.Errorf("%w: %s", ErrBadRequest, errRes.Error)
    	case http.StatusUnauthorized:
    		return fmt.Errorf("%w: %s", ErrUnauthorized, errRes.Error)
    	case http.StatusNotImplemented:
    		return fmt.Errorf("%w: %s", ErrNotImplemented, errRes.Error)
    	}
    
    	return fmt.Errorf("%w: %s", ErrUnknownError, errRes.Error)
    }
    
    func (api *GoShimmerAPI) do(method string, route string, reqObj interface{}, resObj interface{}) error {
    	// marshal request object
    	var data []byte
    	if reqObj != nil {
    		var err error
    		data, err = json.Marshal(reqObj)
    		if err != nil {
    			return err
    		}
    	}
    
    	// construct request
    	req, err := http.NewRequest(method, fmt.Sprintf("%s/%s", api.node, route), func() io.Reader {
    		if data == nil {
    			return nil
    		}
    		return bytes.NewReader(data)
    	}())
    	if err != nil {
    		return err
    	}
    
    	if data != nil {
    		req.Header.Set("Content-Type", contentTypeJSON)
    	}
    
    	// add authorization header with JWT
    	if len(api.jwt) > 0 {
    		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", api.jwt))
    	}
    
    	// make the request
    	res, err := api.httpClient.Do(req)
    	if err != nil {
    		return err
    	}
    
    	if resObj == nil {
    		return nil
    	}
    
    	// write response into response object
    	if err := interpretBody(res, resObj); err != nil {
    		return err
    	}
    	return nil
    }
    
    // Login authorizes this API instance against the web API.
    // You must call this function before any before any other call, if the web-auth plugin is enabled.
    func (api *GoShimmerAPI) Login(username string, password string) error {
    	res := &webapi_auth.Response{}
    	if err := api.do(http.MethodPost, routeLogin,
    		&webapi_auth.Request{Username: username, Password: password}, res); err != nil {
    		return err
    	}
    	api.jwt = res.Token
    	return nil
    }
    
    // BroadcastData sends the given data by creating a zero value transaction in the backend targeting the given address.
    func (api *GoShimmerAPI) BroadcastData(targetAddress trinary.Trytes, data string) (trinary.Hash, error) {
    	if !guards.IsHash(targetAddress) {
    		return "", fmt.Errorf("%w: invalid address: %s", consts.ErrInvalidHash, targetAddress)
    	}
    
    	res := &webapi_broadcastData.Response{}
    	if err := api.do(http.MethodPost, routeBroadcastData,
    		&webapi_broadcastData.Request{Address: targetAddress, Data: data}, res); err != nil {
    		return "", err
    	}
    
    	return res.Hash, nil
    }
    
    // GetTransactionTrytesByHash gets the corresponding transaction trytes given the transaction hashes.
    func (api *GoShimmerAPI) GetTransactionTrytesByHash(txHashes trinary.Hashes) ([]trinary.Trytes, error) {
    	for _, hash := range txHashes {
    		if !guards.IsTrytes(hash) {
    			return nil, fmt.Errorf("%w: invalid hash: %s", consts.ErrInvalidHash, hash)
    		}
    	}
    
    	res := &webapi_getTransactionTrytesByHash.Response{}
    	if err := api.do(http.MethodPost, routeGetTransactionTrytesByHash,
    		&webapi_getTransactionTrytesByHash.Request{Hashes: txHashes}, res); err != nil {
    		return nil, err
    	}
    
    	return res.Trytes, nil
    }
    
    // GetTransactionObjectsByHash gets the transaction objects given the transaction hashes.
    func (api *GoShimmerAPI) GetTransactionObjectsByHash(txHashes trinary.Hashes) ([]webapi_getTransactionObjectsByHash.Transaction, error) {
    	for _, hash := range txHashes {
    		if !guards.IsTrytes(hash) {
    			return nil, fmt.Errorf("%w: invalid hash: %s", consts.ErrInvalidHash, hash)
    		}
    	}
    
    	res := &webapi_getTransactionObjectsByHash.Response{}
    	if err := api.do(http.MethodPost, routeGetTransactionObjectsByHash,
    		&webapi_getTransactionObjectsByHash.Request{Hashes: txHashes}, res); err != nil {
    		return nil, err
    	}
    
    	return res.Transactions, nil
    }
    
    // FindTransactionHashes finds the given transaction hashes given the query.
    func (api *GoShimmerAPI) FindTransactionHashes(query *webapi_findTransactionHashes.Request) ([]trinary.Hashes, error) {
    	for _, hash := range query.Addresses {
    		if !guards.IsTrytes(hash) {
    			return nil, fmt.Errorf("%w: invalid hash: %s", consts.ErrInvalidHash, hash)
    		}
    	}
    
    	res := &webapi_findTransactionHashes.Response{}
    	if err := api.do(http.MethodPost, routeFindTransactionsHashes, query, res); err != nil {
    		return nil, err
    	}
    
    	return res.Transactions, nil
    }
    
    // GetNeighbors gets the chosen/accepted neighbors.
    // If knownPeers is set, also all known peers to the node are returned additionally.
    func (api *GoShimmerAPI) GetNeighbors(knownPeers bool) (*webapi_getNeighbors.Response, error) {
    	res := &webapi_getNeighbors.Response{}
    	if err := api.do(http.MethodGet, func() string {
    		if !knownPeers {
    			return routeGetNeighbors
    		}
    		return fmt.Sprintf("%s?known=1", routeGetNeighbors)
    	}(), nil, res); err != nil {
    		return nil, err
    	}
    	return res, nil
    }
    
    // GetTips executes the tip-selection on the node to retrieve tips to approve.
    func (api *GoShimmerAPI) GetTransactionsToApprove() (*webapi_gtta.Response, error) {
    	res := &webapi_gtta.Response{}
    	if err := api.do(http.MethodGet, routeGetTransactionsToApprove, nil, res); err != nil {
    		return nil, err
    	}
    	return res, nil
    }
    
    // ToggleSpammer toggles the node internal spammer.
    func (api *GoShimmerAPI) ToggleSpammer(enable bool) (*webapi_spammer.Response, error) {
    	res := &webapi_spammer.Response{}
    	if err := api.do(http.MethodGet, func() string {
    		if enable {
    			return fmt.Sprintf("%s?cmd=start", routeSpammer)
    		}
    		return fmt.Sprintf("%s?cmd=stop", routeSpammer)
    	}(), nil, res); err != nil {
    		return nil, err
    	}
    	return res, nil
    }