forked from ChainSafe/ChainBridge
-
Notifications
You must be signed in to change notification settings - Fork 0
/
chain.go
145 lines (117 loc) 路 3.86 KB
/
chain.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// Copyright 2020 ChainSafe Systems
// 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, Nonfungible, and generic.
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, it constructs proposals. If a proposal is still active, the writer will attempt to vote on it. Resource IDs are resolved to method name on-chain, which are then used in the proposals when constructing the resulting Call struct.
*/
package substrate
import (
"github.com/PolkaFoundry/chainbridge-utils/blockstore"
"github.com/PolkaFoundry/chainbridge-utils/core"
"github.com/PolkaFoundry/chainbridge-utils/crypto/sr25519"
"github.com/PolkaFoundry/chainbridge-utils/keystore"
metrics "github.com/PolkaFoundry/chainbridge-utils/metrics/types"
"github.com/PolkaFoundry/chainbridge-utils/msg"
"github.com/ChainSafe/log15"
)
var _ core.Chain = &Chain{}
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
}
// 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 InitializeChain(cfg *core.ChainConfig, logger log15.Logger, sysErr chan<- error, m *metrics.ChainMetrics) (*Chain, error) {
kp, err := keystore.KeypairFromAddress(cfg.From, keystore.SubChain, cfg.KeystorePath, cfg.Insecure)
if err != nil {
return nil, err
}
krp := kp.(*sr25519.Keypair).AsKeyringPair()
// Attempt to load latest block
bs, err := blockstore.NewBlockstore(cfg.BlockstorePath, cfg.Id, kp.Address())
if err != nil {
return nil, err
}
startBlock := parseStartBlock(cfg)
if !cfg.FreshStart {
startBlock, err = checkBlockstore(bs, startBlock)
if err != nil {
return nil, err
}
}
stop := make(chan int)
// Setup connection
conn := NewConnection(cfg.Endpoint, cfg.Name, krp, logger, stop, sysErr)
err = conn.Connect()
if err != nil {
return nil, err
}
err = conn.checkChainId(cfg.Id)
if err != nil {
return nil, err
}
if cfg.LatestBlock {
curr, err := conn.api.RPC.Chain.GetHeaderLatest()
if err != nil {
return nil, err
}
startBlock = uint64(curr.Number)
}
ue := parseUseExtended(cfg)
// Setup listener & writer
l := NewListener(conn, cfg.Name, cfg.Id, startBlock, logger, bs, stop, sysErr, m)
w := NewWriter(conn, logger, sysErr, m, ue)
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
}
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) LatestBlock() metrics.LatestBlock {
return c.listener.latestBlock
}
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)
}