Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Unsafe account_balances RPC method #1625

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ BINANCE_TGORACLE_DIR=$(GOPATH)/src/$(PKG_BINANCE_TGORACLE)
# specific commit.
GO_LOOM_GIT_REV = HEAD
# Specifies the loomnetwork/transfer-gateway branch/revision to use.
TG_GIT_REV = HEAD
TG_GIT_REV = export-getcontract-mapping
# loomnetwork/go-ethereum loomchain branch
ETHEREUM_GIT_REV = 6128fa1a8c767035d3da6ef0c27ebb7778ce3713
# use go-plugin we get 'timeout waiting for connection info' error
Expand Down
11 changes: 11 additions & 0 deletions rpc/instrumenting.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,17 @@ func (m InstrumentingMiddleware) GetCanonicalTxHash(block, txIndex uint64, evmTx
return resp, err
}

func (m InstrumentingMiddleware) GetAccountBalances(contracts []string) (resp *AccountsBalanceResponse, err error) {
defer func(begin time.Time) {
lvs := []string{"method", "GetAccountBalances", "error", fmt.Sprint(err != nil)}
m.requestCount.With(lvs...).Add(1)
m.requestLatency.With(lvs...).Observe(time.Since(begin).Seconds())
}(time.Now())

resp, err = m.next.GetAccountBalances(contracts)
return resp, err
}

