Skip to content

Commit

Permalink
add stafihub chain
Browse files Browse the repository at this point in the history
  • Loading branch information
asilenced committed Mar 1, 2022
1 parent 5a00e6b commit 57f1bfe
Show file tree
Hide file tree
Showing 17 changed files with 901 additions and 18 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ build:
cd cmd/chainbridge && env GOARCH=amd64 go build -o ../../build/chainbridge
cd cmd/solvault && env GOARCH=amd64 go build -o ../../build/solvault
cd cmd/soltool && env GOARCH=amd64 go build -o ../../build/soltool
cd cmd/keytool && env GOARCH=amd64 go build -o ../../build/keytool

install:
@echo " > \033[32mInstalling bridge...\033[0m "
Expand Down
186 changes: 186 additions & 0 deletions chains/stafihub/chain.go
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
}
67 changes: 67 additions & 0 deletions chains/stafihub/connection.go
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() {
}
Loading

0 comments on commit 57f1bfe

Please sign in to comment.