-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
901 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
// Copyright 2020 Stafi Protocol | ||
// SPDX-License-Identifier: LGPL-3.0-only | ||
|
||
/* | ||
The substrate package contains the logic for interacting with substrate chains. | ||
The current supported transfer types are Fungible. | ||
There are 3 major components: the connection, the listener, and the writer. | ||
Connection | ||
The Connection handles connecting to the substrate client, and submitting transactions to the client. | ||
It also handles state queries. The connection is shared by the writer and listener. | ||
Listener | ||
The substrate listener polls blocks and parses the associated events for the three transfer types. It then forwards these into the router. | ||
Writer | ||
As the writer receives messages from the router, nothing happened. | ||
*/ | ||
package stafihub | ||
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/ChainSafe/log15" | ||
"github.com/shopspring/decimal" | ||
"github.com/stafiprotocol/chainbridge/utils/blockstore" | ||
"github.com/stafiprotocol/chainbridge/utils/core" | ||
"github.com/stafiprotocol/chainbridge/utils/msg" | ||
) | ||
|
||
type Chain struct { | ||
cfg *core.ChainConfig // The config of the chain | ||
conn *Connection // THe chains connection | ||
listener *listener // The listener of this chain | ||
writer *writer // The writer of the chain | ||
stop chan<- int | ||
} | ||
|
||
func InitializeChain(cfg *core.ChainConfig, logger log15.Logger, sysErr chan<- error) (*Chain, error) { | ||
decimals, err := getDecimals(cfg) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
stop := make(chan int) | ||
conn, err := NewConnection(cfg, logger, stop) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Attempt to load latest block | ||
bs, err := blockstore.NewBlockstore(cfg.BlockstorePath, cfg.Id, conn.Address()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
startBlock := parseStartBlock(cfg) | ||
if !cfg.FreshStart { | ||
startBlock, err = checkBlockstore(bs, startBlock) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
|
||
if cfg.LatestBlock { | ||
curr, err := conn.LatestBlockNumber() | ||
if err != nil { | ||
return nil, err | ||
} | ||
startBlock = curr | ||
} | ||
|
||
// Setup listener & writer | ||
l := NewListener(conn, cfg.Name, cfg.Id, startBlock, logger, bs, stop, sysErr, decimals) | ||
w := NewWriter(conn, logger, sysErr, stop, decimals) | ||
return &Chain{cfg: cfg, conn: conn, listener: l, writer: w, stop: stop}, nil | ||
} | ||
|
||
func (c *Chain) Start() error { | ||
err := c.listener.start() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = c.writer.start() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
c.conn.log.Debug("Successfully started chain", "chainId", c.cfg.Id) | ||
return nil | ||
} | ||
|
||
func (c *Chain) SetRouter(r *core.Router) { | ||
r.Listen(c.cfg.Id, c.writer) | ||
c.listener.setRouter(r) | ||
} | ||
|
||
func (c *Chain) Id() msg.ChainId { | ||
return c.cfg.Id | ||
} | ||
|
||
func (c *Chain) Name() string { | ||
return c.cfg.Name | ||
} | ||
|
||
func (c *Chain) Stop() { | ||
close(c.stop) | ||
} | ||
|
||
// checkBlockstore queries the blockstore for the latest known block. If the latest block is | ||
// greater than startBlock, then the latest block is returned, otherwise startBlock is. | ||
func checkBlockstore(bs *blockstore.Blockstore, startBlock uint64) (uint64, error) { | ||
latestBlock, err := bs.TryLoadLatestBlock() | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
if latestBlock.Uint64() > startBlock { | ||
return latestBlock.Uint64(), nil | ||
} else { | ||
return startBlock, nil | ||
} | ||
} | ||
|
||
func parseStartBlock(cfg *core.ChainConfig) uint64 { | ||
if blk, ok := cfg.Opts["startBlock"]; ok { | ||
res, err := strconv.ParseUint(blk, 10, 32) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return res | ||
} | ||
return 0 | ||
} | ||
|
||
func getDecimals(cfg *core.ChainConfig) (map[string]decimal.Decimal, error) { | ||
symbols := cfg.Symbols | ||
if len(symbols) == 0 { | ||
return nil, fmt.Errorf("config does not contains symbols") | ||
} | ||
|
||
decimals := make(map[string]decimal.Decimal) | ||
for _, sym := range symbols { | ||
info, ok := sym.(map[string]interface{}) | ||
if !ok { | ||
return nil, fmt.Errorf("symbol not a string map") | ||
} | ||
|
||
rId, ok := info["resourceId"].(string) | ||
if !ok { | ||
return nil, fmt.Errorf("unable to get symbol resourceId") | ||
} | ||
if rId != decimalDefault { | ||
rId = strings.ToLower(rId) | ||
} | ||
if strings.Contains(rId, "0x") { | ||
return nil, fmt.Errorf("resourceId should not with prefix 0x") | ||
} | ||
if rId != decimalDefault && len(rId) != 64 { | ||
return nil, fmt.Errorf("resourceId length must be 64") | ||
} | ||
|
||
df, ok := info["decimalFactor"].(string) | ||
if !ok { | ||
return nil, fmt.Errorf("unable to get symbol decimalFactor") | ||
} | ||
|
||
dDeci, err := decimal.NewFromString(df) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
decimals[rId] = dDeci | ||
} | ||
fmt.Println(decimals) | ||
|
||
return decimals, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// Copyright 2020 Stafi Protocol | ||
// SPDX-License-Identifier: LGPL-3.0-only | ||
|
||
package stafihub | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
"github.com/ChainSafe/log15" | ||
"github.com/cosmos/cosmos-sdk/crypto/keyring" | ||
"github.com/cosmos/cosmos-sdk/types" | ||
"github.com/stafiprotocol/chainbridge/shared/stafihub" | ||
"github.com/stafiprotocol/chainbridge/utils/core" | ||
) | ||
|
||
type Connection struct { | ||
url string // API endpoint | ||
name string // Chain name | ||
client *stafihub.Client | ||
stop <-chan int // Signals system shutdown, should be observed in all selects and loops | ||
log log15.Logger | ||
} | ||
|
||
func NewConnection(cfg *core.ChainConfig, log log15.Logger, stop <-chan int) (*Connection, error) { | ||
log.Info("NewConnection", "name", cfg.Name, "KeystorePath", cfg.KeystorePath, "Endpoint", cfg.Endpoint) | ||
fmt.Printf("Will open stafihub wallet from <%s>. \nPlease ", cfg.KeystorePath) | ||
key, err := keyring.New(types.KeyringServiceName(), keyring.BackendFile, cfg.KeystorePath, os.Stdin) | ||
if err != nil { | ||
return nil, err | ||
} | ||
account := cfg.Opts["account"] | ||
gasPrice := cfg.Opts["gasPrice"] | ||
client, err := stafihub.NewClient(key, account, gasPrice, cfg.Endpoint) | ||
if err != nil { | ||
return nil, fmt.Errorf("hubClient.NewClient err: %s", err) | ||
} | ||
|
||
return &Connection{ | ||
url: cfg.Endpoint, | ||
name: cfg.Name, | ||
client: client, | ||
stop: stop, | ||
log: log, | ||
}, nil | ||
} | ||
|
||
func (c *Connection) Address() string { | ||
return c.client.GetFromAddress().String() | ||
} | ||
|
||
func (c *Connection) LatestBlockNumber() (uint64, error) { | ||
blockHeight, err := c.client.GetCurrentBlockHeight() | ||
return uint64(blockHeight), err | ||
} | ||
|
||
func (c *Connection) FinalizedBlockNumber() (uint64, error) { | ||
blockHeight, err := c.client.GetCurrentBlockHeight() | ||
return uint64(blockHeight - 6), err | ||
} | ||
|
||
func (c *Connection) GetEvents(blockNum uint64) ([]*types.TxResponse, error) { | ||
return c.client.GetBlockTxs(int64(blockNum)) | ||
} | ||
|
||
func (c *Connection) Close() { | ||
} |
Oops, something went wrong.