Node API Refactor - pt3 (Stage 3.1 of Node API Overhaul) (#3297)
* [rosetta] Add server with example block & network services * Update go.mod for rosetta SDK Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [internal/configs] Add RosettaServer to node config Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [cmd] Add rosetta port flag Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [node] Add rosetta server start & rename api.go Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Change server start to use nodeconfig of rosetta Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [cmd] Cleanup debugging prints Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Fix stdout print & document placeholders Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Fix lint & make StartSevers more consistent Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [cmd] Disable rosetta by default Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [test] Make explorer deploy rosetta server Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Use direct http server for start * Make go.mod changes minimal Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Fix fmt Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Fix fmt of go.mod Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [rosetta] Use port 9700 instead of 10000 Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [cmd] Bump config version Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [cmd] Add v1.0.0 config backwards compatibility test * Included update message if old config is loaded Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [test] Do not broadcast invalid tx on localnet Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [cmd] Correct for invalid port when loading old config Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu> * [cmd] Make rosetta variable names consistent Signed-off-by: Daniel Van Der Maden <dvandermaden0@berkeley.edu>pull/3303/head
parent
03bf274048
commit
8068d80200
@ -0,0 +1,75 @@ |
||||
package config |
||||
|
||||
import ( |
||||
"fmt" |
||||
"time" |
||||
|
||||
"github.com/coinbase/rosetta-sdk-go/types" |
||||
shardingconfig "github.com/harmony-one/harmony/internal/configs/sharding" |
||||
"github.com/harmony-one/harmony/rpc" |
||||
"github.com/harmony-one/harmony/shard" |
||||
staking "github.com/harmony-one/harmony/staking/types" |
||||
) |
||||
|
||||
const ( |
||||
// Blockchain ..
|
||||
Blockchain = "Harmony" |
||||
|
||||
// Symbol ..
|
||||
Symbol = "ONE" |
||||
|
||||
// Decimals ..
|
||||
Decimals = 18 |
||||
|
||||
// CurveType ..
|
||||
CurveType = types.Secp256k1 |
||||
) |
||||
|
||||
var ( |
||||
// TransactionTypes ..
|
||||
TransactionTypes = []string{ |
||||
"Transfer", |
||||
"CrossShardTransfer", |
||||
staking.DirectiveCreateValidator.String(), |
||||
staking.DirectiveEditValidator.String(), |
||||
staking.DirectiveDelegate.String(), |
||||
staking.DirectiveUndelegate.String(), |
||||
staking.DirectiveCollectRewards.String(), |
||||
} |
||||
|
||||
// ReadTimeout ..
|
||||
ReadTimeout = 30 * time.Second |
||||
|
||||
// WriteTimeout ..
|
||||
WriteTimeout = 30 * time.Second |
||||
|
||||
// IdleTimeout ..
|
||||
IdleTimeout = 120 * time.Second |
||||
) |
||||
|
||||
// ShardMetadata for the network identifier
|
||||
type ShardMetadata struct { |
||||
IsBeacon bool `json:"isBeacon"` |
||||
} |
||||
|
||||
// GetNetwork fetches the networking identifier for the given shard
|
||||
func GetNetwork(shardID uint32) *types.NetworkIdentifier { |
||||
metadata, _ := rpc.NewStructuredResponse(ShardMetadata{ |
||||
IsBeacon: shardID == shard.BeaconChainShardID, |
||||
}) |
||||
return &types.NetworkIdentifier{ |
||||
Blockchain: Blockchain, |
||||
Network: getNetworkName(), |
||||
SubNetworkIdentifier: &types.SubNetworkIdentifier{ |
||||
Network: fmt.Sprintf("shard %d", shardID), |
||||
Metadata: metadata, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
func getNetworkName() string { |
||||
if shard.Schedule.GetNetworkID() == shardingconfig.MainNet { |
||||
return "Mainnet" |
||||
} |
||||
return "Testnet" |
||||
} |
@ -0,0 +1,87 @@ |
||||
package rosetta |
||||
|
||||
import ( |
||||
"fmt" |
||||
"net" |
||||
"net/http" |
||||
"time" |
||||
|
||||
"github.com/coinbase/rosetta-sdk-go/asserter" |
||||
"github.com/coinbase/rosetta-sdk-go/server" |
||||
"github.com/coinbase/rosetta-sdk-go/types" |
||||
"github.com/harmony-one/harmony/hmy" |
||||
nodeconfig "github.com/harmony-one/harmony/internal/configs/node" |
||||
"github.com/harmony-one/harmony/internal/utils" |
||||
common "github.com/harmony-one/harmony/rosetta/common" |
||||
"github.com/harmony-one/harmony/rosetta/services" |
||||
) |
||||
|
||||
// StartServers starts the rosetta http server
|
||||
func StartServers(hmy *hmy.Harmony, config nodeconfig.RosettaServerConfig) error { |
||||
if !config.HTTPEnabled { |
||||
utils.Logger().Info().Msg("Rosetta http server disabled...") |
||||
return nil |
||||
} |
||||
network := common.GetNetwork(hmy.ShardID) |
||||
|
||||
serverAsserter, err := asserter.NewServer( |
||||
common.TransactionTypes, |
||||
nodeconfig.GetDefaultConfig().Role() == nodeconfig.ExplorerNode, |
||||
[]*types.NetworkIdentifier{network}, |
||||
) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
router := server.CorsMiddleware(loggerMiddleware(getRouter(network, serverAsserter, hmy))) |
||||
utils.Logger().Info(). |
||||
Int("port", config.HTTPPort). |
||||
Str("ip", config.HTTPIp). |
||||
Msg("Starting Rosetta server") |
||||
|
||||
endpoint := fmt.Sprintf("%s:%d", config.HTTPIp, config.HTTPPort) |
||||
var ( |
||||
listener net.Listener |
||||
) |
||||
if listener, err = net.Listen("tcp", endpoint); err != nil { |
||||
return err |
||||
} |
||||
go newHTTPServer(router).Serve(listener) |
||||
return nil |
||||
} |
||||
|
||||
func newHTTPServer(handler http.Handler) *http.Server { |
||||
return &http.Server{ |
||||
Handler: handler, |
||||
ReadTimeout: common.ReadTimeout, |
||||
WriteTimeout: common.WriteTimeout, |
||||
IdleTimeout: common.IdleTimeout, |
||||
} |
||||
} |
||||
|
||||
func getRouter( |
||||
network *types.NetworkIdentifier, |
||||
asserter *asserter.Asserter, |
||||
hmy *hmy.Harmony, |
||||
) http.Handler { |
||||
return server.NewRouter( |
||||
server.NewNetworkAPIController(services.NewNetworkAPIService(network, hmy), asserter), |
||||
server.NewBlockAPIController(services.NewBlockAPIService(network, hmy), asserter), |
||||
) |
||||
} |
||||
|
||||
func loggerMiddleware(router http.Handler) http.Handler { |
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
||||
start := time.Now() |
||||
router.ServeHTTP(w, r) |
||||
msg := fmt.Sprintf( |
||||
"Rosetta: %s %s %s", |
||||
r.Method, |
||||
r.RequestURI, |
||||
time.Since(start), |
||||
) |
||||
utils.Logger().Info().Msg(msg) |
||||
// Print to stdout for quick check of rosetta activity
|
||||
fmt.Printf("%s %s\n", time.Now().Format("2006-01-02 15:04:05"), msg) |
||||
}) |
||||
} |
@ -0,0 +1,158 @@ |
||||
package services |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
"time" |
||||
|
||||
"github.com/coinbase/rosetta-sdk-go/server" |
||||
"github.com/coinbase/rosetta-sdk-go/types" |
||||
|
||||
"github.com/harmony-one/harmony/hmy" |
||||
) |
||||
|
||||
// BlockAPIService implements the server.BlockAPIServicer interface.
|
||||
type BlockAPIService struct { |
||||
hmy *hmy.Harmony |
||||
network *types.NetworkIdentifier |
||||
} |
||||
|
||||
// NewBlockAPIService creates a new instance of a BlockAPIService.
|
||||
func NewBlockAPIService( |
||||
network *types.NetworkIdentifier, hmy *hmy.Harmony, |
||||
) server.BlockAPIServicer { |
||||
return &BlockAPIService{ |
||||
hmy: hmy, |
||||
network: network, |
||||
} |
||||
} |
||||
|
||||
// Block implements the /block endpoint (placeholder)
|
||||
// FIXME: remove placeholder & implement block endpoint
|
||||
func (s *BlockAPIService) Block( |
||||
ctx context.Context, |
||||
request *types.BlockRequest, |
||||
) (*types.BlockResponse, *types.Error) { |
||||
if *request.BlockIdentifier.Index != 1000 { |
||||
previousBlockIndex := *request.BlockIdentifier.Index - 1 |
||||
if previousBlockIndex < 0 { |
||||
previousBlockIndex = 0 |
||||
} |
||||
|
||||
return &types.BlockResponse{ |
||||
Block: &types.Block{ |
||||
BlockIdentifier: &types.BlockIdentifier{ |
||||
Index: *request.BlockIdentifier.Index, |
||||
Hash: fmt.Sprintf("block %d", *request.BlockIdentifier.Index), |
||||
}, |
||||
ParentBlockIdentifier: &types.BlockIdentifier{ |
||||
Index: previousBlockIndex, |
||||
Hash: fmt.Sprintf("block %d", previousBlockIndex), |
||||
}, |
||||
Timestamp: time.Now().UnixNano() / 1000000, |
||||
Transactions: []*types.Transaction{}, |
||||
}, |
||||
}, nil |
||||
} |
||||
|
||||
return &types.BlockResponse{ |
||||
Block: &types.Block{ |
||||
BlockIdentifier: &types.BlockIdentifier{ |
||||
Index: 1000, |
||||
Hash: "block 1000", |
||||
}, |
||||
ParentBlockIdentifier: &types.BlockIdentifier{ |
||||
Index: 999, |
||||
Hash: "block 999", |
||||
}, |
||||
Timestamp: 1586483189000, |
||||
Transactions: []*types.Transaction{ |
||||
{ |
||||
TransactionIdentifier: &types.TransactionIdentifier{ |
||||
Hash: "transaction 0", |
||||
}, |
||||
Operations: []*types.Operation{ |
||||
{ |
||||
OperationIdentifier: &types.OperationIdentifier{ |
||||
Index: 0, |
||||
}, |
||||
Type: "Transfer", |
||||
Status: "Success", |
||||
Account: &types.AccountIdentifier{ |
||||
Address: "account 0", |
||||
}, |
||||
Amount: &types.Amount{ |
||||
Value: "-1000", |
||||
Currency: &types.Currency{ |
||||
Symbol: "ROS", |
||||
Decimals: 2, |
||||
}, |
||||
}, |
||||
}, |
||||
{ |
||||
OperationIdentifier: &types.OperationIdentifier{ |
||||
Index: 1, |
||||
}, |
||||
RelatedOperations: []*types.OperationIdentifier{ |
||||
{ |
||||
Index: 0, |
||||
}, |
||||
}, |
||||
Type: "Transfer", |
||||
Status: "Reverted", |
||||
Account: &types.AccountIdentifier{ |
||||
Address: "account 1", |
||||
}, |
||||
Amount: &types.Amount{ |
||||
Value: "1000", |
||||
Currency: &types.Currency{ |
||||
Symbol: "ROS", |
||||
Decimals: 2, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
OtherTransactions: []*types.TransactionIdentifier{ |
||||
{ |
||||
Hash: "transaction 1", |
||||
}, |
||||
}, |
||||
}, nil |
||||
} |
||||
|
||||
// BlockTransaction implements the /block/transaction endpoint (placeholder)
|
||||
// FIXME: remove placeholder & implement block endpoint
|
||||
func (s *BlockAPIService) BlockTransaction( |
||||
ctx context.Context, |
||||
request *types.BlockTransactionRequest, |
||||
) (*types.BlockTransactionResponse, *types.Error) { |
||||
return &types.BlockTransactionResponse{ |
||||
Transaction: &types.Transaction{ |
||||
TransactionIdentifier: &types.TransactionIdentifier{ |
||||
Hash: "transaction 1", |
||||
}, |
||||
Operations: []*types.Operation{ |
||||
{ |
||||
OperationIdentifier: &types.OperationIdentifier{ |
||||
Index: 0, |
||||
}, |
||||
Type: "Reward", |
||||
Status: "Success", |
||||
Account: &types.AccountIdentifier{ |
||||
Address: "account 2", |
||||
}, |
||||
Amount: &types.Amount{ |
||||
Value: "1000", |
||||
Currency: &types.Currency{ |
||||
Symbol: "ROS", |
||||
Decimals: 2, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, nil |
||||
} |
@ -0,0 +1,100 @@ |
||||
package services |
||||
|
||||
import ( |
||||
"context" |
||||
|
||||
"github.com/coinbase/rosetta-sdk-go/server" |
||||
"github.com/coinbase/rosetta-sdk-go/types" |
||||
|
||||
"github.com/harmony-one/harmony/hmy" |
||||
) |
||||
|
||||
// NetworkAPIService implements the server.NetworkAPIServicer interface.
|
||||
type NetworkAPIService struct { |
||||
hmy *hmy.Harmony |
||||
network *types.NetworkIdentifier |
||||
} |
||||
|
||||
// NewNetworkAPIService creates a new instance of a NetworkAPIService.
|
||||
func NewNetworkAPIService( |
||||
network *types.NetworkIdentifier, hmy *hmy.Harmony, |
||||
) server.NetworkAPIServicer { |
||||
return &NetworkAPIService{ |
||||
hmy: hmy, |
||||
network: network, |
||||
} |
||||
} |
||||
|
||||
// NetworkList implements the /network/list endpoint (placeholder)
|
||||
// FIXME: remove placeholder & implement block endpoint
|
||||
func (s *NetworkAPIService) NetworkList( |
||||
ctx context.Context, |
||||
request *types.MetadataRequest, |
||||
) (*types.NetworkListResponse, *types.Error) { |
||||
return &types.NetworkListResponse{ |
||||
NetworkIdentifiers: []*types.NetworkIdentifier{ |
||||
s.network, |
||||
}, |
||||
}, nil |
||||
} |
||||
|
||||
// NetworkStatus implements the /network/status endpoint (placeholder)
|
||||
// FIXME: remove placeholder & implement block endpoint
|
||||
func (s *NetworkAPIService) NetworkStatus( |
||||
ctx context.Context, |
||||
request *types.NetworkRequest, |
||||
) (*types.NetworkStatusResponse, *types.Error) { |
||||
return &types.NetworkStatusResponse{ |
||||
CurrentBlockIdentifier: &types.BlockIdentifier{ |
||||
Index: 1000, |
||||
Hash: "block 1000", |
||||
}, |
||||
CurrentBlockTimestamp: int64(1586483189000), |
||||
GenesisBlockIdentifier: &types.BlockIdentifier{ |
||||
Index: 0, |
||||
Hash: "block 0", |
||||
}, |
||||
Peers: []*types.Peer{ |
||||
{ |
||||
PeerID: "peer 1", |
||||
}, |
||||
}, |
||||
}, nil |
||||
} |
||||
|
||||
// NetworkOptions implements the /network/options endpoint (placeholder)
|
||||
// FIXME: remove placeholder & implement block endpoint
|
||||
func (s *NetworkAPIService) NetworkOptions( |
||||
ctx context.Context, |
||||
request *types.NetworkRequest, |
||||
) (*types.NetworkOptionsResponse, *types.Error) { |
||||
return &types.NetworkOptionsResponse{ |
||||
Version: &types.Version{ |
||||
RosettaVersion: "1.4.0", |
||||
NodeVersion: "0.0.1", |
||||
}, |
||||
Allow: &types.Allow{ |
||||
OperationStatuses: []*types.OperationStatus{ |
||||
{ |
||||
Status: "Success", |
||||
Successful: true, |
||||
}, |
||||
{ |
||||
Status: "Reverted", |
||||
Successful: false, |
||||
}, |
||||
}, |
||||
OperationTypes: []string{ |
||||
"Transfer", |
||||
"Reward", |
||||
}, |
||||
Errors: []*types.Error{ |
||||
{ |
||||
Code: 1, |
||||
Message: "not implemented", |
||||
Retriable: false, |
||||
}, |
||||
}, |
||||
}, |
||||
}, nil |
||||
} |
Loading…
Reference in new issue