func (m InstrumentingMiddleware) GetEvmCode(contract string) (resp []byte, err error) {
defer func(begin time.Time) {
lvs := []string{"method", "GetEvmCode", "error", fmt.Sprint(err != nil)}
Expand Down
12 changes: 12 additions & 0 deletions rpc/query_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1323,3 +1323,15 @@ func getTxByTendermintHash(
)
return txObj, err
}

type AccountsBalanceResponse struct {
Accounts map[string]map[string]string
}

type AccountMappingBalance struct {
DAppAddress loom.Address
ContractsBalance []struct {
Address loom.Address
Balance string
}
}
132 changes: 132 additions & 0 deletions rpc/query_server_gateway.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// +build gateway

package rpc

import (
"fmt"

"github.com/loomnetwork/go-loom"
ctypes "github.com/loomnetwork/go-loom/builtin/types/coin"
"github.com/loomnetwork/go-loom/plugin/contractpb"
"github.com/loomnetwork/loomchain/builtin/plugins/ethcoin"
"github.com/pkg/errors"

amtypes "github.com/loomnetwork/go-loom/builtin/types/address_mapper"
tgtypes "github.com/loomnetwork/go-loom/builtin/types/transfer_gateway"
glcommon "github.com/loomnetwork/go-loom/common"
gtypes "github.com/loomnetwork/go-loom/types"
am "github.com/loomnetwork/loomchain/builtin/plugins/address_mapper"
"github.com/loomnetwork/loomchain/builtin/plugins/coin"
"github.com/loomnetwork/transfer-gateway/builtin/plugins/gateway"
)

func (s *QueryServer) GetAccountBalances(contracts []string) (*AccountsBalanceResponse, error) {
snapshot := s.StateProvider.ReadOnlyState()
defer snapshot.Release()

addrMapperCtx, err := s.createAddressMapperCtx(s.StateProvider.ReadOnlyState())
if err != nil {
return nil, err
}
var resp *amtypes.AddressMapperListMappingResponse
mapper := &am.AddressMapper{}
resp, err = mapper.ListMapping(addrMapperCtx, nil)
if err != nil {
return nil, err
}

if len(resp.Mappings) == 0 {
return nil, errors.Errorf("Empty address mapping list")
}

ethcoinCtx, err := s.createStaticContractCtx(snapshot, "ethcoin")
if err != nil {
return nil, err
}

loomCoinCtx, err := s.createStaticContractCtx(snapshot, "coin")
if err != nil {
return nil, err
}

gatewayCtx, err := s.createStaticContractCtx(snapshot, "gateway")
if err != nil {
return nil, err
}

accounts := make(map[string]map[string]string, len(resp.Mappings))
for _, mp := range resp.Mappings {
atchapcyp marked this conversation as resolved.
Show resolved Hide resolved
var localAddr, foreignAddr loom.Address
if mp.From.ChainId == s.ChainID {
localAddr = loom.UnmarshalAddressPB(mp.From)
foreignAddr = loom.UnmarshalAddressPB(mp.To)
} else {
localAddr = loom.UnmarshalAddressPB(mp.To)
foreignAddr = loom.UnmarshalAddressPB(mp.From)
}
fmt.Println("from -> ", mp.From, " - ", mp.To)
account := make(map[string]string, len(resp.Mappings))
for _, contract := range contracts {
switch contract {
case "eth":
ethBal, err := getEthBalance(ethcoinCtx, localAddr)
if err != nil {
return nil, err
}
account[contract] = ethBal.String()
case "loom":
loomBal, err := getLoomBalance(loomCoinCtx, localAddr)
if err != nil {
return nil, err
}
account[contract] = loomBal.String()
default:
erc20ContractAddr, err := getMappedContractAddress(gatewayCtx, loom.MustParseAddress(contract))
if err != nil {
return nil, err
}
erc20Ctx := gateway.NewERC20StaticContext(gatewayCtx, loom.UnmarshalAddressPB(erc20ContractAddr))
erc20Bal, err := gateway.BalanceOf(erc20Ctx, localAddr)
if err != nil {
return nil, err
}
account[contract] = erc20Bal.String()
}
}
accounts[foreignAddr.String()] = account
}

return &AccountsBalanceResponse{Accounts: accounts}, nil
}

func getEthBalance(ctx contractpb.StaticContext, address loom.Address) (*glcommon.BigUInt, error) {
amount, err := ethcoin.BalanceOf(ctx, address)
if err != nil {
return nil, errors.Wrapf(err, "error getting balance of %s", address.Local.String())
}
if amount == nil {
return glcommon.BigZero(), nil
}
return amount, nil
}

func getLoomBalance(ctx contractpb.StaticContext, address loom.Address) (*glcommon.BigUInt, error) {
lc := &coin.Coin{}
amount, err := lc.BalanceOf(ctx, &ctypes.BalanceOfRequest{Owner: address.MarshalPB()})
if err != nil {
return nil, errors.Wrapf(err, "error getting balance of %s", address.Local.String())
}
if amount == nil {
return glcommon.BigZero(), nil
}
return &amount.Balance.Value, nil
}

func getMappedContractAddress(ctx contractpb.StaticContext, address loom.Address) (*gtypes.Address, error) {
var resp *tgtypes.TransferGatewayGetContractMappingResponse
resp, err := gateway.GetContractMapping(ctx, &tgtypes.TransferGatewayGetContractMappingRequest{From: address.MarshalPB()})
if err != nil {
return nil, err
}
return resp.MappedAddress, nil
}
11 changes: 11 additions & 0 deletions rpc/query_server_no_gateway.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// +build !gateway

package rpc

import (
"github.com/pkg/errors"
)

func (s *QueryServer) GetAccountBalances(contract []string) (*AccountsBalanceResponse, error) {
return nil, errors.New("GetAccountBalances not implemented in this build")
}
5 changes: 4 additions & 1 deletion rpc/query_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ type QueryService interface {
GetContractRecord(contractAddr string) (*types.ContractRecordResponse, error)
DPOSTotalStaked() (*DPOSTotalStakedResponse, error)
GetCanonicalTxHash(block, txIndex uint64, evmTxHash eth.Data) (eth.Data, error)
GetAccountBalances(contracts []string) (*AccountsBalanceResponse, error)

// deprecated function
EvmTxReceipt(txHash []byte) ([]byte, error)
Expand Down Expand Up @@ -213,7 +214,7 @@ func MakeEthQueryServiceHandler(logger log.TMLogger, hub *Hub, routes map[string
}

// MakeUnsafeQueryServiceHandler returns a http handler for unsafe RPC routes
func MakeUnsafeQueryServiceHandler(logger log.TMLogger) http.Handler {
func MakeUnsafeQueryServiceHandler(svc QueryService, logger log.TMLogger) http.Handler {
codec := amino.NewCodec()
mux := http.NewServeMux()
routes := map[string]*rpcserver.RPCFunc{}
Expand All @@ -226,6 +227,8 @@ func MakeUnsafeQueryServiceHandler(logger log.TMLogger) http.Handler {
routes["unsafe_stop_cpu_profiler"] = rpcserver.NewRPCFunc(rpccore.UnsafeStopCPUProfiler, "")
routes["unsafe_write_heap_profile"] = rpcserver.NewRPCFunc(rpccore.UnsafeWriteHeapProfile, "filename")

routes["account_balances"] = rpcserver.NewRPCFunc(svc.GetAccountBalances, "contracts")

rpcserver.RegisterRPCFuncs(mux, routes, codec, logger)
return mux
}
2 changes: 1 addition & 1 deletion rpc/rpc_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func RPCServer(

if enableUnsafeRPC {
unsafeLogger := logger.With("interface", "unsafe")
unsafeHandler := MakeUnsafeQueryServiceHandler(unsafeLogger)
unsafeHandler := MakeUnsafeQueryServiceHandler(qsvc, unsafeLogger)
unsafeListener, err := rpcserver.Listen(
unsafeRPCBindAddress,
rpcserver.Config{MaxOpenConnections: 0},
Expand Down