forked from ChainSafe/ChainBridge
-
Notifications
You must be signed in to change notification settings - Fork 0
/
chain.go
225 lines (184 loc) 路 6.14 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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
// Copyright 2020 ChainSafe Systems
// SPDX-License-Identifier: LGPL-3.0-only
/*
The ethereum package contains the logic for interacting with ethereum chains.
There are 3 major components: the connection, the listener, and the writer.
The currently supported transfer types are Fungible (ERC20), Non-Fungible (ERC721), and generic.
Connection
The connection contains the ethereum RPC client and can be accessed by both the writer and listener.
Listener
The listener polls for each new block and looks for deposit events in the bridge contract. If a deposit occurs, the listener will fetch additional information from the handler before constructing a message and forwarding it to the router.
Writer
The writer recieves the message and creates a proposals on-chain. Once a proposal is made, the writer then watches for a finalization event and will attempt to execute the proposal if a matching event occurs. The writer skips over any proposals it has already seen.
*/
package ethereum
import (
"fmt"
"math/big"
bridge "github.com/PolkaFoundry/ChainBridge/bindings/Bridge"
erc20Handler "github.com/PolkaFoundry/ChainBridge/bindings/ERC20Handler"
erc721Handler "github.com/PolkaFoundry/ChainBridge/bindings/ERC721Handler"
"github.com/PolkaFoundry/ChainBridge/bindings/GenericHandler"
connection "github.com/PolkaFoundry/ChainBridge/connections/ethereum"
utils "github.com/PolkaFoundry/ChainBridge/shared/ethereum"
"github.com/PolkaFoundry/chainbridge-utils/blockstore"
"github.com/PolkaFoundry/chainbridge-utils/core"
"github.com/PolkaFoundry/chainbridge-utils/crypto/secp256k1"
"github.com/PolkaFoundry/chainbridge-utils/keystore"
metrics "github.com/ChainSafe/chainbridge-utils/metrics/types"
"github.com/PolkaFoundry/chainbridge-utils/msg"
"github.com/ChainSafe/log15"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
)
var _ core.Chain = &Chain{}
var _ Connection = &connection.Connection{}
type Connection interface {
Connect() error
Keypair() *secp256k1.Keypair
Opts() *bind.TransactOpts
CallOpts() *bind.CallOpts
LockAndUpdateOpts() error
UnlockOpts()
Client() *ethclient.Client
EnsureHasBytecode(address common.Address) error
LatestBlock() (*big.Int, error)
WaitForBlock(block *big.Int, delay *big.Int) error
Close()
}
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 cfg.startBlock, then cfg.startBlock is replaced with the latest known block.
func setupBlockstore(cfg *Config, kp *secp256k1.Keypair) (*blockstore.Blockstore, error) {
bs, err := blockstore.NewBlockstore(cfg.blockstorePath, cfg.id, kp.Address())
if err != nil {
return nil, err
}
if !cfg.freshStart {
latestBlock, err := bs.TryLoadLatestBlock()
if err != nil {
return nil, err
}
if latestBlock.Cmp(cfg.startBlock) == 1 {
cfg.startBlock = latestBlock
}
}
return bs, nil
}
func InitializeChain(chainCfg *core.ChainConfig, logger log15.Logger, sysErr chan<- error, m *metrics.ChainMetrics) (*Chain, error) {
cfg, err := parseChainConfig(chainCfg)
if err != nil {
return nil, err
}
kpI, err := keystore.KeypairFromAddress(cfg.from, keystore.EthChain, cfg.keystorePath, chainCfg.Insecure)
if err != nil {
return nil, err
}
kp, _ := kpI.(*secp256k1.Keypair)
bs, err := setupBlockstore(cfg, kp)
if err != nil {
return nil, err
}
stop := make(chan int)
conn := connection.NewConnection(cfg.endpoint, cfg.http, kp, logger, cfg.gasLimit, cfg.maxGasPrice, cfg.gasMultiplier, cfg.egsApiKey, cfg.egsSpeed)
err = conn.Connect()
if err != nil {
return nil, err
}
err = conn.EnsureHasBytecode(cfg.bridgeContract)
if err != nil {
return nil, err
}
if cfg.erc20HandlerContract != utils.ZeroAddress {
err = conn.EnsureHasBytecode(cfg.erc20HandlerContract)
if err != nil {
return nil, err
}
}
if cfg.genericHandlerContract != utils.ZeroAddress {
err = conn.EnsureHasBytecode(cfg.genericHandlerContract)
if err != nil {
return nil, err
}
}
bridgeContract, err := bridge.NewBridge(cfg.bridgeContract, conn.Client())
if err != nil {
return nil, err
}
chainId, err := bridgeContract.ChainID(conn.CallOpts())
if err != nil {
return nil, err
}
if chainId != uint8(chainCfg.Id) {
return nil, fmt.Errorf("chainId (%d) and configuration chainId (%d) do not match", chainId, chainCfg.Id)
}
erc20HandlerContract, err := erc20Handler.NewERC20Handler(cfg.erc20HandlerContract, conn.Client())
if err != nil {
return nil, err
}
erc721HandlerContract, err := erc721Handler.NewERC721Handler(cfg.erc721HandlerContract, conn.Client())
if err != nil {
return nil, err
}
genericHandlerContract, err := GenericHandler.NewGenericHandler(cfg.genericHandlerContract, conn.Client())
if err != nil {
return nil, err
}
if chainCfg.LatestBlock {
curr, err := conn.LatestBlock()
if err != nil {
return nil, err
}
cfg.startBlock = curr
}
listener := NewListener(conn, cfg, logger, bs, stop, sysErr, m)
listener.setContracts(bridgeContract, erc20HandlerContract, erc721HandlerContract, genericHandlerContract)
writer := NewWriter(conn, cfg, logger, stop, sysErr, m)
writer.setContract(bridgeContract)
return &Chain{
cfg: chainCfg,
conn: conn,
writer: writer,
listener: listener,
stop: stop,
}, nil
}
func (c *Chain) SetRouter(r *core.Router) {
r.Listen(c.cfg.Id, c.writer)
c.listener.setRouter(r)
}
func (c *Chain) Start() error {
err := c.listener.start()
if err != nil {
return err
}
err = c.writer.start()
if err != nil {
return err
}
c.writer.log.Debug("Successfully started chain")
return nil
}
func (c *Chain) Id() msg.ChainId {
return c.cfg.Id
}
func (c *Chain) Name() string {
return c.cfg.Name
}
func (c *Chain) LatestBlock() metrics.LatestBlock {
return c.listener.latestBlock
}
// Stop signals to any running routines to exit
func (c *Chain) Stop() {
close(c.stop)
if c.conn != nil {
c.conn.Close()
}
}