Skip to content

Commit eca87ac

Browse files
pedro-linoVahe9611
andauthored
feat: Add Algorand Integration (#860)
Co-authored-by: vahe9611 <vah.vahe97@gmail.com> Co-authored-by: Vahe Hakobyan <49304207+Vahe9611@users.noreply.github.com>
1 parent 566fcce commit eca87ac

File tree

131 files changed

+7341
-3
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

131 files changed

+7341
-3
lines changed
+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
name: Algorand-Icon-Integration
2+
on:
3+
push:
4+
branches: ["main"]
5+
pull_request:
6+
branches: ["main"]
7+
jobs:
8+
Algorand-Icon-Integration:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v3
12+
13+
- name: Build the Relayer
14+
working-directory: ./cmd/iconbridge
15+
run: |
16+
go build .
17+
18+
- name: Build Goloop container
19+
working-directory: ./devnet/docker/goloop
20+
run: docker build -t icon-algorand_goloop .
21+
22+
- name: Run Goloop container
23+
working-directory: ./devnet/docker/goloop
24+
run: |
25+
docker run -d \
26+
--name goloop \
27+
-p 9080:9080 \
28+
-e GOLOOP_NODE_DIR=/goloop/data/goloop \
29+
-e GOLOOP_LOG_WRITER_FILENAME=/goloop/data/log/goloop.log \
30+
-t icon-algorand_goloop
31+
32+
- name: Install Algod and friends
33+
run: |
34+
sudo mkdir /tmp/algorand
35+
sudo wget -O /tmp/algorand/algorand.tar.gz https://github.com/algorand/go-algorand/releases/download/v3.13.3-stable/node_stable_linux-amd64_3.13.3.tar.gz
36+
cd /tmp/algorand
37+
sudo tar xf algorand.tar.gz
38+
cd bin
39+
sudo mv algod goal kmd /usr/local/bin
40+
41+
- name: Run Algod
42+
working-directory: ./devnet/algorand
43+
run: |
44+
goal network create -r /tmp/testnet -t ./template.json
45+
cp ./config.json /tmp/testnet/Node
46+
cp ./algod.token /tmp/testnet/Node
47+
cp ./kmd_config.json /tmp/testnet/Node/kmd-v0.5/kmd_config.json
48+
cp ./kmd.token /tmp/testnet/Node/kmd-v0.5/kmd.token
49+
goal network start -r /tmp/testnet
50+
51+
- name: Run Algorand lifecheck test
52+
run: |
53+
go test -v pyteal/test/lifecheck_test/lifecheck_test.go
54+
55+
- name: Install goloop
56+
run: |
57+
cd /tmp
58+
wget https://github.com/icon-project/goloop/archive/refs/tags/v1.3.3.tar.gz
59+
tar xf v1.3.3.tar.gz
60+
cd goloop-1.3.3/cmd/goloop
61+
go install
62+
cd /tmp
63+
rm -rf goloop-1.3.3 v1.3.3.tar.gz
64+
65+
- name: Install PyTeal
66+
working-directory: ./pyteal
67+
run: |
68+
sudo apt update
69+
sudo apt install -y python3-pip
70+
pip install pyteal
71+
72+
- name: Compile golang tools
73+
working-directory: cmd/tools/algorand
74+
run: ./install-tools.sh
75+
76+
- name: Set up JDK and Gradle
77+
uses: actions/setup-java@v1
78+
with:
79+
java-version: 11
80+
distribution: "gradle"
81+
gradle-version: "7.6"
82+
83+
- name: Test Javascore
84+
working-directory: ./javascore
85+
run: |
86+
./gradlew wrapped-token:test
87+
88+
- name: Build ICON smart contracts
89+
working-directory: ./javascore
90+
run: |
91+
./gradlew dummyBSH:optimizedJar
92+
./gradlew bmc:optimizedJar
93+
./gradlew wrapped-token:optimizedJar
94+
./gradlew test-token:optimizedJar
95+
./gradlew escrow:optimizedJar
96+
97+
- name: Build Algorand smart contracts
98+
working-directory: pyteal
99+
run: |
100+
./build.sh bmc.bmc bmc
101+
./build.sh bsh.bsh bsh
102+
./build.sh escrow.escrow escrow
103+
./build.sh reserve.reserve reserve
104+
105+
- name: Deploy contracts, setup system and update config file
106+
working-directory: ./devnet/docker/icon-algorand
107+
run: |
108+
./setup_system.sh
109+
110+
- name: Start the Relayer
111+
working-directory: ./cmd/iconbridge
112+
run: |
113+
./iconbridge -config=../../devnet/docker/icon-algorand/algo-config.json &
114+
sleep 10
115+
116+
- name: Run integration test
117+
working-directory: ./devnet/docker/icon-algorand
118+
run: |
119+
export PATH=$PATH:~/go/bin
120+
./dbsh-integration-test.sh
121+
./a2i-integration-test.sh
122+
./i2a-integration-test.sh

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ __pycache__
66
**/*.iml
77
build/
88
devnet/docker/icon-bsc/build*/
9+
devnet/docker/icon-algorand/build*/
910
bin/
1011
**/*/node_modules/
1112
abigenBindings/
@@ -22,3 +23,7 @@ rust/near/res
2223

2324
devnet/docker/icon-bsc/local/
2425
devnet/docker/icon-bsc/data/
26+
devnet/docker/icon-algorand/local/
27+
devnet/docker/icon-algorand/cache/
28+
devnet/docker/icon-algorand/iconvalidators
29+
devnet/docker/icon-algorand/*keystore.json

cmd/iconbridge/chain/algo/bmc_abi.go

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package algo
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"io/ioutil"
8+
"path/filepath"
9+
"time"
10+
11+
"github.com/algorand/go-algorand-sdk/abi"
12+
"github.com/algorand/go-algorand-sdk/future"
13+
"github.com/algorand/go-algorand-sdk/types"
14+
)
15+
16+
const contractDir = "../../pyteal/teal/bmc/"
17+
const waitRounds = 5
18+
19+
type AbiFunc struct {
20+
name string
21+
args []interface{}
22+
}
23+
24+
func GetMethod(c *abi.Contract, name string) (abi.Method, error) {
25+
m, err := c.GetMethodByName(name)
26+
if err != nil {
27+
return abi.Method{}, err
28+
}
29+
return m, nil
30+
}
31+
32+
func Combine(mcp future.AddMethodCallParams, m abi.Method,
33+
a []interface{}) future.AddMethodCallParams {
34+
mcp.Method = m
35+
mcp.MethodArgs = a
36+
return mcp
37+
}
38+
39+
func (s *sender) initAbi() error {
40+
abiPath, err := filepath.Abs(contractDir + "contract.json")
41+
if err != nil {
42+
return fmt.Errorf("Couldn't retrieve abi file: %w", err)
43+
}
44+
rawBmc, err := ioutil.ReadFile(abiPath)
45+
if err != nil {
46+
return fmt.Errorf("Failed to open contract file: %w", err)
47+
}
48+
abiBmc := &abi.Contract{}
49+
if err = json.Unmarshal(rawBmc, abiBmc); err != nil {
50+
return fmt.Errorf("Failed to marshal abi contract: %w", err)
51+
}
52+
s.bmc = abiBmc
53+
54+
ctx, _ := context.WithTimeout(context.Background(), 60*time.Second)
55+
sp, err := s.cl.algod.SuggestedParams().Do(ctx)
56+
if err != nil {
57+
return fmt.Errorf("Failed to get suggeted params: %w", err)
58+
}
59+
60+
sp.Fee = 1000
61+
s.mcp = &future.AddMethodCallParams{
62+
AppID: s.opts.BmcId,
63+
Sender: s.wallet.TypedAddress(),
64+
SuggestedParams: sp,
65+
OnComplete: types.NoOpOC,
66+
Signer: s.wallet,
67+
}
68+
return nil
69+
}
70+
71+
func (s *sender) callAbi(ctx context.Context, abiFuncs ...AbiFunc) (future.ExecuteResult, error) {
72+
var atc = future.AtomicTransactionComposer{}
73+
for _, abiFunc := range abiFuncs {
74+
method, err := GetMethod(s.bmc, abiFunc.name)
75+
if err != nil {
76+
return future.ExecuteResult{}, fmt.Errorf("Failed to get %s method from json contract: %w",
77+
abiFunc.name, err)
78+
}
79+
80+
err = atc.AddMethodCall(Combine(*s.mcp, method, abiFunc.args))
81+
if err != nil {
82+
return future.ExecuteResult{}, fmt.Errorf("Failed to add %s method to atc: %w", abiFunc.name, err)
83+
}
84+
}
85+
86+
ret, err := atc.Execute(s.cl.algod, ctx, waitRounds)
87+
if err != nil {
88+
return future.ExecuteResult{}, fmt.Errorf("Failed to execute atc: %w", err)
89+
}
90+
return ret, nil
91+
}

cmd/iconbridge/chain/algo/client.go

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package algo
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"math/big"
7+
"time"
8+
9+
"github.com/algorand/go-algorand-sdk/client/v2/algod"
10+
"github.com/algorand/go-algorand-sdk/client/v2/common/models"
11+
"github.com/algorand/go-algorand-sdk/future"
12+
"github.com/algorand/go-algorand-sdk/types"
13+
"github.com/icon-project/icon-bridge/cmd/iconbridge/chain"
14+
"github.com/icon-project/icon-bridge/common/log"
15+
)
16+
17+
const (
18+
AlgoBlockRate = 4
19+
BlockRetryLimit = 5
20+
)
21+
22+
type Client struct {
23+
log log.Logger
24+
algod *algod.Client
25+
}
26+
27+
type IClient interface {
28+
Log() log.Logger
29+
GetBalance(ctx context.Context, hexAddr string) (*big.Int, error)
30+
WaitForTransaction(ctx context.Context, txId string) (models.PendingTransactionInfoResponse, error)
31+
GetLatestRound(ctx context.Context) (uint64, error)
32+
GetBlockbyRound(ctx context.Context, round uint64) (block *types.Block, err error)
33+
DecodeBtpMsg(log string) (*chain.Event, error)
34+
}
35+
36+
func newClient(algodAccess []string, l log.Logger) (*Client, error) {
37+
algodClient, err := algod.MakeClient(algodAccess[0], algodAccess[1])
38+
if err != nil {
39+
return nil, err
40+
}
41+
cli := &Client{
42+
log: l,
43+
algod: algodClient,
44+
}
45+
return cli, nil
46+
}
47+
48+
func (cl *Client) WaitForTransaction(ctx context.Context, txId string) (models.PendingTransactionInfoResponse, error) {
49+
return future.WaitForConfirmation(cl.algod, txId, BlockRetryLimit, ctx)
50+
}
51+
52+
func (cl *Client) GetBalance(ctx context.Context, walletAddr string) (*big.Int, error) {
53+
accountInfo, err := cl.algod.AccountInformation(walletAddr).Do(ctx)
54+
if err != nil {
55+
return nil, err
56+
} else {
57+
return new(big.Int).SetUint64(accountInfo.Amount), nil
58+
}
59+
}
60+
61+
func (c *Client) Log() log.Logger {
62+
return c.log
63+
}
64+
65+
// get latest block round
66+
func (cl *Client) GetLatestRound(ctx context.Context) (uint64, error) {
67+
sta, err := cl.algod.Status().Do(ctx)
68+
return sta.LastRound, err
69+
}
70+
71+
// get latest block number
72+
func (cl *Client) GetBlockbyRound(ctx context.Context, round uint64) (*types.Block, error) {
73+
for i := 1; i <= BlockRetryLimit; i++ {
74+
block, err := cl.algod.Block(round).Do(ctx)
75+
if err != nil {
76+
time.Sleep(AlgoBlockRate * time.Second)
77+
continue
78+
}
79+
return &block, nil
80+
}
81+
err := fmt.Errorf("GetBlock reached retry limit")
82+
return nil, err
83+
}
84+
85+
// get latest block round
86+
func (cl *Client) GetBlockHash(ctx context.Context, round uint64) (string, error) {
87+
hash, err := cl.algod.GetBlockHash(round).Do(ctx)
88+
if err != nil {
89+
return "", fmt.Errorf("Failed to get block hash: %v", err)
90+
}
91+
return hash.Blockhash, nil
92+
}

0 commit comments

Comments
 (0)