Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: icon-bridge integration with Tezos #868

Open
wants to merge 230 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
230 commits
Select commit Hold shift + click to select a range
bb3fd40
bts periphery initial translation
simusud Mar 14, 2023
756dff6
added string library
simusud Mar 19, 2023
3e47ca3
initial commit
Anatehc Mar 20, 2023
4e5d8b7
initial commit for tezos bridge
Mar 20, 2023
ad808b9
fix: log type to common log
Mar 20, 2023
32b0aa7
fix: src and dst address type in NewReceiver function from tezos.Addr…
Mar 20, 2023
6cb10cd
fix: changed src and dst address type from tezos.Address to BTPAddres…
Mar 20, 2023
dfb5567
feat: added new file for registering into icon-bridge
Mar 20, 2023
5c57c72
feat: transfer functions
Anatehc Mar 21, 2023
510ca49
fix: remove redundant function
Anatehc Mar 21, 2023
1c02d17
feat(bmc): Add bmc periphery functions and libraries
simusud Mar 28, 2023
b898028
feat(bmc): Add bmc management functions
simusud Mar 28, 2023
4e6e8ad
Utils file added
Suyog007 Mar 29, 2023
834cc80
fix(bmc): Implement sets instead of lists
simusud Mar 29, 2023
27244d3
fix: interscore calls
Anatehc Mar 31, 2023
ee0a4af
feat(bts): Add bts core functions
simusud Apr 5, 2023
85aeefc
Adding deployment file
simusud Apr 5, 2023
b25004f
fix(bts_periphery): Function changed to onchain view
simusud Apr 5, 2023
c0f821e
Adding libraries in Types.py
simusud Apr 5, 2023
c90e1d4
Merge branch 'bts-periphery' into bmc-periphery
simusud Apr 5, 2023
7b0dc39
fix(bmc): Deployment errors fixes in bmc periphery, bmc management an…
simusud Apr 6, 2023
002b06f
feat: added new client methods
Apr 11, 2023
92f6f0c
feat: added verifier synchronization feature and message delivery fea…
Apr 11, 2023
f5f5263
feat: added logic to Receipt() and Status()
Apr 11, 2023
ba5da24
feat: added block signature verification feature
Apr 11, 2023
6608cb6
feat: added BlockNotification struct for receiving BTPMessage
Apr 11, 2023
bcafabc
fix(bts): zero address check fixes
simusud Apr 12, 2023
822c97b
fix(bts): small fixes
simusud Apr 12, 2023
b95002e
feat(FA2): added params and view methods
simusud Apr 12, 2023
8ca90a9
fix(bmc): added checks if a map contains a key
simusud Apr 18, 2023
7374d32
fix(lib): return value fix in String.py library
simusud Apr 18, 2023
901f6dd
feat(bmc): added RLP encode library
simusud Apr 18, 2023
b0a9e84
feat(bmc): implement RLP encode library
simusud Apr 19, 2023
45ff99c
fix(bmc): view method fixes
simusud Apr 19, 2023
617f0e8
feat(bts): implement allowance and transfer from method
simusud Apr 20, 2023
8fe429b
feat(FA2): add allowance and transfer from method
simusud Apr 20, 2023
e2d5107
feat(library): add rlp encode library
simusud Apr 20, 2023
e513455
fix(bts): type fix in payment_transfer function
simusud Apr 21, 2023
9bad530
fix(btsOwnerManager): issues fixes
simusud Apr 21, 2023
2dd10bf
fix(btsOwnerManager): small fix
simusud Apr 21, 2023
8314229
fix(bts_periphery): parse address and RLP encode implementation
simusud Apr 26, 2023
b95a2d9
fix(library): data type fixes
simusud Apr 26, 2023
8d9275b
feat(library): added parse address library
simusud Apr 26, 2023
3034991
Merge branch 'bts-periphery' into bmc-periphery
simusud Apr 26, 2023
50b868a
rlp decode library added
Suyog007 May 3, 2023
e665b29
fix(library): rlp decode functions added
Suyog007 May 3, 2023
f53e691
feat(bts_core): added callback implementation and uncommented checks …
simusud May 3, 2023
ed63ee7
feat(bts_periphery): added rlp decode implementation and uncommented …
simusud May 3, 2023
998b534
feat(FA2): callback implementation in transfer function
simusud May 3, 2023
594ea3f
fix(library): small fixes
simusud May 3, 2023
1af2c95
Merge branch 'bts-periphery' into bmc-periphery
simusud May 4, 2023
fc60d5b
feat(library): added decode and parse address library
simusud May 4, 2023
7379421
fix(bmc_management): contract initialization fixes
simusud May 4, 2023
0f41b35
feat(bmc_periphery): rlp decode library implementation
simusud May 4, 2023
bb1641d
Merge branch 'icon-project:main' into bts-periphery
simusud May 4, 2023
fff55e7
fix(bts_core): minor fixes
simusud May 5, 2023
72c6a95
fix(bmc): contract size issue fixed by splitting libraries into separ…
simusud May 11, 2023
6a00aa1
fix(bts): contract size issue fixed by splitting libraries into separ…
simusud May 11, 2023
f70e635
fix: small fixes
simusud May 12, 2023
40673a9
feat: added receipt filter
icondev22 May 15, 2023
ff5f735
feat: receiveloop added
icondev22 May 15, 2023
5002a7a
fix: rlp encoding on message sends
icondev22 May 15, 2023
0087958
fix(bmc): rlp encoding fixed
Suyog007 May 15, 2023
93ecb9c
fix: added new fields in BlockNotification struct
icondev22 May 15, 2023
2c87c61
fix(bts): rlp encoding fixed and encode_service_message parameter typ…
Suyog007 May 15, 2023
6a9b5f0
feat: added height() method in Verifier
icondev22 May 15, 2023
fbeafed
fix(bts): rlp decoding fixed
Suyog007 May 16, 2023
7c1f3b6
fix(bmc): rlp decoding fixed
Suyog007 May 16, 2023
d0a0c98
fix(bmc,bts): return type fixed for without length prefix
Suyog007 May 16, 2023
109b8fc
Merge branch 'bts-periphery' into tezos-bridge
icondev22 May 16, 2023
a673451
fix(bmc): modified type of sn from nat to string
simusud May 16, 2023
d915d12
feat(bmc): modified type of sn from nat to int and implemented callba…
simusud May 16, 2023
fe09061
feat: tezos contracts deployment script
icondev22 May 17, 2023
75a6261
add(bmc): setters added to set bmc management address
Suyog007 May 17, 2023
fb303cc
add(bts): setters added to set bmc address and bts core address
Suyog007 May 17, 2023
26ee721
fix(bmc): decode receipt proof fixed
Suyog007 May 18, 2023
e3511e3
fix: icon tezos relay integration
icondev22 May 18, 2023
f477d62
Merge branch 'bts-periphery' into tezos-bridge
icondev22 May 18, 2023
40d12d2
fix(bmc): decoding negative value of sn fixed and new contract added …
Suyog007 May 18, 2023
78c57c2
feat(bts): try catch implementation through callback
simusud May 18, 2023
1d9714c
fix(bmc:library): sn type fix in RLP_decode_struct.py
simusud May 18, 2023
10c3c7a
fix(bts): small fixes
simusud May 19, 2023
5dce489
fix(bmc): decoding list only if the given bytes is in list form.
Suyog007 May 19, 2023
d3ef321
fix(bts): decoding list only if the given bytes is in list form.
Suyog007 May 19, 2023
62fa044
feat: added support for tezos wallet
icondev22 May 21, 2023
16c1b46
fix: icon tezos bridge integration
icondev22 May 21, 2023
b1f6282
Merge branch 'bts-periphery' into tezos-bridge
icondev22 May 21, 2023
abfbeba
fix: tezos iconbridge integration
icondev22 May 29, 2023
af1f8fe
fix: icon tezos integration
icondev22 Jun 1, 2023
0a12fab
feat: icon-bridge integration with Tezos
simusud Mar 14, 2023
827ca2e
fix(bts:library): fixed decode_transfer_coin_msg function
simusud Jun 5, 2023
ece3793
fix(bts:library): fixed encode_transfer_coin_msg function
simusud Jun 5, 2023
82ce583
fix(bts): fixed transfer fees function and added default value while …
simusud Jun 9, 2023
f6dbc5e
perf(bmc): improved performance by splitting rlp encode and decode in…
simusud Jun 12, 2023
2e24bed
perf(bts): improved performance by splitting rlp encode and decode in…
simusud Jun 12, 2023
c94d35e
perf(bmc): rlp encoder and decoder merged into one contract.
Suyog007 Jun 13, 2023
d62e4ad
perf(bts): rlp encoder and decoder merged into a single contract.
Suyog007 Jun 13, 2023
77d9632
feat(bmc): added upgradable feature in contract functions
simusud Jun 13, 2023
be0cc10
feat(bts): added upgradable feature in contract functions
simusud Jun 13, 2023
4e233d3
fix(bmc): added default value while reading value from map
simusud Jun 13, 2023
1dac429
fix(bts): fixes in reclaim and mint functions
simusud Jun 13, 2023
4cb8e77
fix(bts): fixes bts_core.py file size issue by removing some onchain …
simusud Jun 13, 2023
0f969ab
fix(bts): fixed variant issue in bts_periphery.py
simusud Jun 14, 2023
ad26a67
refactor(bmc): updated rlp contract address
simusud Jun 14, 2023
364f317
refactor(bts): updated rlp contract address
simusud Jun 14, 2023
75f3116
refactor(bts): changed record key name in RLP_struct.py
simusud Jun 15, 2023
d5f28c9
refactor(bts): updated rlp contract address in bts_periphery.py
simusud Jun 15, 2023
15e6ee5
fix(bmc): decode_response fixed in case of error tx
simusud Jun 15, 2023
fa10f8a
feat: added GetBMCManagement() for getting bmc_management address
icondev22 Jun 19, 2023
e85ba7a
feat: tezos wallet creation script
icondev22 Jun 19, 2023
5bad7c4
refactor(bmc): rlp_contract address update
simusud Jun 19, 2023
703848d
fix(bts): removed callback from transfer in bts_core.py and checked t…
simusud Jun 19, 2023
6380df9
fix: inclusion of newer packages
icondev22 Jun 20, 2023
906dd0b
fix: added CustomCall() for fixing the incorrect simulated gas and st…
icondev22 Jun 20, 2023
edb574f
refactor(bmc): updated library addresses
simusud Jun 21, 2023
cac9040
fix(bmc): encode_bmc_message fix for negative encoding
simusud Jun 21, 2023
964ff22
revert(bmc): changed RLP contract back to a library
simusud Jun 23, 2023
63a3e94
revert(bts): changed RLP contract back to a library
simusud Jun 23, 2023
2a41a78
fix: event filter for events present in the same blocks
icondev22 Jun 23, 2023
086e0fc
feat: syncverifier and receiveloop in same function
icondev22 Jun 23, 2023
17cc124
fix: endorsement threshold calculation
icondev22 Jun 23, 2023
21d920c
fix: type of tezos blockhash
icondev22 Jun 23, 2023
25ea9b4
fix: new config for new env
icondev22 Jun 23, 2023
fffcd24
feat(bts): implemented callback in balance_of
simusud Jun 23, 2023
7c15d62
perf(bmc): try catch added on rlp decoding
Suyog007 Jun 26, 2023
63d86a4
perf(bts): try catch added on rlp decoding
Suyog007 Jun 26, 2023
1eb70e0
fix(bts): get_balance_of used instead of balance_of
Suyog007 Jun 26, 2023
ebcc23e
fix(bts): removed balance_of function
Suyog007 Jun 26, 2023
3c5bd9b
feat: added consensus key getter for alternate wallets
icondev22 Jun 27, 2023
88a6040
fix: icon - tezos bridge integration
icondev22 Jun 27, 2023
9d7bc5d
Merge branch 'transfer-coin-msg-fix' into tezos-bridge
icondev22 Jun 27, 2023
87cd646
fix(bts): unregistered coin handled
simusud Jun 27, 2023
6f4bfb4
perf(bmc): internal review changes
simusud Jun 30, 2023
f243214
perf(bts): internal review changes
simusud Jun 30, 2023
f98461e
fix(bts): onchain view return type fixed
Suyog007 Jul 3, 2023
88594d9
fix(bts): negative amount check skipped if caller is bts core .
Suyog007 Jul 3, 2023
8efd9ca
style(bts): code formatting
simusud Jul 4, 2023
3c74516
style(bmc): code formatting
simusud Jul 4, 2023
b8fdb21
fix(bmc): code review changes
simusud Jul 4, 2023
bec8a4c
fix(bmc): removed pack
simusud Jul 5, 2023
8192012
fix(bts): removed pack
simusud Jul 5, 2023
76476b4
docs(bmc): added comment in string split
simusud Jul 5, 2023
ea18b39
test(bmc): unit test fixes
simusud Jul 5, 2023
c2dd79b
test(bts): unit test fixes
simusud Jul 5, 2023
2d81c9e
fix: tezos to icon bridge integraton changes
icondev22 Jul 6, 2023
0d9d1eb
fix: tezos icon bridge integration fixes
icondev22 Jul 6, 2023
f525318
Merge branch 'transfer-coin-msg-fix' into tezos-bridge
icondev22 Jul 6, 2023
bb60dc2
fix: tezos icon bridge integration
icondev22 Jul 6, 2023
1c84b0b
fix: tezos icon integration fixes
icondev22 Jul 7, 2023
656ef0e
fix(bmc): removed redundant params
simusud Jul 7, 2023
6f20dab
perf(bmc): review changes
simusud Jul 7, 2023
feacc97
test(bmc): integration test update
simusud Jul 7, 2023
2df1831
fix: tezos verifier turned off
icondev22 Jul 9, 2023
0d87810
test(bmc): variable name change and integration test update
simusud Jul 9, 2023
ba527ac
style(bts): typo fix
simusud Jul 9, 2023
214a8cb
fix(bts): verifying bts core token balance before burning token.
Suyog007 Jul 10, 2023
4711088
test(bmc): updated integration test and added fa2 dummy contract
simusud Jul 11, 2023
dabb6d7
perf(bts): rlp struct library optimized.
Suyog007 Jul 13, 2023
2092c4a
perf(bmc): rlp struct library optimized.
Suyog007 Jul 13, 2023
26a8d1b
perf(bts): added update functions and removed integration test file
simusud Jul 13, 2023
e92afd2
perf(bmc): review changes and removed parse address file
simusud Jul 13, 2023
d33c50b
test(bmc): message modified in integration test.
Suyog007 Jul 13, 2023
7f92b49
perf(bmc): typo fixed.
Suyog007 Jul 13, 2023
e567ba8
test(bmc): owner address removed from bmc periphery integration test.
Suyog007 Jul 13, 2023
a254ae7
test(bmc): unit test fixes
simusud Jul 14, 2023
5ee0f95
fix(bmc): added check for map key in is_owner view function
simusud Jul 14, 2023
a2a6bdf
perf(bmc): removed unused functions and added lazify on callback func…
Suyog007 Jul 18, 2023
4f008da
perf(bts): removed comments and added lazify on fee gathering
Suyog007 Jul 18, 2023
be4d3ed
feat(bmc): added function to pause bridge
simusud Jul 20, 2023
9ebb4a9
feat(bts): added function to pause bridge
simusud Jul 20, 2023
196f88b
test(bmc): updated tests for bridge pause feature
simusud Jul 20, 2023
c35dcad
test(bts): updated test for set token limit
simusud Jul 20, 2023
51dbaf9
fix: icon tezos bridge integration
icondev22 Jul 21, 2023
98b92d2
refactor(bmc): removed unused db
simusud Jul 21, 2023
0e441a3
refactor(bts): resolved todo
simusud Jul 21, 2023
9b1eba4
Merge pull request #1 from TechFlow-Space/transfer-coin-msg-fix
Suyog007 Jul 21, 2023
107c67d
refactor(bmc): renamed entrypoint in bmc_periphery.py and updated che…
simusud Jul 24, 2023
6e39504
fix: tezos icon bridge integration
icondev22 Jul 24, 2023
6d16afc
feat: added tezos score setters
icondev22 Jul 24, 2023
6f05393
Merge branch 'main' into tezos-bridge
icondev22 Jul 24, 2023
95dceab
Merge pull request #2 from TechFlow-Space/transfer-coin-msg-fix
Suyog007 Jul 24, 2023
fa5419b
fix: block processing logic
icondev22 Jul 25, 2023
e3a3e4f
fix: configure dot env for tezos setter
icondev22 Jul 25, 2023
300644c
fix: tezos icon bridge integration
icondev22 Jul 27, 2023
9ba9b68
fix: tezos icon bridge config
icondev22 Aug 4, 2023
2b0c398
fix: added build to token.smartpy.sh
icondev22 Aug 4, 2023
f5fe4bc
fix: path in token.smartpy.sh
icondev22 Aug 4, 2023
a57443e
fix: path in token.smartpy.sh
icondev22 Aug 4, 2023
7da9e3e
fix: path in token.smartpy.sh
icondev22 Aug 4, 2023
03558c2
fix: path in token.smartpy.sh
icondev22 Aug 4, 2023
3efc78b
fix: path in token.smartpy.io
icondev22 Aug 4, 2023
1143144
fix: path in token.smartpy.sh
icondev22 Aug 4, 2023
e7b3eb2
fix: icon tezos bridge configuration
icondev22 Aug 8, 2023
e7c5bd5
feat: auto relay start from build script
icondev22 Aug 8, 2023
98f80ab
fix: include methods in `token.smartpy.sh`
icondev22 Aug 8, 2023
7462558
fix: tezos icon bridge configuration
icondev22 Aug 10, 2023
8ad4f35
fix: path in token.smartpy.sh
icondev22 Aug 10, 2023
a2f7189
fix: added unpausing bridge in tezos setters
icondev22 Aug 10, 2023
aef805d
fix: removed else if from tokenfallback
icondev22 Aug 11, 2023
90896a6
feat(bts): added increase and decrease allowance function in FA2_cont…
simusud Aug 16, 2023
f61a2b4
fix(bts): added checks and emit in allowance functions in FA2_contrac…
simusud Aug 17, 2023
4acc522
Merge pull request #3 from TechFlow-Space/add-and-decrease-allowance
Suyog007 Aug 17, 2023
f3d3f00
chore: code cleanup for bmr integration
icondev22 Aug 18, 2023
2556b5f
fix: lowercase issue in bts contract
icondev22 Aug 18, 2023
69e949f
feat: deployment script for mainnet and testnet
icondev22 Aug 18, 2023
ddddc7b
chore: added documentation for ICON Tezos bmr impl
icondev22 Aug 18, 2023
e27f887
fix: removed token.smartpy.sh
icondev22 Aug 18, 2023
f3e8b45
Merge branch 'main' into tezos-bridge
icondev22 Aug 18, 2023
7d06aa1
chore: file reformat to original
icondev22 Aug 18, 2023
80c7da3
fix: file reverted to original
icondev22 Aug 18, 2023
d4524b5
fix: removed unnecessary files
icondev22 Aug 18, 2023
b2a9af3
fix: reformatted to original
icondev22 Aug 18, 2023
c75d312
fix: removed register token
icondev22 Aug 18, 2023
35a90a6
fix: uncommented build script
icondev22 Aug 18, 2023
9a6c948
fix: remove unnecessary files
icondev22 Aug 18, 2023
7cd7eba
fix: hardcoded values
icondev22 Aug 18, 2023
0de533c
fix: hardcoded values
icondev22 Aug 18, 2023
be1ef6a
fix: tezos icon bridge implementation
icondev22 Aug 18, 2023
5b7938b
fix:config change
icondev22 Aug 18, 2023
4140011
Merge pull request #4 from TechFlow-Space/tezos-bridge
Suyog007 Aug 18, 2023
79f7ce9
fix: path in build scripts
icondev22 Aug 18, 2023
d9ea6b7
Merge pull request #5 from TechFlow-Space/fix/build_scripts
Suyog007 Aug 18, 2023
fd3e819
revert(bts): reverted previous commit due to storage issue
simusud Aug 18, 2023
c3f0766
Merge pull request #6 from TechFlow-Space/add-and-decrease-allowance
Suyog007 Aug 18, 2023
524fcdc
fix: timeout fixes
icondev22 Aug 21, 2023
3f42069
fix: sender fix
icondev22 Aug 21, 2023
9f765ca
chore: removed unnecessary prints
icondev22 Aug 21, 2023
89dd696
Merge branch 'main' into fix/build_scripts
icondev22 Aug 21, 2023
5b34a75
fix(bmc): fixed bmc sequence number decoding and encoding
Suyog007 Aug 21, 2023
0359b5d
Merge pull request #7 from TechFlow-Space/fix/build_scripts
Suyog007 Aug 21, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -26,4 +26,5 @@ devnet/docker/icon-bsc/data/
devnet/docker/icon-algorand/local/
devnet/docker/icon-algorand/cache/
devnet/docker/icon-algorand/iconvalidators
devnet/docker/icon-algorand/*keystore.json
devnet/docker/icon-algorand/*keystore.json
javascore/wallet1.json
4 changes: 2 additions & 2 deletions cmd/iconbridge/chain/icon/receiver.go
Original file line number Diff line number Diff line change
@@ -174,6 +174,7 @@ func (r *Receiver) newVerifier(opts *types.VerifierOptions) (*Verifier, error) {
if err != nil {
return nil, err
}

ok, err := vr.Verify(header, votes)
if !ok {
err = errors.New("verification failed")
@@ -304,7 +305,6 @@ func handleVerifierBlockRequests(requestCh chan *verifierBlockRequest, client IC
}

func (r *Receiver) receiveLoop(ctx context.Context, startHeight, startSeq uint64, callback func(rs []*chain.Receipt) error) (err error) {

blockReq, logFilter := r.blockReq, r.logFilter // copy

blockReq.Height, logFilter.seq = types.NewHexInt(int64(startHeight)), startSeq
@@ -378,7 +378,7 @@ loop:
}
}(ctxMonitorBlock, cancelMonitorBlock)

// sync verifier
// sync verifier disabled
if vr != nil {
if err := r.syncVerifier(vr, next); err != nil {
return errors.Wrapf(err, "sync verifier: %v", err)
2 changes: 0 additions & 2 deletions cmd/iconbridge/chain/icon/sender.go
Original file line number Diff line number Diff line change
@@ -190,7 +190,6 @@ func (s *sender) Segment(
if err != nil {
return nil, nil, err
}

return tx, newMsg, nil
}

@@ -247,7 +246,6 @@ func (tx *relayTx) ID() interface{} {
func (tx *relayTx) Send(ctx context.Context) error {
tx.cl.log.WithFields(log.Fields{
"prev": tx.Prev}).Debug("handleRelayMessage: send tx")

SignLoop:
for {
if err := tx.cl.SignTransaction(tx.w, tx.txParam); err != nil {
20 changes: 10 additions & 10 deletions cmd/iconbridge/chain/icon/verifier.go
Original file line number Diff line number Diff line change
@@ -2,16 +2,15 @@ package icon

import (
"fmt"
"github.com/icon-project/icon-bridge/cmd/iconbridge/chain/icon/types"
"sync"

"github.com/icon-project/icon-bridge/cmd/iconbridge/chain/icon/types"

"github.com/icon-project/goloop/common"
"github.com/icon-project/goloop/common/codec"
"github.com/icon-project/icon-bridge/common/crypto"
)



const (
VoteTypePrevote types.VoteType = iota
VoteTypePrecommit
@@ -38,12 +37,12 @@ type TxResult struct {
CumulativeStepUsed []byte
StepUsed []byte
StepPrice []byte
LogsBloom []byte
EventLogs []types.EventLog
ScoreAddress []byte
EventLogsHash common.HexBytes
TxIndex types.HexInt
BlockHeight types.HexInt
LogsBloom []byte
EventLogs []types.EventLog
ScoreAddress []byte
EventLogsHash common.HexBytes
TxIndex types.HexInt
BlockHeight types.HexInt
}

type Verifier struct {
@@ -114,8 +113,9 @@ func (vr *Verifier) Verify(blockHeader *types.BlockHeader, votes []byte) (ok boo
return true, nil
}
}
return true, nil

return false, fmt.Errorf("insufficient votes")
// return false, fmt.Errorf("insufficient votes")
}

func (vr *Verifier) Update(blockHeader *types.BlockHeader, nextValidators []common.Address) (err error) {
283 changes: 283 additions & 0 deletions cmd/iconbridge/chain/tezos/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
package tezos

import (
"bytes"
"context"
"encoding/json"
"io/ioutil"
"math/big"
"net/http"

// "io"
"time"

"github.com/icon-project/icon-bridge/common/log"

"blockwatch.cc/tzgo/codec"
"blockwatch.cc/tzgo/contract"
"blockwatch.cc/tzgo/micheline"
"blockwatch.cc/tzgo/rpc"
"blockwatch.cc/tzgo/tezos"
"github.com/icon-project/icon-bridge/cmd/iconbridge/chain"
"github.com/icon-project/icon-bridge/cmd/iconbridge/chain/tezos/types"
)

const (
DefaultSendTransactionRetryInterval = 30 * time.Second
DefaultGetTransactionResultPollingInterval = 15 * time.Second
DefaultBlockWaitInterval = 15 * time.Second
)

type IClient interface {
GetBalance(ctx context.Context, connection *rpc.Client, account tezos.Address, blockLevel int64)
GetBlockByHeight(ctx context.Context, connection *rpc.Client, blockLevel int64) (*rpc.Block, error)
GetBlockHeightByHash(ctx context.Context, connection *rpc.Client, hash tezos.BlockHash) (int64, error)
MonitorBlock(ctx context.Context, client *rpc.Client, connection *contract.Contract, blockLevel int64, callback func(v *types.BlockNotification) error) (*rpc.Block, error)
GetLastBlock(ctx context.Context, connection *rpc.Client) (*rpc.Block, error)
GetStatus(ctx context.Context, contr *contract.Contract) (TypesLinkStats, error)
HandleRelayMessage(ctx context.Context, callArgs contract.CallArguments) (*rpc.Receipt, error)
}

// tezos periphery
type TypesLinkStats struct {
RxSeq *big.Int
TxSeq *big.Int
RxHeight *big.Int
CurrentHeight *big.Int
}

type Client struct {
Log log.Logger
// Ctx context.Context
Cl *rpc.Client
Contract *contract.Contract
blockLevel int64
BmcManagement tezos.Address
}

func (c *Client) GetLastBlock(ctx context.Context, connection *rpc.Client) (*rpc.Block, error) {
block, err := connection.GetHeadBlock(ctx)
if err != nil {
return nil, err
}
return block, nil
}

func (c *Client) GetBlockByHeight(ctx context.Context, connection *rpc.Client, blockLevel int64) (*rpc.Block, error) {
block, err := connection.GetBlock(ctx, rpc.BlockLevel(blockLevel))
if err != nil {
return nil, err
}
return block, nil
}

func (c *Client) GetBlockHeightByHash(ctx context.Context, connection *rpc.Client, hash tezos.BlockHash) (uint64, error) {
block, err := connection.GetBlock(ctx, hash)
if err != nil {
return 0, err
}
return uint64(block.Header.Level), nil
}

func (c *Client) GetBlockHeaderByHeight(ctx context.Context, connection *rpc.Client, blockLevel int64) (*rpc.BlockHeader, error) {
block, err := connection.GetBlockHeader(ctx, rpc.BlockLevel(blockLevel))
if err != nil {
return nil, err
}
return block, nil
}

func filterMessageEvents(tx *rpc.Transaction, contractAddress tezos.Address, height uint64, dst string) (*chain.Receipt, error) {
receipt := &chain.Receipt{}
var events []*chain.Event

for i := 0; i < len(tx.Metadata.InternalResults); i++ {
internalResults := tx.Metadata.InternalResults[i]
if internalResults.Kind.String() == "event" && internalResults.Source.ContractAddress() == contractAddress.ContractAddress() {
if internalResults.Tag == "Message" {
message := internalResults.Payload.Args[0].Bytes
next := internalResults.Payload.Args[1].Args[0].String
seq := internalResults.Payload.Args[1].Args[1].Int

if next == dst {
events = append(events, &chain.Event{
Message: message,
Next: chain.BTPAddress(next),
Sequence: seq.Uint64(),
})

receipt.Index = uint64(i)
receipt.Height = height
receipt.Events = events
}
}

}
}
return receipt, nil
}

func (c *Client) GetClient() *rpc.Client {
return c.Cl
}

func (c *Client) GetBalance(ctx context.Context, connection *rpc.Client, account tezos.Address, blockLevel int64) (*big.Int, error) {
balance, err := connection.GetContractBalance(ctx, account, rpc.BlockLevel(blockLevel))
if err != nil {
return nil, err
}
return balance.Big(), nil
}

func (c *Client) GetBMCManangement(ctx context.Context, contr *contract.Contract, account tezos.Address) (string, error) {
result, err := contr.RunView(ctx, "get_bmc_periphery", micheline.Prim{})
if err != nil {
return "", err
}
return result.String, nil
}

func (c *Client) GetStatus(ctx context.Context, contr *contract.Contract, link string) (TypesLinkStats, error) {
prim := micheline.Prim{}

in := "{ \"string\": \"" + link + "\" }"

if err := prim.UnmarshalJSON([]byte(in)); err != nil {
return *new(TypesLinkStats), err
}

result, err := contr.RunView(ctx, "get_status", prim)
if err != nil {
return *new(TypesLinkStats), err
}
linkStats := &TypesLinkStats{}

linkStats.CurrentHeight = result.Args[0].Args[0].Int
linkStats.RxHeight = result.Args[0].Args[1].Int
linkStats.RxSeq = result.Args[1].Int
linkStats.TxSeq = result.Args[2].Int

return *linkStats, nil
}

func (c *Client) GetOperationByHash(ctx context.Context, clinet *rpc.Client, blockHash tezos.BlockHash, list int, pos int) (*rpc.Operation, error) {
operation, err := clinet.GetBlockOperation(ctx, blockHash, list, pos)
if err != nil {
return nil, err
}
return operation, nil
}

func (c *Client) GetConsensusKey(ctx context.Context, bakerConsensusKey tezos.Address) (tezos.Key, error) {
var exposedPublicKey tezos.Key
for i := 0; i < 5; i++ {
url := c.Cl.BaseURL.String() + "/chains/main/blocks/head/context/raw/json/contracts/index/" + bakerConsensusKey.String() + "/consensus_key/active"

resp, err := http.Get(url)
if err != nil {
return tezos.Key{}, err
}

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return tezos.Key{}, err
}
//Convert the body to type string
sb := string(body)

exposedPublicKey, err = tezos.ParseKey(sb[1 : len(sb)-2])
if err != nil {
time.Sleep(2 * time.Second)
continue
}
break
}
return exposedPublicKey, nil
}

func (c *Client) HandleRelayMessage(ctx context.Context, callArgs contract.CallArguments, opts *rpc.CallOptions) (*rpc.Receipt, error) {
result, err := c.Contract.Call(ctx, callArgs, opts)
if err != nil {
return nil, err
}
return result, nil
}

func (c *Client) CustomCall(ctx context.Context, args []contract.CallArguments, opts *rpc.CallOptions) (*rpc.Receipt, error) {
if opts == nil {
opts = &rpc.DefaultOptions
}

// assemble batch transaction
op := codec.NewOp().WithTTL(opts.TTL)
for _, arg := range args {
if arg == nil {
continue
}
op.WithContents(arg.Encode())
}

var limits []tezos.Limits
limit := tezos.Limits{
GasLimit: tezos.MumbainetParams.HardGasLimitPerOperation,
StorageLimit: tezos.MumbainetParams.HardStorageLimitPerOperation,
}

limits = append(limits, limit)

op.WithLimits(limits, 0).WithMinFee()

// prepare, sign and broadcast
return c.Cl.Send(ctx, op, opts)
}

func NewClient(uri string, src tezos.Address, bmcManagement tezos.Address, l log.Logger) (*Client, error) {
c, err := rpc.NewClient(uri, nil)

conn := contract.NewContract(src, c)

if err != nil {
return nil, err
}

return &Client{Log: l, Cl: c, Contract: conn, BmcManagement: bmcManagement}, nil
}

func PrettyEncode(data interface{}) error {
var buffer bytes.Buffer
enc := json.NewEncoder(&buffer)
enc.SetIndent("", " ")
if err := enc.Encode(data); err != nil {
return err
}
return nil
}

func filterTransactionOperations(block *rpc.Block, contractAddress tezos.Address, blockHeight int64, cl *Client, dst string) (bool, []*chain.Receipt, error) {
blockOperations := block.Operations
var tx *rpc.Transaction
var receipt []*chain.Receipt
for i := 0; i < len(blockOperations); i++ {
for j := 0; j < len(blockOperations[i]); j++ {
for _, operation := range blockOperations[i][j].Contents {
switch operation.Kind() {
case tezos.OpTypeTransaction:
tx = operation.(*rpc.Transaction)
r, err := filterMessageEvents(tx, cl.BmcManagement, uint64(blockHeight), dst)
if err != nil {
return false, nil, err
}
if len(r.Events) != 0 {
receipt = append(receipt, r)
}
}
}
}
}
// var transaction *rpc.Transaction

if len(receipt) == 0 {
return false, nil, nil
}
return true, receipt, nil
}
402 changes: 402 additions & 0 deletions cmd/iconbridge/chain/tezos/receiver.go

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions cmd/iconbridge/chain/tezos/register_relay.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package tezos

import "github.com/icon-project/icon-bridge/cmd/iconbridge/relay"

func init() {
relay.Senders["tezos"] = NewSender
relay.Receivers["tezos"] = NewReceiver
}
303 changes: 303 additions & 0 deletions cmd/iconbridge/chain/tezos/sender.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
package tezos

import (
"context"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"math/big"
"time"

"github.com/icon-project/icon-bridge/common/log"
"github.com/icon-project/icon-bridge/common/wallet"
"github.com/icon-project/icon-bridge/common/codec"

"blockwatch.cc/tzgo/contract"
"blockwatch.cc/tzgo/micheline"
"blockwatch.cc/tzgo/rpc"
"blockwatch.cc/tzgo/tezos"
"github.com/icon-project/icon-bridge/cmd/iconbridge/chain"
)

const (
txMaxDataSize = 1024 // 1 KB
txOverheadScale = 0.01
defaultTxSizeLimit = txMaxDataSize / (1 + txOverheadScale) // with the rlp overhead
defaultSendTxTimeOut = 30 * time.Second // 30 seconds is the block time for tezos
maxEventPropagation = 5
)

var (
originalRxSeq = big.NewInt(0)
statusFlag = false
)

type senderOptions struct {
StepLimit uint64 `json:"step_limit"`
TxDataSizeLimit uint64 `json:"tx_data_size_limit"`
BalanceThreshold uint64 `json:"balance_threshold"`
BMCManagment string `json:"bmcManagement"`
}

type sender struct {
log log.Logger
src chain.BTPAddress
dst tezos.Address
connection *contract.Contract
parameters micheline.Parameters
cls *Client
blockLevel int64
opts senderOptions
w wallet.Wallet
}

func NewSender(
src, dst chain.BTPAddress,
urls []string, w wallet.Wallet,
rawOpts json.RawMessage, l log.Logger) (chain.Sender, error) {
var err error
// srcAddr := tezos.MustParseAddress(src.ContractAddress())
dstAddr := tezos.MustParseAddress(dst.ContractAddress())
s := &sender{
log: l,
src: src,
dst: dstAddr,
w: w,
}

json.Unmarshal(rawOpts, &s.opts)

if len(urls) == 0 {
return nil, fmt.Errorf("Empty url")
}

bmcManaement := tezos.MustParseAddress(s.opts.BMCManagment)

s.cls, err = NewClient(urls[0], dstAddr, bmcManaement, l)
if err != nil {
return nil, err
}

return s, nil

}

func (s *sender) Balance(ctx context.Context) (balance, threshold *big.Int, err error) {
address := tezos.MustParseAddress(s.w.Address())
balance, err = s.cls.GetBalance(ctx, s.cls.Cl, address, s.cls.blockLevel)
if err != nil {
return nil, nil, err
}

return balance, big.NewInt(0), nil
}

func (s *sender) Segment(ctx context.Context, msg *chain.Message) (tx chain.RelayTx, newMsg *chain.Message, err error) {

if ctx.Err() != nil {
return nil, nil, ctx.Err()
}

if s.opts.TxDataSizeLimit == 0 {
limit := defaultTxSizeLimit
s.opts.TxDataSizeLimit = uint64(limit)
}

if len(msg.Receipts) == 0 {
return nil, msg, nil
}
rm := &chain.RelayMessage{
Receipts: make([][]byte, 0),
}

var msgSize uint64

newMsg = &chain.Message{
From: msg.From,
Receipts: msg.Receipts,
}

var newEvent []*chain.Event
var newReceipt *chain.Receipt
var newReceipts []*chain.Receipt

for i, receipt := range msg.Receipts {
if len(receipt.Events) > maxEventPropagation {
newEvent = receipt.Events[maxEventPropagation:]
receipt.Events = receipt.Events[:maxEventPropagation]
}

rlpEvents, err := codec.RLP.MarshalToBytes(receipt.Events) //json.Marshal(receipt.Events) // change to rlp bytes
if err != nil {
return nil, nil, err
}

rlpReceipt, err := codec.RLP.MarshalToBytes(&chain.RelayReceipt{
Index: receipt.Index,
Height: receipt.Height,
Events: rlpEvents,
}) //json.Marshal(chainReceipt) // change to rlp bytes
if err != nil {
return nil, nil, err
}

newMsgSize := msgSize + uint64(len(rlpReceipt))
if newMsgSize > s.opts.TxDataSizeLimit {
newMsg.Receipts = msg.Receipts[i:]
break
}
msgSize = newMsgSize
rm.Receipts = append(rm.Receipts, rlpReceipt)

if newEvent != nil {
newReceipt = receipt
newReceipt.Events = newEvent
newReceipts = append(newReceipts, newReceipt)
newReceipts = append(newReceipts, msg.Receipts...)
msg.Receipts = newReceipts
break
}
}
message, err := codec.RLP.MarshalToBytes(rm) // json.Marshal(rm)
if err != nil {
return nil, nil, err
}

tx, err = s.newRelayTx(ctx, msg.From.String(), message)
if err != nil {
return nil, nil, err
}

return tx, newMsg, nil
}

func (s *sender) Status(ctx context.Context) (link *chain.BMCLinkStatus, err error) {
if ctx.Err() != nil {
return nil, ctx.Err()
}

status, err := s.cls.GetStatus(ctx, s.cls.Contract, s.src.String())
if err != nil {
return nil, err
}

ls := &chain.BMCLinkStatus{}

ls.TxSeq = status.TxSeq.Uint64()
ls.RxSeq = status.RxSeq.Uint64()
ls.CurrentHeight = status.CurrentHeight.Uint64()
ls.RxHeight = status.RxHeight.Uint64()

return ls, nil
}

func (s *sender) newRelayTx(ctx context.Context, prev string, message []byte) (*relayTx, error) {
client := s.cls

return &relayTx{
Prev: prev,
Message: message,
cl: client,
w: s.w,
link: s.src.String(),
}, nil
}

type relayTx struct {
Prev string `json:"_prev"`
Message []byte `json:"_msg"`

cl *Client
receipt *rpc.Receipt
w wallet.Wallet
link string
}

func (tx *relayTx) ID() interface{} {
return nil
}

func (tx *relayTx) Send(ctx context.Context) (err error) {
_ctx, cancel := context.WithTimeout(ctx, defaultSendTxTimeOut)
defer cancel()

prim := micheline.Prim{}
messageHex := hex.EncodeToString(tx.Message)

status, err := tx.cl.GetStatus(ctx, tx.cl.Contract, tx.link)
if err != nil {
return err
}

if !statusFlag {
originalRxSeq = status.RxSeq
statusFlag = true
}

if status.RxSeq.Cmp(originalRxSeq) > 0 {
statusFlag = false
hash, err := tx.cl.GetBlockByHeight(ctx, tx.cl.Cl, status.CurrentHeight.Int64())
if err != nil {
return err
}

tx.receipt = &rpc.Receipt{
Pos: 0,
List: 0,
Block: hash.Hash,
}
return nil
}

in := "{ \"prim\": \"Pair\", \"args\": [ { \"bytes\": \"" + messageHex + "\" }, { \"string\": \"" + tx.Prev + "\" } ] }"

if err := prim.UnmarshalJSON([]byte(in)); err != nil {
return err
}

args := contract.NewTxArgs()
args.WithParameters(micheline.Parameters{Entrypoint: "handle_relay_message", Value: prim})

opts := rpc.DefaultOptions

w, ok := tx.w.(*wallet.TezosWallet)
if !ok {
return errors.New("not a tezos wallet")
}

opts.Signer = w.Signer()
opts.TTL = 3

from := tezos.MustParseAddress(tx.w.Address()) // pubk

argument := args.WithSource(from).WithDestination(tx.cl.Contract.Address())

receipt, err := tx.cl.HandleRelayMessage(_ctx, argument, &opts)
tx.cl.Log.WithFields(log.Fields{
"tx": messageHex}).Debug("handleRelayMessage: tx sent")
if err != nil {
return err
}

tx.receipt = receipt
statusFlag = false
return nil
}

func (tx *relayTx) Receipt(ctx context.Context) (blockHeight uint64, err error) {
if tx.receipt == nil {
return 0, fmt.Errorf("couldnot get receipt")
}

_, err = tx.cl.GetOperationByHash(ctx, tx.cl.Cl, tx.receipt.Block, tx.receipt.List, tx.receipt.Pos)
if err != nil {
return 0, err
}

blockHeight, err = tx.cl.GetBlockHeightByHash(ctx, tx.cl.Cl, tx.receipt.Block)
if err != nil {
return 0, err
}
return blockHeight, nil
}
22 changes: 22 additions & 0 deletions cmd/iconbridge/chain/tezos/types/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package types

import (
"math/big"
"github.com/icon-project/icon-bridge/cmd/iconbridge/chain"
"blockwatch.cc/tzgo/rpc"
"blockwatch.cc/tzgo/tezos"


)

type BlockNotification struct {
Hash tezos.BlockHash
Height *big.Int
Header *rpc.BlockHeader
Receipts []*chain.Receipt
HasBTPMessage *bool
Proposer tezos.Address
Block *rpc.Block
BakerConsensusKey tezos.Key
}

261 changes: 261 additions & 0 deletions cmd/iconbridge/chain/tezos/verifier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
package tezos

import (
"fmt"
"sync"

"context"
"strconv"

"blockwatch.cc/tzgo/codec"
"blockwatch.cc/tzgo/rpc"
"blockwatch.cc/tzgo/tezos"
"github.com/icon-project/icon-bridge/cmd/iconbridge/chain/tezos/types"
"github.com/pkg/errors"
)

type IVerifier interface {
Next() int64
Verify(ctx context.Context, lbn *types.BlockNotification) error
Update(ctx context.Context, lbn *types.BlockNotification) error
ParentHash() tezos.BlockHash
IsValidator(proposer tezos.Address, height int64) bool
Height() int64
LastVerifiedBn() *types.BlockNotification
}

const (
threshold = 4667
)

type Verifier struct {
chainID uint32
mu sync.RWMutex
validators map[tezos.Address]bool
validatorsPublicKey map[tezos.Address]tezos.Key
next int64
parentHash tezos.BlockHash
parentFittness int64
height int64
cycle int64
lastVerifiedBn *types.BlockNotification
updatedBn *types.BlockNotification
cl *Client
}

func (vr *Verifier) Next() int64 {
vr.mu.RLock()
defer vr.mu.RUnlock()
return vr.next
}

func (vr *Verifier) Verify(ctx context.Context, lbn *types.BlockNotification) error {
vr.mu.RLock()
defer vr.mu.RUnlock()
blockFittness := lbn.Header.Fitness
currentFittness, err := strconv.ParseInt(string(blockFittness[1].String()), 16, 64)
if err != nil {
return err
}

if currentFittness < vr.parentFittness {
return fmt.Errorf("invalid block fittness %d", currentFittness)
}

previousHashInBlock := lbn.Block.Header.Predecessor

if previousHashInBlock.String() != vr.parentHash.String() {
return fmt.Errorf("invalid block hash %d", lbn.Header.Level)
}

isValidSignature, _ := vr.VerifySignature(ctx, lbn)

if !isValidSignature {
return fmt.Errorf("invalid block signature. signature mismatch")
}

err = vr.verifyEndorsement(lbn.Block, lbn.Header.ChainId)
if err != nil {
return fmt.Errorf("invalid endorsement")
}

return nil
}

func (vr *Verifier) Update(ctx context.Context, lbn *types.BlockNotification) error {
vr.mu.Lock()
defer vr.mu.Unlock()

header := lbn.Header
block := lbn.Block
blockFittness := header.Fitness

currentFittness, err := strconv.ParseInt(string(blockFittness[1].String()), 16, 64)
if err != nil {
return err
}

vr.parentFittness = currentFittness
vr.parentHash = block.Hash
vr.height = header.Level
vr.next = header.Level + 1

if vr.cycle != block.Metadata.LevelInfo.Cycle {
vr.updateValidatorsAndCycle(ctx, block.Header.Level, block.Metadata.LevelInfo.Cycle)
}

if vr.updatedBn == nil {
vr.updatedBn = lbn
return nil
}
vr.lastVerifiedBn = vr.updatedBn
vr.updatedBn = lbn
return nil
}

func (vr *Verifier) ParentHash() tezos.BlockHash {
vr.mu.RLock()
defer vr.mu.RUnlock()
return vr.parentHash
}

func (vr *Verifier) LastVerifiedBn() *types.BlockNotification {
vr.mu.RLock()
defer vr.mu.RUnlock()
return vr.lastVerifiedBn
}

func (vr *Verifier) Height() int64 {
vr.mu.RLock()
defer vr.mu.RUnlock()
return vr.height
}

func (vr *Verifier) IsValidator(proposer tezos.Address, height int64) bool {
vr.mu.RLock()
defer vr.mu.RUnlock()
return true
}

func (vr *Verifier) VerifySignature(ctx context.Context, lbn *types.BlockNotification) (bool, error) {
header := lbn.Block.Header

blockHeader := codec.BlockHeader{
Level: int32(header.Level),
Proto: byte(header.Proto),
Predecessor: header.Predecessor,
Timestamp: header.Timestamp,
ValidationPass: byte(header.ValidationPass),
OperationsHash: header.OperationsHash,
Fitness: header.Fitness,
Context: header.Context,
PayloadHash: header.PayloadHash,
PayloadRound: header.PayloadRound,
ProofOfWorkNonce: header.ProofOfWorkNonce,
LbToggleVote: header.LbVote(),
SeedNonceHash: lbn.Block.Metadata.NonceHash,
ChainId: &lbn.Block.ChainId,
}

digestedHash := blockHeader.Digest()

err := vr.validatorsPublicKey[lbn.Block.Metadata.Baker].Verify(digestedHash[:], header.Signature)

if err != nil {
return false, err
}
return true, nil
}

func (vr *Verifier) updateValidatorsAndCycle(ctx context.Context, blockHeight int64, cycle int64) error {
validatorsList, err := vr.cl.Cl.ListEndorsingRights(ctx, rpc.BlockLevel(blockHeight))
if err != nil {
return err
}

bakersList, err := vr.cl.Cl.ListBakingRightsCycle(ctx, rpc.BlockLevel(blockHeight), cycle, 1)
if err != nil {
return err
}

var validatorsPublicKey tezos.Key
// remove all validators
for a := range vr.validators {
delete(vr.validators, a)
}
vr.validators = make(map[tezos.Address]bool)
// add new validators
for _, validator := range validatorsList {
vr.validators[validator.Delegate] = true
validatorsPublicKey, err = vr.cl.GetConsensusKey(ctx, validator.Delegate)
if err != nil {
return err
}
vr.validatorsPublicKey[validator.Delegate] = validatorsPublicKey
}

for _, validator := range bakersList {
if !vr.validators[validator.Delegate] {
vr.validators[validator.Delegate] = true
validatorsPublicKey, err = vr.cl.GetConsensusKey(ctx, validator.Delegate)
if err != nil {
return err
}
vr.validatorsPublicKey[validator.Delegate] = validatorsPublicKey
}
}

vr.cycle = cycle
return nil
}

func (vr *Verifier) verifyEndorsement(block *rpc.Block, chainID tezos.ChainIdHash) error {
endorsementPower := 0
endorsers := make(map[tezos.Address]bool)
op := block.Operations
for i := 0; i < len(op); i++ {
for j := 0; j < len(op[i]); j++ {
for _, operation := range op[i][j].Contents {
signature := op[i][j].Signature
branch := op[i][j].Branch
switch operation.Kind() {
case tezos.OpTypeEndorsement:
tx := operation.(*rpc.Endorsement)

if _, isDelegate := vr.validators[tx.Metadata.Delegate]; isDelegate {
endorsement := codec.TenderbakeEndorsement{
Slot: int16(tx.Slot),
Level: int32(tx.Level),
Round: int32(tx.Round),
BlockPayloadHash: tx.PayloadHash,
}
digested := codec.NewOp().WithContentsFront(&endorsement).WithChainId(block.ChainId).WithBranch(branch).Digest()

managerKey := vr.validatorsPublicKey[tx.Metadata.Delegate]

err := managerKey.Verify(digested[:], signature)

if err != nil {
return err
}

if _, ok := endorsers[tx.Metadata.Delegate]; !ok {
endorsers[tx.Metadata.Delegate] = true
endorsementPower += tx.Metadata.EndorsementPower
}
}
}
}
}
}
if endorsementPower > int(threshold) {
return nil
}
return errors.New("endorsement verification failed")

}

type VerifierOptions struct {
BlockHeight int64 `json:"blockHeight"`
BlockHash tezos.BlockHash `json:"parentHash"`
}
115 changes: 0 additions & 115 deletions cmd/iconbridge/example.config.json

This file was deleted.

1 change: 1 addition & 0 deletions cmd/iconbridge/main.go
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@ import (
_ "github.com/icon-project/icon-bridge/cmd/iconbridge/chain/icon"
_ "github.com/icon-project/icon-bridge/cmd/iconbridge/chain/near"
_ "github.com/icon-project/icon-bridge/cmd/iconbridge/chain/substrate-eth"
_ "github.com/icon-project/icon-bridge/cmd/iconbridge/chain/tezos"
)

var (
2 changes: 1 addition & 1 deletion cmd/iconbridge/relay/multi_relay.go
Original file line number Diff line number Diff line change
@@ -56,7 +56,7 @@ func NewMultiRelay(cfg *Config, l log.Logger) (Relay, error) {
}
l := l.WithFields(log.Fields{
log.FieldKeyModule: rc.Name,
log.FieldKeyWallet: w.Address(),
// log.FieldKeyWallet: w.Address(),
log.FieldKeyService: srvName,
})

6 changes: 2 additions & 4 deletions cmd/iconbridge/relay/relay.go
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ import (
const (
relayTickerInterval = 5 * time.Second
relayBalanceCheckInterval = 60 * time.Second
relayTriggerReceiptsCount = 20
relayTriggerReceiptsCount = 5
relayTxSendWaitInterval = time.Second / 2
relayTxReceiptWaitInterval = time.Second
relayInsufficientBalanceWaitInterval = 30 * time.Second
@@ -157,7 +157,6 @@ func (r *relay) Start(ctx context.Context) error {
}
}
msg.Receipts = receipts

if len(msg.Receipts) > 0 {
r.log.WithFields(log.Fields{
"seq": []uint64{seqBegin, seqEnd}}).Debug("srcMsg added")
@@ -168,7 +167,6 @@ func (r *relay) Start(ctx context.Context) error {
}

case <-relayCh:

link, err = r.dst.Status(ctx)
if err != nil {
r.log.WithFields(log.Fields{"error": err}).Debug("dst.Status: failed")
@@ -188,7 +186,7 @@ func (r *relay) Start(ctx context.Context) error {
r.log.WithFields(log.Fields{"rxSeq": missing}).Error("missing event sequence")
return fmt.Errorf("missing event sequence")
}

tx, newMsg, err := r.dst.Segment(ctx, srcMsg)
if err != nil {
return err
30 changes: 17 additions & 13 deletions common/address.go
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ import (
"encoding/json"
"reflect"

// "github.com/btcsuite/btcutil/base58"
"github.com/icon-project/icon-bridge/common/codec"
"github.com/icon-project/icon-bridge/common/crypto"
"github.com/icon-project/icon-bridge/common/errors"
@@ -46,22 +47,25 @@ func (a *Address) UnmarshalJSON(b []byte) error {
func (a *Address) SetString(s string) error {
var isContract = false
if len(s) >= 2 {
var err error
var bytes []byte
prefix := s[0:2]
switch {
case s[0:2] == "cx":
isContract = true
s = s[2:]
case s[0:2] == "hx":
s = s[2:]
case s[0:2] == "0x":
case prefix == "tz" || prefix == "KT":
isContract = prefix == "KT"
// bytes, _, err = base58.CheckDecode(s[2:])
return nil
case prefix == "cx" || prefix == "hx" || prefix == "0x":
isContract = prefix == "cx"
s = s[2:]
if len(s)%2 == 1 {
s = "0" + s
}
bytes, err = hex.DecodeString(s)
}
if err != nil {
return err
}
}
if len(s)%2 == 1 {
s = "0" + s
}
if bytes, err := hex.DecodeString(s); err != nil {
return err
} else {
if err := a.SetTypeAndID(isContract, bytes); err != nil {
return err
}
6 changes: 6 additions & 0 deletions common/wallet/keystore.go
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ import (
"fmt"
"io"

"blockwatch.cc/tzgo/tezos"
"github.com/gofrs/uuid"
"golang.org/x/crypto/scrypt"
"golang.org/x/crypto/sha3"
@@ -26,6 +27,7 @@ const (
cipherAES128CTR = "aes-128-ctr"
kdfScrypt = "scrypt"
coinTypeNear = "near"
coinTypeXTZ = "xtz"
)

type AES128CTRParams struct {
@@ -177,6 +179,10 @@ func DecryptKeyStore(data, pw []byte) (Wallet, error) {
return nil, err
}
return NewNearwalletFromPrivateKey(key)
case coinTypeXTZ:
key := tezos.MustParsePrivateKey(ksdata.Crypto.Cipher)

return NewTezosWalletFromPrivateKey(key)
default:
return nil, errors.Errorf("InvalidCoinType(coin=%s)", ksdata.CoinType)
}
41 changes: 41 additions & 0 deletions common/wallet/wallet_tezos.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package wallet

import (
"errors"

"blockwatch.cc/tzgo/signer"
"blockwatch.cc/tzgo/tezos"
)

type TezosWallet struct {
Skey tezos.PrivateKey
Pkey tezos.Key
}

func (w *TezosWallet) Address() string {
return w.Pkey.Address().String()
}

func (w *TezosWallet) Sign(data []byte) ([]byte, error) {
return nil, errors.New("not allowed, use Signer instead")
}

func (w *TezosWallet) Signer() *signer.MemorySigner {
return signer.NewFromKey(w.Skey)
}

func (w *TezosWallet) PublicKey() []byte {
return w.Pkey.Bytes()
}

func (w *TezosWallet) ECDH(pubKey []byte) ([]byte, error) {
//TODO: Not implemented yet
return nil, nil
}

func NewTezosWalletFromPrivateKey(sk tezos.PrivateKey) (*TezosWallet, error) {
return &TezosWallet{
Skey: sk,
Pkey: sk.Public(),
}, nil
}
42 changes: 42 additions & 0 deletions devnet/docker/icon-tezos/bmr.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# dev build
FROM ubuntu:18.04

ARG TARGETARCH
ARG GOLANG_VERSION="1.20"

SHELL ["/bin/bash", "-c"]

ENV GOPATH=/root/go
ENV GO111MODULE=on
ENV GIMME_GO_VERSION=${GOLANG_VERSION}
ENV PATH="/root/bin:${PATH}"

RUN apt update && apt upgrade -y && \
apt install libgmp-dev libssl-dev curl git \
psmisc dnsutils jq make gcc g++ bash tig tree sudo vim \
silversearcher-ag unzip emacs-nox nano bash-completion -y

RUN mkdir ~/bin && \
curl -sL -o ~/bin/gimme \
https://raw.githubusercontent.com/travis-ci/gimme/master/gimme && \
chmod +x ~/bin/gimme

RUN eval "$(~/bin/gimme ${GIMME_GO_VERSION})"
RUN touch /root/.bash_profile && \
gimme ${GIMME_GO_VERSION} >> /root/.bash_profile && \
echo "GIMME_GO_VERSION='${GIMME_GO_VERSION}'" >> /root/.bash_profile && \
echo "GO111MODULE='on'" >> /root/.bash_profile && \
echo ". ~/.bash_profile" >> /root/.profile && \
echo ". ~/.bash_profile" >> /root/.bashrc
ENV PATH="/root/.gimme/versions/go${GIMME_GO_VERSION}.linux.${TARGETARCH:-amd64}/bin:${GOPATH}/bin:${PATH}"
RUN . ~/.bash_profile

COPY . bmr
RUN cd bmr/cmd/iconbridge

# prod build
FROM ubuntu:18.04
SHELL ["/bin/bash", "-c"]
RUN apt update -y && apt install -y make ca-certificates libssl-dev
COPY --from=0 /bmr/cmd/iconbridge/iconbridge /bin/iconbridge
RUN rm -rf /var/lib/apt/lists/*
15 changes: 15 additions & 0 deletions devnet/docker/icon-tezos/docker-compose-bmr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
version: '3.3'
services:
bmr:
image: i2tbmr2:latest
container_name: bmr
network_mode: host
restart: unless-stopped
# ports:
# - 6060:6060 # golang pprof
entrypoint: ["/bin/iconbridge", "-config", "/config.json"]
volumes:
- /home/sheldor/GoProjects/icon-bridge/devnet/docker/icon-tezos/_ixh/relay.config.json:/config.json
#environment:
#AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
#AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/bin/bash
#BUILD_DIR=$(echo "$(cd "$(dirname "../../../../../")"; pwd)"/build)
BASE_DIR=$(echo "$(cd "$(dirname "../../")"; pwd)")
BUILD_DIR=$BASE_DIR/build
export ICONBRIDGE_CONFIG_DIR=$BASE_DIR/_ixh
export ICONBRIDGE_CONTRACTS_DIR=$BUILD_DIR/contracts
export ICONBRIDGE_SCRIPTS_DIR=$BASE_DIR/scripts
export ICONBRIDGE_BIN_DIR=$BASE_DIR

export CONFIG_DIR=${CONFIG_DIR:-${ICONBRIDGE_CONFIG_DIR}}
export CONTRACTS_DIR=${CONTRACTS_DIR:-${ICONBRIDGE_CONTRACTS_DIR}}
export SCRIPTS_DIR=${SCRIPTS_DIR:-${ICONBRIDGE_SCRIPTS_DIR}}

###################################################################################
# testnet: begin
export TAG="ICON BSC TESTNET"
export BSC_BMC_NET="0x61.bsc"
export ICON_BMC_NET="0x2.icon"
export SNOW_BMC_NET="0x229.snow"
export TZ_BMC_NET="0x63.tezos"
export GOLOOP_RPC_NID="0x2"
export BSC_NID="97"
export TEZOS_NID="NetXnHfVqm9iesp"

export ICON_ENDPOINT="https://lisbon.net.solidwallet.io/api/v3/icon_dex"
export ICON_NATIVE_COIN_SYM=("ICX")
export ICON_NATIVE_COIN_NAME=("btp-$ICON_BMC_NET-ICX")
export ICON_NATIVE_TOKEN_SYM=("sICX" "bnUSD")
export ICON_NATIVE_TOKEN_NAME=("btp-$ICON_BMC_NET-sICX" "btp-$ICON_BMC_NET-bnUSD")
export ICON_WRAPPED_COIN_SYM=("BNB" "BUSD" "USDT" "USDC" "BTCB" "ETH" "ICZ")
export ICON_WRAPPED_COIN_NAME=("btp-$BSC_BMC_NET-BNB" "btp-$BSC_BMC_NET-BUSD" "btp-$BSC_BMC_NET-USDT" "btp-$BSC_BMC_NET-USDC" "btp-$BSC_BMC_NET-BTCB" "btp-$BSC_BMC_NET-ETH" "btp-$SNOW_BMC_NET-ICZ")
export FEE_GATHERING_INTERVAL=21600


export ICON_NATIVE_COIN_FIXED_FEE=(4300000000000000000)
export ICON_NATIVE_COIN_FEE_NUMERATOR=(100)
export ICON_NATIVE_COIN_DECIMALS=(18)
export ICON_NATIVE_TOKEN_FIXED_FEE=(3900000000000000000 1500000000000000000)
export ICON_NATIVE_TOKEN_FEE_NUMERATOR=(100 100)
export ICON_NATIVE_TOKEN_DECIMALS=(18 18)
export ICON_WRAPPED_COIN_FIXED_FEE=(5000000000000000 1500000000000000000 1500000000000000000 1500000000000000000 62500000000000 750000000000000 4300000000000000000)
export ICON_WRAPPED_COIN_FEE_NUMERATOR=(100 100 100 100 100 100 100)
export ICON_WRAPPED_COIN_DECIMALS=(18 18 18 18 18 18 18)

export TZ_ENDPOINT="https://ghostnet.tezos.marigold.dev"
export TZ_NATIVE_COIN_SYM=("XTZ")
export TZ_NATIVE_COIN_NAME=("btp-$TZ_BMC_NET-TZ")
export TZ_NATIVE_TOKEN_SYM=("BUSD" "USDT" "USDC" "BTCB" "ETH")
export TZ_NATIVE_TOKEN_NAME=("btp-$TZ_BMC_NET-BUSD" "btp-$TZ_BMC_NET-USDT" "btp-$TZ_BMC_NET-USDC" "btp-$TZ_BMC_NET-BTCB" "btp-$TZ_BMC_NET-ETH")
export TZ_WRAPPED_COIN_SYM=("ICX" "sICX" "bnUSD" "ICZ")
export TZ_WRAPPED_COIN_NAME=("btp-$ICON_BMC_NET-ICX" "btp-$ICON_BMC_NET-sICX" "btp-$ICON_BMC_NET-bnUSD" "btp-$SNOW_BMC_NET-ICZ")

export TZ_NATIVE_COIN_FIXED_FEE=(1)
export TZ_NATIVE_COIN_FEE_NUMERATOR=(100)
export TZ_NATIVE_COIN_DECIMALS=(6)
export TZ_NATIVE_TOKEN_FIXED_FEE=(1500000000000000000 1500000000000000000 1500000000000000000 62500000000000 750000000000000)
export TZ_NATIVE_TOKEN_FEE_NUMERATOR=(100 100 100 100 100)
export TZ_NATIVE_TOKEN_DECIMALS=(18 18 18 18 18)
export TZ_WRAPPED_COIN_FIXED_FEE=(4300000000000000000 3900000000000000000 1500000000000000000 4300000000000000000)
export TZ_WRAPPED_COIN_FEE_NUMERATOR=(100 100 100 100)
export TZ_WRAPPED_COIN_DECIMALS=(18 18 18 18)

# testnet: end
###################################################################################

GOLOOPCHAIN=${GOLOOPCHAIN:-"goloop"}
export GOLOOP_RPC_STEP_LIMIT=5000000000
export ICON_KEY_STORE=$ICONBRIDGE_CONFIG_DIR/keystore/icon.god.wallet.json
export ICON_SECRET=$ICONBRIDGE_CONFIG_DIR/keystore/icon.god.wallet.secret
export GOLOOP_RPC_URI=$ICON_ENDPOINT
export GOLOOP_RPC_KEY_STORE=$ICON_KEY_STORE
export GOLOOP_RPC_KEY_SECRET=$ICON_SECRET

export TZ_RPC_URI=$BSC_ENDPOINT
export TZ_KEY_STORE=$ICONBRIDGE_CONFIG_DIR/keystore/tz.god.wallet.json
# export BSC_SECRET=$ICONBRIDGE_CONFIG_DIR/keystore/bsc.god.wallet.secret
815 changes: 815 additions & 0 deletions devnet/docker/icon-tezos/scripts/mainnet.i2t.bmr.sh

Large diffs are not rendered by default.

816 changes: 816 additions & 0 deletions devnet/docker/icon-tezos/scripts/testnet.i2t.bmr.sh

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
@@ -25,6 +25,7 @@ Get familiar with some terminologies used in icon-bridge [here.](terminologies.m
| /docker | Scripts to create docker containers (Some of it Obsolete) |
| /javascore | javascore smart contracts |
| /solidity | solidity smart contracts |
| /smartpy | tezos smart contracts |

>TLDR version of deployment is available [here](#tdlr)
53 changes: 53 additions & 0 deletions docs/icon-tezos/getting-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Getting Started
This section is for deployment of ICON-Tezos bridge in the testnet. This file also documents all the dependencies and prerequisits that are necessary for the bridge.

## Setting up on ICON
Install `goloop` cli for interacting with the ICON chain.

```
https://github.com/icon-project/goloop/blob/master/doc/build.md
```

## Setting up on Tezos
Install `octez-client`, cli tool in tezos for interacting with the Tezos chain.

```
https://opentezos.com/tezos-basics/cli-and-rpc/
```
Once the `octez-client` is all set, you will have to change the network that your `octez-client` is connected to.

```sh
octez-client --endpoint https://ghostnet.tezos.marigold.dev config update
```

## ICON Tezos Bridge Configuration(Testnet)
For the complete deployment, build and running the relay navigate to
```sh
$ cd $BASE_DIR/devnet/docker/icon-tezos/scripts
$ bash testnet.i2t.bmr.sh
```
When running the script, first wallets are created and we wil have to fund the wallets using the faucet of the respective chains.

For example you will get a message like this.
```
Fund the recently created wallet and run the script once again
icon bmc wallet: hxbc77026b7c3823744d5746507ab5bdb570ef9ca3
icon bts wallet: hx05256b36068144ec4523fd3943966ea018208ddb
icon bmr wallet: hx416d86b01f48ffe425a8a8a5f61182b1c17a339d
icon fa wallet : hxb7d5d680576de816459ad7b4af659886b2b0e4e3
tz bmr wallet : tz1dxhHuEcZNXoFyX3PX5A8NNpWnJ3MKHDY2
```
Fund the icon wallets using the faucet
```
https://faucet.iconosphere.io/
```
Fund the tezos wallets using the faucet
```
https://faucet.marigold.dev/
```

Rerun the script again and wait for the relay to start. This script will deploy the ICON smart contracts, Tezos smart contracts and register the native coin of the destination chains in its own chain.

```sh
$ bash testnet.i2t.bmr.sh
```
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ module github.com/icon-project/icon-bridge
go 1.13

require (
blockwatch.cc/tzgo v1.17.0
github.com/MuhammedIrfan/testify-mock v0.0.0-20220912121829-185fc90cd1b6
github.com/algorand/go-algorand-sdk v1.22.0
github.com/aws/aws-sdk-go v1.44.76
@@ -34,7 +35,7 @@ require (
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
github.com/tinylib/msgp v1.1.2 // indirect
github.com/vmihailenco/msgpack/v4 v4.3.11
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
golang.org/x/crypto v0.10.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0
)

66 changes: 56 additions & 10 deletions go.sum

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -105,7 +105,7 @@ public void setProperties(BMCProperties properties) {

@External(readonly = true)
public String name() {
return "BTP Message Center";
return "BTP Message Center-Tezos";
}

@External(readonly = true)
Original file line number Diff line number Diff line change
@@ -121,7 +121,7 @@ public BTPTokenService(Address _bmc, String _name, int _decimals,

@External(readonly = true)
public String name() {
return "BTP Token Service";
return "BTP Token Service-Tezos";
}

/**
@@ -289,7 +289,7 @@ public void addBlacklistAddress(String _net, String[] _addresses) {
List<String> blacklist = new ArrayList<>();

for (String addr: _addresses) {
addr = lowercase(addr);
addr = trimWhitespace(addr);
if (! isUserBlackListed(_net, addr) && addr.length() > 0) {
if (_net.equals(net) && !isValidIconAddress(addr)) {
continue;
@@ -1456,8 +1456,8 @@ private BigInteger increaseSn() {
return newSn;
}

private String lowercase(String word) {
return word.trim().toLowerCase();
private String trimWhitespace(String word) {
return word.trim();
}

private String[] getLinks() {
@@ -1481,4 +1481,4 @@ private void checkUintLimit(BigInteger value) {
require(UINT_CAP.compareTo(value) >= 0, "Value cannot exceed uint(256)-1");
}

}
}
Original file line number Diff line number Diff line change
@@ -46,8 +46,8 @@ public String at(String net, int index) {
return null;
}

private String lowercase(String user) {
return user.trim().toLowerCase();
private String trimWhitespace(String user) {
return user.trim();
}

public Integer indexOf(String net, String user) {
@@ -65,13 +65,13 @@ public Integer indexOf(String net, String user) {
public boolean contains(String net, String user) {
var a = blacklistIndex.at(net);
if (a != null) {
return a.get(lowercase(user)) != null;
return a.get(trimWhitespace(user)) != null;
}
return false;
}

public void addToBlacklist(String net, String user) {
user = lowercase(user);
user = trimWhitespace(user);
if (!contains(net, user)) {
blacklistedUsers.at(net).add(user);
int size = length(net);
@@ -80,7 +80,7 @@ public void addToBlacklist(String net, String user) {
}

public String removeFromBlacklist(String net, String user) {
user = lowercase(user);
user = trimWhitespace(user);
Integer valueIdx = indexOf(net, user);
var netUsers = blacklistedUsers.at(net);
var netIndex = blacklistIndex.at(net);
Original file line number Diff line number Diff line change
@@ -35,6 +35,7 @@ public class IRC2Tradeable extends IRC2Basic implements OwnerManager {

public IRC2Tradeable(String _name, String _symbol, int _decimals) {
super(_name, _symbol, _decimals);
addOwner(Context.getOrigin());
}

@External
@@ -144,4 +145,4 @@ public boolean isOwner(Address _addr) {

@EventLog(indexed = 2)
protected void Approval(Address owner, Address spender, BigInteger value) {};
}
}
77 changes: 77 additions & 0 deletions smartpy/bmc/compile.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@

#!/usr/bin/env bash

set -e -o pipefail

echo "----------------------------------------"
echo "Compiling contracts ... "
echo "----------------------------------------"

# Expected location of SmartPy CLI.
SMART_PY_CLI=~/smartpy-cli/SmartPy.sh

# Build artifact directory.
OUT_DIR=./contracts/build/.contract_build

# Array of SmartPy files to compile.
# CONTRACTS_ARRAY=(counter)

# Exit if SmartPy is not installed.
if [ ! -f "$SMART_PY_CLI" ]; then
echo "Fatal: Please install SmartPy CLI at $SMART_PY_CLI" && exit
fi

function processContract {
CONTRACT_NAME=$1
OUT_DIR=$2
CONTRACT_IN="./contracts/src/${CONTRACT_NAME}.py"
CONTRACT_OUT="${CONTRACT_NAME}.json"
STORAGE_OUT="${CONTRACT_NAME}_storage.json"
CONTRACT_COMPILED="${CONTRACT_NAME}/step_000_cont_0_contract.json"
STORAGE_COMPILED="${CONTRACT_NAME}/step_000_cont_0_storage.json"

echo ">> Processing ${CONTRACT_NAME}"

# Ensure file exists.
if [ ! -f "$CONTRACT_IN" ]; then
echo "Fatal: $CONTRACT_IN not found. Running from wrong dir?" && exit
fi

echo ">>> [1 / 3] Testing ${CONTRACT_NAME} ... "
$SMART_PY_CLI test $CONTRACT_IN $OUT_DIR --html

echo ">>> [2 / 3] Compiling ${CONTRACT_NAME} ..."
$SMART_PY_CLI compile $CONTRACT_IN $OUT_DIR --html

echo ">>> [3 / 3] Extracting Michelson contract ... "
cp $OUT_DIR/$CONTRACT_COMPILED ./contracts/build/$CONTRACT_OUT
cp $OUT_DIR/$STORAGE_COMPILED ./contracts/build/$STORAGE_OUT

echo ">>> Michelson contract written to ${CONTRACT_OUT}"
}

export PYTHONPATH=$PWD


echo "> [1 / 2] Unit Testing and Compiling Contracts."
# Use if you want to pass a contract or more as arguments.
for n in $(seq 1 $#); do
processContract $1 $OUT_DIR
shift
done

# Use if you want to compile all contracts in CONTRACTS_ARRAY. No arguments needed.
# for i in ${!CONTRACTS_ARRAY[@]}; do
# processContract ${CONTRACTS_ARRAY[$i]} $OUT_DIR
# done

# Remove build artifacts.
echo "> [2 / 2] Cleaning up ..."
rm -rf $OUT_DIR
rm -rf ./contracts/__pycache__
rm -rf ./__pycache__


echo "> Removed artifacts."

echo "> Compilation successful."
20 changes: 20 additions & 0 deletions smartpy/bmc/config/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// List your config files here

export const NETWORK = {
GHOSTNET: {
name: "ghostnet",
url: "https://ghostnet.smartpy.io",
},
KATHMANDUNET: {
name: "kathmandunet",
url: "https://kathmandunet.smartpy.io",
},
JAKARTANET: {
name: "jakartanet",
url: "https://jakartanet.smartpy.io",
},
MAINNET: {
name: "mainnet",
url: "https://mainnet.smartpy.io",
},
};
285 changes: 285 additions & 0 deletions smartpy/bmc/contracts/src/RLP_struct.py

Large diffs are not rendered by default.

51 changes: 51 additions & 0 deletions smartpy/bmc/contracts/src/String.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import smartpy as sp



def split_btp_address(base, prev_string, result_string, list_string, last_string, penultimate_string):
"""
Split the BTP Address format i.e. btp://1234.iconee/0x123456789
into Network_address (1234.iconee) and Server_address (0x123456789)
:param prev_string: local variable name
:param result_string: local variable name
:param list_string: local variable name
:param last_string: local variable name
:param penultimate_string: local variable name
:param base: String base BTP Address format to be split
:return: The resulting strings of Network_address and Server_address
"""
sp.set_type(base, sp.TString)

# sep = sp.local("sep", "/")
prev_idx = sp.local(prev_string, 0)
result = sp.local(result_string, [])
sp.for idx in sp.range(0, sp.len(base)):
sp.if sp.slice(base, idx, 1).open_some() == "/":
result.value.push(sp.slice(base, prev_idx.value, sp.as_nat(idx - prev_idx.value)).open_some())
prev_idx.value = idx + 1
sp.if sp.len(base) > 0:
result.value.push(sp.slice(base, prev_idx.value, sp.as_nat(sp.len(base) - prev_idx.value)).open_some())

inverted_list = sp.local(list_string, result.value)
last = sp.local(last_string, "")
penultimate = sp.local(penultimate_string, "")

with sp.match_cons(inverted_list.value) as l:
last.value = l.head
inverted_list.value = l.tail
# with sp.else_():
# sp.failwith("Empty list")


with sp.match_cons(inverted_list.value) as l:
penultimate.value = l.head
# with sp.else_():
# sp.failwith("Only one element")

return sp.pair(penultimate.value, last.value)






90 changes: 90 additions & 0 deletions smartpy/bmc/contracts/src/Types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import smartpy as sp


class Types:

MessageEvent = sp.TRecord(
next_bmc=sp.TString,
seq=sp.TNat,
message=sp.TBytes
)

ReceiptProof = sp.TRecord(
index=sp.TNat,
events=sp.TMap(sp.TNat, MessageEvent),
height=sp.TNat
)

BMCMessage = sp.TRecord(
src=sp.TString,
dst=sp.TString,
svc=sp.TString,
sn=sp.TInt,
message=sp.TBytes
)

MessageEvent = sp.TRecord(
next_bmc=sp.TString,
seq=sp.TNat,
message=sp.TBytes
)

Response = sp.TRecord(
code=sp.TNat,
message=sp.TString
)

Route = sp.TRecord(
dst=sp.TString,
next=sp.TString
)

Link = sp.TRecord(
relays=sp.TSet(sp.TAddress),
reachable=sp.TSet(sp.TString),
rx_seq=sp.TNat,
tx_seq=sp.TNat,
block_interval_src=sp.TNat,
block_interval_dst=sp.TNat,
max_aggregation=sp.TNat,
delay_limit=sp.TNat,
relay_idx=sp.TNat,
rotate_height=sp.TNat,
rx_height=sp.TNat,
rx_height_src=sp.TNat,
is_connected=sp.TBool
)

LinkStats = sp.TRecord(
rx_seq=sp.TNat,
tx_seq=sp.TNat,
rx_height=sp.TNat,
current_height=sp.TNat
)

BMCService = sp.TRecord(
serviceType=sp.TString,
payload=sp.TBytes
)

GatherFeeMessage = sp.TRecord(
fa=sp.TString,
svcs=sp.TMap(sp.TNat, sp.TString)
)

RelayStats = sp.TRecord(
addr=sp.TAddress,
block_count=sp.TNat,
msg_count=sp.TNat
)

Tuple = sp.TRecord(
prev=sp.TString,
to=sp.TString
)

Service = sp.TRecord(
svc=sp.TString,
addr=sp.TAddress
)

570 changes: 570 additions & 0 deletions smartpy/bmc/contracts/src/bmc_management.py

Large diffs are not rendered by default.

421 changes: 421 additions & 0 deletions smartpy/bmc/contracts/src/bmc_periphery.py

Large diffs are not rendered by default.

27 changes: 27 additions & 0 deletions smartpy/bmc/contracts/src/check_negative.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import smartpy as sp


@sp.module
def main():
class Convert(sp.Contract):

@sp.onchain_view()
def check_negative(self, x):
sp.cast(x, sp.bytes)
return sp.to_int(x) < 0

@sp.onchain_view()
def to_int(self, x):
sp.cast(x, sp.bytes)
return sp.to_int(x)

@sp.onchain_view()
def to_byte(self, x):
return sp.to_bytes(x)


@sp.add_test(name="test")
def test():
scenario = sp.test_scenario(main)
c = main.Convert()
scenario += c
77 changes: 77 additions & 0 deletions smartpy/bmc/contracts/src/helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import smartpy as sp

Utils = sp.io.import_script_from_url("https://raw.githubusercontent.com/Acurast/acurast-hyperdrive/main/contracts/tezos/libs/utils.py")


class Helper(sp.Contract):
def __init__(self):
self.init()

@sp.onchain_view()
def decode_string(self, params):
sp.set_type(params, sp.TBytes)
decode_string = sp.build_lambda(Utils.RLP.Decoder.decode_string)
sp.result(decode_string(params))

@sp.onchain_view()
def prefix_length(self, params):
sp.set_type(params, sp.TBytes)
prefix_length = sp.build_lambda(Utils.RLP.Decoder.prefix_length)
sp.result(prefix_length(params))

@sp.onchain_view()
def decode_list(self, params):
sp.set_type(params, sp.TBytes)
decode_list = sp.build_lambda(Utils.RLP.Decoder.decode_list)
sp.result(decode_list(params))

@sp.onchain_view()
def is_list(self, params):
sp.set_type(params, sp.TBytes)
is_list = sp.build_lambda(Utils.RLP.Decoder.is_list)
sp.result(is_list(params))

@sp.onchain_view()
def of_string(self, params):
sp.set_type(params, sp.TString)
encode_string_packed = sp.build_lambda(Utils.Bytes.of_string)
sp.result(encode_string_packed(params))

@sp.onchain_view()
def encode_string(self, params):
sp.set_type(params, sp.TString)
encode_string_packed = sp.build_lambda(Utils.RLP.Encoder.encode_string)
sp.result(encode_string_packed(params))

@sp.onchain_view()
def encode_nat(self, params):
sp.set_type(params, sp.TNat)
encode_nat_packed = sp.build_lambda(Utils.RLP.Encoder.encode_nat)
sp.result(encode_nat_packed(params))

@sp.onchain_view()
def of_nat(self, params):
sp.set_type(params, sp.TNat)
encode_nat_packed = sp.build_lambda(Utils.Bytes.of_nat)
sp.result(encode_nat_packed(params))

@sp.onchain_view()
def with_length_prefix(self, params):
sp.set_type(params, sp.TBytes)
encode_length_packed = sp.build_lambda(Utils.RLP.Encoder.with_length_prefix)
sp.result(encode_length_packed(params))

@sp.onchain_view()
def without_length_prefix(self, params):
sp.set_type(params, sp.TBytes)
decode = sp.build_lambda(Utils.RLP.Decoder.without_length_prefix)
sp.result(decode(params))

@sp.onchain_view()
def encode_list(self, params):
sp.set_type(params, sp.TList(sp.TBytes))
encode_list_packed = sp.build_lambda(Utils.RLP.Encoder.encode_list)
sp.result(encode_list_packed(params))


sp.add_compilation_target("helper", Helper())
345 changes: 345 additions & 0 deletions smartpy/bmc/contracts/tests/BMCManagement_test.py

Large diffs are not rendered by default.

402 changes: 402 additions & 0 deletions smartpy/bmc/contracts/tests/bmc_management_test.py

Large diffs are not rendered by default.

99 changes: 99 additions & 0 deletions smartpy/bmc/contracts/tests/bmc_periphery_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import smartpy as sp

BMCManagement = sp.io.import_script_from_url("file:./contracts/src/bmc_management.py")
BMCPeriphery = sp.io.import_script_from_url("file:./contracts/src/bmc_periphery.py")
BMCHelper = sp.io.import_script_from_url("file:./contracts/src/helper.py")
ParseAddress = sp.io.import_script_from_url("file:../bts/contracts/src/parse_address.py")


@sp.add_test("BMCPeripheryTest")
def test():
sc = sp.test_scenario()

# test account
alice = sp.test_account("Alice")
jack = sp.test_account("Jack")
helper2 = sp.test_account("Helper2")
owner = sp.test_account("owner")

helper_contract = deploy_helper_contract()
sc += helper_contract

# deploy BMCManagement contract
bmc_management_contract = deploy_bmc_management_contract(owner.address, helper_contract.address)
sc += bmc_management_contract

parse_address = deploy_parse_address()
sc += parse_address

bmc_periphery_contract = deploy_bmc_periphery_contract(
bmc_management_contract.address, helper_contract.address, helper2.address, parse_address.address)
sc += bmc_periphery_contract

# Scenario 1: Contract setters

# Test cases:
# 1: set_bmc_btp_address by non-owner
bmc_periphery_contract.set_bmc_btp_address("tezos.77").run(sender=alice, valid=False, exception="Unauthorized")

# 2: set_bmc_btp_address by owner
bmc_periphery_contract.set_bmc_btp_address("tezos.77").run(sender=bmc_management_contract.address)

# 3: verify value for bmc_btp_address
sc.verify(bmc_periphery_contract.data.bmc_btp_address ==
sp.string("btp://tezos.77/KT1Tezooo3zzSmartPyzzSTATiCzzzseJjWC"))

# 4: verify value for get_bmc_btp_address
sc.verify_equal(bmc_periphery_contract.get_bmc_btp_address(),
sp.string("btp://tezos.77/KT1Tezooo3zzSmartPyzzSTATiCzzzseJjWC"))

# 5: set_helper_address by non-owner
bmc_periphery_contract.set_helper_address(helper_contract.address).run(sender=jack, valid=False,
exception="Unauthorized")

# 6: set_helper_address by non-owner
bmc_periphery_contract.set_helper_address(helper_contract.address).run(sender=owner.address)

# 7: verify helper address
sc.verify(bmc_periphery_contract.data.helper == helper_contract.address)

# 8: set_parse_address by non-owner
bmc_periphery_contract.set_parse_address(parse_address.address).run(sender=jack, valid=False,
exception="Unauthorized")

# 8: set_parse_address by owner
bmc_periphery_contract.set_parse_address(parse_address.address).run(sender=owner.address)

# 9: verify parse_contract address
sc.verify(bmc_periphery_contract.data.parse_contract == parse_address.address)

# 10: set_bmc_management_addr by non-owner
bmc_periphery_contract.set_bmc_management_addr(bmc_management_contract.address).run(sender=jack, valid=False,
exception="Unauthorized")

# 11: set_bmc_management_addr by owner
bmc_periphery_contract.set_bmc_management_addr(bmc_management_contract.address).run(
sender=owner.address)

# 9: verifying bmc_management address
sc.verify(bmc_periphery_contract.data.bmc_management == bmc_management_contract.address)


def deploy_bmc_management_contract(owner, helper):
bmc_management_contract = BMCManagement.BMCManagement(owner, helper)
return bmc_management_contract


def deploy_bmc_periphery_contract(bmc_address, helper, helper2, parse):
bmc_periphery_contract = BMCPeriphery.BMCPreiphery(bmc_address, helper, helper2, parse)
return bmc_periphery_contract


def deploy_helper_contract():
helper_contract = BMCHelper.Helper()
return helper_contract


def deploy_parse_address():
parse_address = ParseAddress.ParseAddress()
return parse_address
33 changes: 33 additions & 0 deletions smartpy/bmc/contracts/tests/fa2_dummy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import smartpy as sp

FA2 = sp.io.import_script_from_url("https://legacy.smartpy.io/templates/fa2_lib.py")


class SingleAssetToken(FA2.Admin, FA2.Fa2SingleAsset, FA2.MintSingleAsset, FA2.BurnSingleAsset,
FA2.OnchainviewBalanceOf):
def __init__(self, admin, metadata, token_metadata):
FA2.Fa2SingleAsset.__init__(self, metadata=metadata, token_metadata=token_metadata)
FA2.Admin.__init__(self, admin)

@sp.onchain_view()
def is_admin(self, address):
sp.result(address == self.data.administrator)


# @sp.add_test(name="FA2DummyContract")
# def test():
# alice = sp.test_account("Alice")
#
# c1 = SingleAssetToken(admin=alice.address, metadata=sp.big_map({"ss": sp.bytes("0x0dae11")}),
# token_metadata=sp.map({"ff": sp.bytes("0x0dae11")}))
#
# scenario = sp.test_scenario()
# scenario += c1


sp.add_compilation_target("fa2_dummy",
SingleAssetToken(
admin=sp.address("tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP"),
metadata=sp.utils.metadata_of_url(
"ipfs://example"),
token_metadata=FA2.make_metadata(name="NativeWrappedCoin", decimals=6, symbol="WTEZ")))
725 changes: 725 additions & 0 deletions smartpy/bmc/contracts/tests/integration_test.py

Large diffs are not rendered by default.

1,431 changes: 1,431 additions & 0 deletions smartpy/bmc/package-lock.json

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions smartpy/bmc/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "create-tezos-app",
"version": "1.0.3",
"description": "A Tezos Dapp Starter using Typescript and Taquito.",
"bin": "./bin/cli.js",
"scripts": {
"compile": "bash ./compile.sh",
"deploy": "npx ts-node scripts/deploy.ts",
"test" : "bash ./test.sh"
},
"author": "Roshan Parajuli",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/0xrpj/create-tezos-dapp"
},
"dependencies": {
"@taquito/signer": "^14.0.0",
"@taquito/taquito": "^14.0.0",
"dotenv": "^16.0.3",
"ts-node": "^10.9.1"
},
"devDependencies": {
"typescript": "^4.8.4"
}
}
62 changes: 62 additions & 0 deletions smartpy/bmc/scripts/deploy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as dotenv from "dotenv";
import { TezosToolkit } from "@taquito/taquito";
import { InMemorySigner } from "@taquito/signer";
import { NETWORK } from "../config/config";

dotenv.config();

const deploy = async () => {
type networkTypes = keyof typeof NETWORK;

const { ORIGINATOR_PRIVATE_KEY } = process.env;
let file = process.argv[2];
let rpcType: networkTypes = process.argv[3]
?.toUpperCase()
.slice(1) as networkTypes;

console.info("Staring validation...");

if (!ORIGINATOR_PRIVATE_KEY) {
console.error("Private key missing in the environment.");
return;
}

if (!file) {
console.log("Pass filename to deploy! Read the docs.");
return;
}

if (!rpcType || !Object.keys(NETWORK).includes(rpcType)) {
console.log("Valid networks:", Object.keys(NETWORK));
console.error("Invalid network");
return;
}

file = file.toLowerCase();

const signer = await InMemorySigner.fromSecretKey(ORIGINATOR_PRIVATE_KEY);
const Tezos = new TezosToolkit(NETWORK[rpcType].url);
Tezos.setProvider({ signer: signer });

console.log(`Validation checks out... \nDeploying the ${file} contract..\n`);
try {
const { hash, contractAddress } = await Tezos.contract.originate({
code: require(`../contracts/build/${file}.json`),
init: require(`../contracts/build/${file}_storage.json`),
});

// console.log("Successfully deployed contract");
// console.log(`>> Transaction hash: ${hash}`);
console.log(`::${contractAddress}`);
} catch (error) {
console.log(
`Oops... Deployment faced an issue.\nHere's the detailed info about the error,\n${error}`
);

console.log(
"Make sure of these things. \n1. Compiled contracts are inside the build folder.\n2. You have enough Tezos balance in the wallet for deployment."
);
}
};

deploy();
102 changes: 102 additions & 0 deletions smartpy/bmc/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#!/usr/bin/env bash

set -e -o pipefail

echo "----------------------------------------"
echo "Compiling contracts ... "
echo "----------------------------------------"

# Expected location of SmartPy CLI.
SMART_PY_CLI=~/smartpy-cli/SmartPy.sh

# Build artifact directory.
TEST_OUT_DIR=./contracts/build/.contract_build/test

# Array of SmartPy files to compile.
# CONTRACTS_ARRAY=(counter)

# Exit if SmartPy is not installed.
if [ ! -f "$SMART_PY_CLI" ]; then
echo "Fatal: Please install SmartPy CLI at $SMART_PY_CLI" && exit
fi

function processContract {
CONTRACT_NAME=$1
TEST_OUT_DIR=$2
CONTRACT_IN_TEST="./contracts/tests/${CONTRACT_NAME}.py"
CONTRACT_OUT="${CONTRACT_NAME}.json"
STORAGE_OUT="${CONTRACT_NAME}_storage.json"
CONTRACT_COMPILED="${CONTRACT_NAME}/step_000_cont_0_contract.json"
STORAGE_COMPILED="${CONTRACT_NAME}/step_000_cont_0_storage.json"

echo ">> Processing ${CONTRACT_NAME}"

# Ensure file exists.
if [ ! -f "$CONTRACT_IN_TEST" ]; then
echo "Fatal: $CONTRACT_IN_TEST not found. Running from wrong dir?" && exit
fi

echo ">>> Commencing the tests ${CONTRACT_NAME} ... "
$SMART_PY_CLI test $CONTRACT_IN_TEST $TEST_OUT_DIR --html
}

export PYTHONPATH=$PWD


# Use if you want to pass a contract or more as arguments.
for n in $(seq 1 $#); do
processContract $1 $TEST_OUT_DIR
shift
done

#!/usr/bin/env bash

set -e -o pipefail

echo "----------------------------------------"
echo "Compiling contracts ... "
echo "----------------------------------------"

# Expected location of SmartPy CLI.
SMART_PY_CLI=~/smartpy-cli/SmartPy.sh

# Build artifact directory.
TEST_OUT_DIR=./contracts/build/.contract_build/test

# Array of SmartPy files to compile.
# CONTRACTS_ARRAY=(counter)

# Exit if SmartPy is not installed.
if [ ! -f "$SMART_PY_CLI" ]; then
echo "Fatal: Please install SmartPy CLI at $SMART_PY_CLI" && exit
fi

function processContract {
CONTRACT_NAME=$1
TEST_OUT_DIR=$2
CONTRACT_IN_TEST="./contracts/tests/${CONTRACT_NAME}_test.py"
CONTRACT_OUT="${CONTRACT_NAME}.json"
STORAGE_OUT="${CONTRACT_NAME}_storage.json"
CONTRACT_COMPILED="${CONTRACT_NAME}/step_000_cont_0_contract.json"
STORAGE_COMPILED="${CONTRACT_NAME}/step_000_cont_0_storage.json"

echo ">> Processing ${CONTRACT_NAME}"

# Ensure file exists.
if [ ! -f "$CONTRACT_IN_TEST" ]; then
echo "Fatal: $CONTRACT_IN_TEST not found. Running from wrong dir?" && exit
fi

echo ">>> Commencing the tests ${CONTRACT_NAME} ... "
$SMART_PY_CLI test $CONTRACT_IN_TEST $TEST_OUT_DIR --html
}

export PYTHONPATH=$PWD


# Use if you want to pass a contract or more as arguments.
for n in $(seq 1 $#); do
processContract $1 $TEST_OUT_DIR
shift
done

77 changes: 77 additions & 0 deletions smartpy/bts/compile.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@

#!/usr/bin/env bash

set -e -o pipefail

echo "----------------------------------------"
echo "Compiling contracts ... "
echo "----------------------------------------"

# Expected location of SmartPy CLI.
SMART_PY_CLI=~/smartpy-cli/SmartPy.sh

# Build artifact directory.
OUT_DIR=./contracts/build/.contract_build

# Array of SmartPy files to compile.
# CONTRACTS_ARRAY=(counter)

# Exit if SmartPy is not installed.
if [ ! -f "$SMART_PY_CLI" ]; then
echo "Fatal: Please install SmartPy CLI at $SMART_PY_CLI" && exit
fi

function processContract {
CONTRACT_NAME=$1
OUT_DIR=$2
CONTRACT_IN="./contracts/src/${CONTRACT_NAME}.py"
CONTRACT_OUT="${CONTRACT_NAME}.json"
STORAGE_OUT="${CONTRACT_NAME}_storage.json"
CONTRACT_COMPILED="${CONTRACT_NAME}/step_000_cont_0_contract.json"
STORAGE_COMPILED="${CONTRACT_NAME}/step_000_cont_0_storage.json"

echo ">> Processing ${CONTRACT_NAME}"

# Ensure file exists.
if [ ! -f "$CONTRACT_IN" ]; then
echo "Fatal: $CONTRACT_IN not found. Running from wrong dir?" && exit
fi

# echo ">>> [1 / 3] Testing ${CONTRACT_NAME} ... "
# $SMART_PY_CLI test $CONTRACT_IN $OUT_DIR --html

echo ">>> [1 / 2] Compiling ${CONTRACT_NAME} ..."
$SMART_PY_CLI compile $CONTRACT_IN $OUT_DIR --html

echo ">>> [2 / 2] Extracting Michelson contract ... "
cp $OUT_DIR/$CONTRACT_COMPILED ./contracts/build/$CONTRACT_OUT
cp $OUT_DIR/$STORAGE_COMPILED ./contracts/build/$STORAGE_OUT

echo ">>> Michelson contract written to ${CONTRACT_OUT}"
}

export PYTHONPATH=$PWD


echo "> [1 / 2] Compiling Contracts."
# Use if you want to pass a contract or more as arguments.
for n in $(seq 1 $#); do
processContract $1 $OUT_DIR
shift
done

# Use if you want to compile all contracts in CONTRACTS_ARRAY. No arguments needed.
# for i in ${!CONTRACTS_ARRAY[@]}; do
# processContract ${CONTRACTS_ARRAY[$i]} $OUT_DIR
# done

# Remove build artifacts.
echo "> [2 / 2] Cleaning up ..."
rm -rf $OUT_DIR
rm -rf ./contracts/__pycache__
rm -rf ./__pycache__


echo "> Removed artifacts."

echo "> Compilation successful."
20 changes: 20 additions & 0 deletions smartpy/bts/config/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// List your config files here

export const NETWORK = {
GHOSTNET: {
name: "ghostnet",
url: "https://ghostnet.smartpy.io",
},
KATHMANDUNET: {
name: "kathmandunet",
url: "https://kathmandunet.smartpy.io",
},
JAKARTANET: {
name: "jakartanet",
url: "https://jakartanet.smartpy.io",
},
MAINNET: {
name: "mainnet",
url: "https://mainnet.smartpy.io",
},
};
150 changes: 150 additions & 0 deletions smartpy/bts/contracts/src/FA2_contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import smartpy as sp

FA2 = sp.io.import_script_from_url("https://legacy.smartpy.io/templates/fa2_lib.py")
ZERO_ADDRESS = sp.address("tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg")

t_transfer_batch = sp.TRecord(
callback=sp.TContract(
sp.TRecord(string=sp.TOption(sp.TString), requester=sp.TAddress, coin_name=sp.TString, value=sp.TNat)),
from_=sp.TAddress,
coin_name=sp.TString,
txs=sp.TList(
sp.TRecord(
to_=sp.TAddress,
token_id=sp.TNat,
amount=sp.TNat,
).layout(("to_", ("token_id", "amount")))
),
).layout((("from_", "coin_name"), ("callback", "txs")))

t_transfer_params = sp.TList(t_transfer_batch)


class SingleAssetToken(FA2.Admin, FA2.Fa2SingleAsset, FA2.MintSingleAsset, FA2.BurnSingleAsset,
FA2.OnchainviewBalanceOf):
def __init__(self, admin, metadata, token_metadata):
FA2.Fa2SingleAsset.__init__(self, metadata=metadata, token_metadata=token_metadata)
FA2.Admin.__init__(self, admin)
self.update_initial_storage(
allowances=sp.big_map(tkey=sp.TRecord(spender=sp.TAddress, owner=sp.TAddress), tvalue=sp.TNat)
)

@sp.onchain_view()
def is_admin(self, address):
sp.result(address == self.data.administrator)

@sp.entry_point
def set_allowance(self, param):
sp.set_type(param, sp.TRecord(spender=sp.TAddress, amount=sp.TNat))

sp.verify(param.spender != ZERO_ADDRESS, "Spender cannot be negative.")
allowance = sp.compute(sp.record(spender=param.spender, owner=sp.sender))
self.data.allowances[allowance] = param.amount

sp.emit(sp.record(owner=sp.sender, spender=param.spender, amount=param.amount),
tag="AllowanceSet")

@sp.onchain_view()
def get_allowance(self, allowance):
sp.set_type(allowance, sp.TRecord(spender=sp.TAddress, owner=sp.TAddress))
sp.result(self.data.allowances.get(allowance, default_value=0))

@sp.onchain_view()
def transfer_permissions(self, params):
sp.set_type(params, sp.TRecord(from_=sp.TAddress, token_id=sp.TNat))

with sp.if_((self.policy.supports_transfer) & (self.is_defined(params.token_id))):
with sp.if_((sp.sender == params.from_) | (self.data.operators.contains(
sp.record(owner=params.from_, operator=sp.sender, token_id=params.token_id)))):
sp.result(True)
with sp.else_():
sp.result(False)
with sp.else_():
sp.result(False)

@sp.entry_point
def transfer(self, batch):
"""Accept a list of transfer operations between a source and multiple
destinations.
Custom version with allowance system.
`transfer_tx_` must be defined in the child class.
"""
sp.set_type(batch, FA2.t_transfer_params)
if self.policy.supports_transfer:
with sp.for_("transfer", batch) as transfer:
with sp.for_("tx", transfer.txs) as tx:
# The ordering of sp.verify is important: 1) token_undefined, 2) transfer permission 3) balance
sp.verify(self.is_defined(tx.token_id), "FA2_TOKEN_UNDEFINED")
self.policy.check_tx_transfer_permissions(
self, transfer.from_, tx.to_, tx.token_id
)
with sp.if_(sp.sender != transfer.from_):
self.update_allowance_(sp.sender, transfer.from_, tx.token_id, tx.amount)
with sp.if_(tx.amount > 0):
self.transfer_tx_(transfer.from_, tx)
else:
sp.failwith("FA2_TX_DENIED")

def update_allowance_(self, spender, owner, token_id, amount):
allowance = sp.record(spender=spender, owner=owner)
self.data.allowances[allowance] = sp.as_nat(self.data.allowances.get(allowance, default_value=0) - amount,
message=sp.pair("FA2_NOT_OPERATOR", "NoAllowance"))


@sp.add_test(name="FA2Contract")
def test():
alice = sp.test_account("Alice")
bob = sp.test_account("Bob")
spender = sp.test_account("spender")
receiver = sp.test_account("receiver")

c1 = SingleAssetToken(admin=alice.address, metadata=sp.big_map({"ss": sp.bytes("0x0dae11")}),
token_metadata=sp.map({"ff": sp.bytes("0x0dae11")}))

scenario = sp.test_scenario()
scenario.h1("FA2Contract")
scenario += c1

c1.mint([sp.record(to_=bob.address, amount=sp.nat(200))]).run(sender=alice)
scenario.verify(c1.data.ledger.get(bob.address) == sp.nat(200))

scenario.verify(c1.get_allowance(sp.record(spender=spender.address, owner=bob.address)) == 0)
# set allowance
c1.set_allowance(sp.record(spender=spender.address, amount=sp.nat(100))).run(sender=bob)

scenario.verify(c1.get_allowance(sp.record(spender=spender.address, owner=bob.address)) == 100)
# increase allowance
c1.set_allowance(sp.record(spender=spender.address, amount=sp.nat(100))).run(sender=bob)
# verify new allowance
scenario.verify(c1.get_allowance(sp.record(spender=spender.address, owner=bob.address)) == 100)
# decrease allowance
c1.set_allowance(sp.record(spender=spender.address, amount=sp.nat(20))).run(sender=bob)
# verify new allowance
scenario.verify(c1.get_allowance(sp.record(spender=spender.address, owner=bob.address)) == 20)
# decrease allowance
c1.set_allowance(sp.record(spender=spender.address, amount=sp.nat(0))).run(sender=bob)
# verify new allowance
scenario.verify(c1.get_allowance(sp.record(spender=spender.address, owner=bob.address)) == 0)

c1.update_operators(
[sp.variant("add_operator", sp.record(owner=bob.address, operator=spender.address, token_id=0))]).run(
sender=bob)
# transfer more than allowance
# c1.transfer([sp.record(callback=sp.self_entry_point("callback"), from_=bob.address, txs=[sp.record(to_=receiver.address, token_id=0, amount=101)])]).run(
# sender=spender, valid=False, exception=('FA2_NOT_OPERATOR', 'NoAllowance'))
# # transfer all allowance
# c1.transfer([sp.record(from_=bob.address, txs=[sp.record(to_=receiver.address, token_id=0, amount=100)])]).run(
# sender=spender)

# # verify remaining allowance
# scenario.verify(c1.get_allowance(sp.record(spender=spender.address, owner=bob.address)) == 0)
# # again set allowance after prev-allowance is 0
# c1.set_allowance([sp.record(spender=spender.address, amount=sp.nat(100))]).run(sender=bob)


sp.add_compilation_target("FA2_contract",
SingleAssetToken(
admin=sp.address("tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP"),
metadata=sp.utils.metadata_of_url(
"ipfs://example"),
token_metadata=FA2.make_metadata(name="NativeWrappedCoin", decimals=6, symbol="WTEZ")))
315 changes: 315 additions & 0 deletions smartpy/bts/contracts/src/RLP_struct.py

Large diffs are not rendered by default.

44 changes: 44 additions & 0 deletions smartpy/bts/contracts/src/String.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import smartpy as sp

def split_btp_address(base):
"""
Split the BTP Address format i.e. btp://1234.iconee/0x123456789
into Network_address (1234.iconee) and Server_address (0x123456789)
:param base: String base BTP Address format to be split
:return: The resulting strings of Network_address and Server_address
"""
sp.set_type(base, sp.TString)

sep = sp.local("sep", "/")
prev_idx = sp.local('prev_idx', 0)
res = sp.local('res', [])
sp.for idx in sp.range(0, sp.len(base)):
sp.if sp.slice(base, idx, 1).open_some() == sep.value:
res.value.push(sp.slice(base, prev_idx.value, sp.as_nat(idx - prev_idx.value)).open_some())
prev_idx.value = idx + 1
sp.if sp.len(base) > 0:
res.value.push(sp.slice(base, prev_idx.value, sp.as_nat(sp.len(base) - prev_idx.value)).open_some())

inverted_list = sp.local("my_list", res.value)
last = sp.local("last", "")
penultimate = sp.local("penultimate", "")

with sp.match_cons(inverted_list.value) as l:
last.value = l.head
inverted_list.value = l.tail
# with sp.else_():
# sp.failwith("Empty list")


with sp.match_cons(inverted_list.value) as l:
penultimate.value = l.head
# with sp.else_():
# sp.failwith("Only one element")
# TODO: should return true false
return sp.pair(penultimate.value, last.value)






77 changes: 77 additions & 0 deletions smartpy/bts/contracts/src/Types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import smartpy as sp


class Types:

Coin = sp.TRecord(addr=sp.TAddress,
fee_numerator=sp.TNat,
fixed_fee=sp.TNat,
coin_type=sp.TNat
)

Asset = sp.TRecord(
coin_name=sp.TString,
value=sp.TNat
)

AssetTransferDetail = sp.TRecord(
coin_name=sp.TString,
value=sp.TNat,
fee=sp.TNat
)

Response = sp.TRecord(
code=sp.TNat,
message=sp.TString
)

ServiceType = sp.TVariant(
REQUEST_COIN_TRANSFER=sp.TNat,
REQUEST_COIN_REGISTER=sp.TNat,
RESPONSE_HANDLE_SERVICE=sp.TNat,
BLACKLIST_MESSAGE=sp.TNat,
CHANGE_TOKEN_LIMIT=sp.TNat,
UNKNOWN_TYPE=sp.TNat,
ERROR=sp.TNat
)

BlacklistService = sp.TVariant(
ADD_TO_BLACKLIST=sp.TNat,
REMOVE_FROM_BLACKLIST=sp.TNat,
ERROR=sp.TNat
)

ServiceMessage = sp.TRecord(
serviceType=ServiceType,
data=sp.TBytes
)

TransferCoin = sp.TRecord(
from_addr=sp.TString,
to=sp.TString,
assets=sp.TList(Asset)
)

PendingTransferCoin = sp.TRecord(
from_=sp.TString,
to=sp.TString,
coin_details=sp.TList(sp.TRecord(coin_name=sp.TString, value=sp.TNat, fee=sp.TNat))
)

BlacklistMessage = sp.TRecord(
serviceType=BlacklistService,
addrs=sp.TMap(sp.TNat, sp.TString),
net=sp.TString
)

TokenLimitMessage = sp.TRecord(
coin_name=sp.TMap(sp.TNat, sp.TString),
token_limit=sp.TMap(sp.TNat, sp.TNat),
net=sp.TString
)

Balance = sp.TRecord(
locked_balance=sp.TNat,
refundable_balance=sp.TNat
)

757 changes: 757 additions & 0 deletions smartpy/bts/contracts/src/bts_core.py

Large diffs are not rendered by default.

68 changes: 68 additions & 0 deletions smartpy/bts/contracts/src/bts_owner_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import smartpy as sp


class BTSOwnerManager(sp.Contract):
def __init__(self, owner):
self.init(
owners=sp.map({owner: True}),
)
self.init_type(sp.TRecord(
owners=sp.TMap(sp.TAddress, sp.TBool),
))

def only_owner(self):
sp.verify(self.data.owners[sp.sender] == True, message="Unauthorized")

@sp.entry_point
def add_owner(self, owner):
"""
:param owner: address to set as owner
:return:
"""
sp.set_type(owner, sp.TAddress)

self.only_owner()
sp.verify(self.data.owners[owner] == False, message="ExistedOwner")

self.data.owners[owner] = True
sp.emit(sp.record(sender=sp.sender, owner=owner), tag="NewOwnerAdded")

@sp.entry_point
def remove_owner(self, owner):
"""
:param owner: address to remove as owner
:return:
"""
sp.set_type(owner, sp.TAddress)

self.only_owner()
sp.verify(sp.len(self.data.owners) > 1, message="CannotRemoveMinOwner")
sp.verify(self.data.owners[owner] == True, message="NotOwner")

del self.data.owners[owner]
sp.emit(sp.record(sender=sp.sender, owner=owner), tag="OwnerRemoved")

@sp.onchain_view()
def is_owner(self, owner):
sp.set_type(owner, sp.TAddress)
sp.result(self.data.owners.get(owner, default_value=False))

@sp.onchain_view()
def get_owners(self):
sp.result(self.data.owners.keys())


@sp.add_test(name="BTSOwnerManager")
def test():
alice = sp.test_account("Alice")
c1 = BTSOwnerManager(alice.address)
scenario = sp.test_scenario()
scenario.h1("BTSOwnerManager")
scenario += c1

scenario.verify(c1.is_owner(alice.address) == True)


sp.add_compilation_target("bts_owner_manager", BTSOwnerManager(owner=sp.address("tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP")))


599 changes: 599 additions & 0 deletions smartpy/bts/contracts/src/bts_periphery.py

Large diffs are not rendered by default.

77 changes: 77 additions & 0 deletions smartpy/bts/contracts/src/helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import smartpy as sp

Utils = sp.io.import_script_from_url("https://raw.githubusercontent.com/Acurast/acurast-hyperdrive/main/contracts/tezos/libs/utils.py")


class Helper(sp.Contract):
def __init__(self):
self.init()

@sp.onchain_view()
def decode_string(self, params):
sp.set_type(params, sp.TBytes)
decode_string = sp.build_lambda(Utils.RLP.Decoder.decode_string)
sp.result(decode_string(params))

@sp.onchain_view()
def prefix_length(self, params):
sp.set_type(params, sp.TBytes)
prefix_length = sp.build_lambda(Utils.RLP.Decoder.prefix_length)
sp.result(prefix_length(params))

@sp.onchain_view()
def decode_list(self, params):
sp.set_type(params, sp.TBytes)
decode_list = sp.build_lambda(Utils.RLP.Decoder.decode_list)
sp.result(decode_list(params))

@sp.onchain_view()
def is_list(self, params):
sp.set_type(params, sp.TBytes)
is_list = sp.build_lambda(Utils.RLP.Decoder.is_list)
sp.result(is_list(params))

@sp.onchain_view()
def of_string(self, params):
sp.set_type(params, sp.TString)
encode_string_packed = sp.build_lambda(Utils.Bytes.of_string)
sp.result(encode_string_packed(params))

@sp.onchain_view()
def encode_string(self, params):
sp.set_type(params, sp.TString)
encode_string_packed = sp.build_lambda(Utils.RLP.Encoder.encode_string)
sp.result(encode_string_packed(params))

@sp.onchain_view()
def encode_nat(self, params):
sp.set_type(params, sp.TNat)
encode_nat_packed = sp.build_lambda(Utils.RLP.Encoder.encode_nat)
sp.result(encode_nat_packed(params))

@sp.onchain_view()
def of_nat(self, params):
sp.set_type(params, sp.TNat)
encode_nat_packed = sp.build_lambda(Utils.Bytes.of_nat)
sp.result(encode_nat_packed(params))

@sp.onchain_view()
def with_length_prefix(self, params):
sp.set_type(params, sp.TBytes)
encode_length_packed = sp.build_lambda(Utils.RLP.Encoder.with_length_prefix)
sp.result(encode_length_packed(params))

@sp.onchain_view()
def without_length_prefix(self, params):
sp.set_type(params, sp.TBytes)
decode = sp.build_lambda(Utils.RLP.Decoder.without_length_prefix)
sp.result(decode(params))

@sp.onchain_view()
def encode_list(self, params):
sp.set_type(params, sp.TList(sp.TBytes))
encode_list_packed = sp.build_lambda(Utils.RLP.Encoder.encode_list)
sp.result(encode_list_packed(params))


sp.add_compilation_target("helper", Helper())
192 changes: 192 additions & 0 deletions smartpy/bts/contracts/src/parse_address.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import smartpy as sp
Utils = sp.io.import_script_from_url("https://raw.githubusercontent.com/RomarQ/tezos-sc-utils/main/smartpy/utils.py")


class ParseAddress(sp.Contract):
tz_prefixes = sp.map({
sp.bytes('0x0000'): sp.string('tz1'),
sp.bytes('0x0001'): sp.string('tz2'),
sp.bytes('0x0002'): sp.string('tz3'),
sp.bytes('0x0003'): sp.string('tz4')
})
base58_encodings = sp.list([
sp.map({"prefix": "tz1", "elem1": "6", "elem2": "161", "elem3": "159", "len": "20"}),
sp.map({"prefix": "tz2", "elem1": "6", "elem2": "161", "elem3": "161", "len": "20"}),
sp.map({"prefix": "tz3", "elem1": "6", "elem2": "161", "elem3": "164", "len": "20"}),
sp.map({"prefix": "tz4", "elem1": "6", "elem2": "161", "elem3": "16", "len": "20"}),
sp.map({"prefix": "KT1", "elem1": "2", "elem2": "90", "elem3": "121", "len": "20"}),
])

def __init__(self):
self.init()

def unforge_address(self, data):
"""Decode address or key_hash from bytes.
:param data: encoded address or key_hash
:returns: base58 encoded address
"""
sp.set_type(data, sp.TBytes)
byt = sp.slice(data, 6, 22).open_some()
prefix = sp.slice(byt, 0, 2).open_some()
starts_with = sp.slice(byt, 0, 1).open_some()
ends_with = sp.slice(byt, 21, 1).open_some()
sliced_byte = sp.slice(byt, 1, 20).open_some()
local_byte = sp.local("local_byte", sp.bytes("0x"))
return_value = sp.local("return_value", "tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg", sp.TString)

sp.for item in self.tz_prefixes.items():
sp.if item.key == prefix:
return_value.value = self.base58_encode(sp.slice(byt, 2, 20).open_some(), Utils.Bytes.of_string(item.value), local_byte)

sp.if (starts_with == sp.bytes("0x01")) & (ends_with == sp.bytes("0x00")):
return_value.value = self.base58_encode(sliced_byte, Utils.Bytes.of_string("KT1"), local_byte)
sp.if (starts_with == sp.bytes("0x02")) & (ends_with == sp.bytes("0x00")):
return_value.value = self.base58_encode(sliced_byte, Utils.Bytes.of_string("txr1"), local_byte)
sp.if (starts_with == sp.bytes("0x03")) & (ends_with == sp.bytes("0x00")):
return_value.value = self.base58_encode(sliced_byte, Utils.Bytes.of_string("sr1"), local_byte)

return return_value.value

def tb(self, _list):
byte_str = sp.local("byte_str", sp.bytes("0x"))
sp.for num in _list:
byte_str.value += Utils.Bytes.of_nat(num)
return byte_str.value

def base58_encode(self, byt_array, prefix, _byte):
"""
Encode data using Base58 with checksum and add an according binary prefix in the end.
:param byt_array: Array of bytes
:param prefix: Human-readable prefix (use b'') e.g. b'tz', b'KT', etc
:param local_byte: local variable
:returns: bytes (use string.decode())
"""
length_v = sp.to_int(sp.len(byt_array))
encoding = sp.local("encode", sp.map({}))
byte_from_tbl = sp.local("byte_from_tbl", sp.bytes("0x"))
byte_value = _byte

sp.for enc in self.base58_encodings:
sp.if (length_v == Utils.Int.of_string(enc["len"])) & (prefix == Utils.Bytes.of_string(enc["prefix"])):
encoding.value = enc
byte_from_tbl.value = self.tb([sp.as_nat(Utils.Int.of_string(enc["elem1"])),
sp.as_nat(Utils.Int.of_string(enc["elem2"])),
sp.as_nat(Utils.Int.of_string(enc["elem3"]))])
sha256_encoding = sp.sha256(sp.sha256(byte_from_tbl.value + byt_array))
sha256_encoding = byte_from_tbl.value + byt_array + sp.slice(sha256_encoding, 0, 4).open_some()
acc = sp.local("for_while_loop", Utils.Int.of_bytes(sha256_encoding))
alphabet = Utils.Bytes.of_string("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
base = 58
sp.while acc.value > 0:
(acc.value, idx) = sp.match_pair(sp.ediv(acc.value, base).open_some())
byte_value.value = sp.slice(alphabet, idx, 1).open_some() + byte_value.value

return sp.unpack(sp.bytes("0x050100000024") + byte_value.value, sp.TString).open_some()

@sp.onchain_view()
def add_to_str(self, params):
sp.set_type(params, sp.TAddress)
sp.result(self.unforge_address(sp.pack(params)))

def _to_addr(self, params):
string_in_bytes = sp.pack(params)
actual_prefix = sp.local("actual_prefix", "")
addr = sp.local("addr", sp.address("tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg"))
alphabet = Utils.Bytes.of_string("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
sp.if sp.len(string_in_bytes) == sp.nat(42):
string_in_bytes = sp.slice(string_in_bytes, 6, 36).open_some()
element_list = sp.range(0, sp.len(alphabet), 1)
temp_map = sp.local("temp_map", {})
temp_var = sp.local("y", 0)
sp.for elem in element_list:
temp_var.value = elem
temp_map.value[sp.slice(alphabet, temp_var.value, 1).open_some()] = temp_var.value
decimal = sp.local("decimal", 0)
base = sp.len(alphabet)
element_list_2 = sp.range(0, sp.len(string_in_bytes), 1)

sp.for elem in element_list_2:
decimal.value = decimal.value * base + temp_map.value[sp.slice(string_in_bytes, elem, 1).open_some()]
byt_value = Utils.Bytes.of_nat(sp.as_nat(sp.to_int(decimal.value)))
new_byt_value = sp.slice(byt_value, 0, sp.as_nat(sp.len(byt_value) - 4)).open_some()
prefix = sp.slice(new_byt_value, 0, 3).open_some()
prefix_len = sp.range(0, sp.len(prefix), 1)
temp_var3 = sp.local("z", 0)
list_string = sp.local("list_string", [])
sp.for x in prefix_len:
temp_var3.value = x
list_string.value.push(Utils.Int.of_bytes(sp.slice(prefix, temp_var3.value, 1).open_some()))
value = sp.slice(new_byt_value, 3, sp.as_nat(sp.len(new_byt_value) - 3))
byte_local = sp.local("byt_old", sp.bytes("0x"))

sp.for enc in self.base58_encodings:
byte_local.value = self.tb([sp.as_nat(Utils.Int.of_string(enc["elem1"])),
sp.as_nat(Utils.Int.of_string(enc["elem2"])),
sp.as_nat(Utils.Int.of_string(enc["elem3"]))])
sp.if byte_local.value == prefix:
actual_prefix.value = enc["prefix"]

sp.for item in self.tz_prefixes.items():
sp.if item.value == actual_prefix.value:
decoded_address = sp.unpack(sp.bytes("0x050a00000016") + item.key + value.open_some(),
sp.TAddress)
addr.value = decoded_address.open_some()
sp.if actual_prefix.value == "KT1":
decoded_address = sp.unpack(
sp.bytes("0x050a00000016") + sp.bytes("0x01") + value.open_some() + sp.bytes("0x00"),
sp.TAddress)
addr.value = decoded_address.open_some()
sp.if actual_prefix.value == "txr1":
decoded_address = sp.unpack(
sp.bytes("0x050a00000016") + sp.bytes("0x02") + value.open_some() + sp.bytes("0x00"),
sp.TAddress)
addr.value = decoded_address.open_some()
sp.if actual_prefix.value == "sr1":
decoded_address = sp.unpack(
sp.bytes("0x050a00000016") + sp.bytes("0x03") + value.open_some() + sp.bytes("0x00"),
sp.TAddress)
addr.value = decoded_address.open_some()

sp.if actual_prefix.value == "":
addr.value = sp.address("tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg")
with sp.else_():
addr.value = sp.address("tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg")

return addr.value

@sp.onchain_view()
def str_to_addr(self, params):
sp.set_type(params, sp.TString)
sp.result(self._to_addr(params))

@sp.onchain_view()
def string_of_int(self, params):
sp.set_type(params, sp.TInt)
sp.result(Utils.String.of_int(params))


@sp.add_test(name="Conversion")
def test():
alice=sp.test_account("Alice")
c1 = ParseAddress()
scenario = sp.test_scenario()
scenario.h1("Conversion")
scenario += c1
c1.add_to_str(sp.address("tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP"))
scenario.verify(c1.add_to_str(sp.address("KT1FfkTSts5DnvyJp2qZbPMeqm2XpMYES7Vr")) == "KT1FfkTSts5DnvyJp2qZbPMeqm2XpMYES7Vr")
scenario.verify(c1.add_to_str(sp.address("tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP")) == "tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP")

c1.str_to_addr("tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP")
c1.str_to_addr("KT1FfkTSts5DnvyJp2qZbPMeqm2XpMYES7Vr")
scenario.verify(c1.str_to_addr("KT1FfkTSts5DnvyJp2qZbPMeqm2XpMYES7Vr") == sp.address("KT1FfkTSts5DnvyJp2qZbPMeqm2XpMYES7Vr"))
scenario.verify(c1.str_to_addr("tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP") == sp.address("tz1g3pJZPifxhN49ukCZjdEQtyWgX2ERdfqP"))
# invalid address
scenario.verify(c1.str_to_addr("tz1g3pJZPifxhN") == sp.address("tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg"))



sp.add_compilation_target("parse_address", ParseAddress())


125 changes: 125 additions & 0 deletions smartpy/bts/contracts/tests/bts_periphery_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import smartpy as sp

BTSPeriphery = sp.io.import_script_from_url("file:./contracts/src/bts_periphery.py")
BTSCore = sp.io.import_script_from_url("file:./contracts/src/bts_core.py")
BTSOwnerManager = sp.io.import_script_from_url("file:./contracts/src/bts_owner_manager.py")
ParseAddress = sp.io.import_script_from_url("file:./contracts/src/parse_address.py")
Helper = sp.io.import_script_from_url("file:./contracts/src/helper.py")


@sp.add_test("BTSPeripheryTest")
def test():
sc = sp.test_scenario()

# test account
admin = sp.test_account('admin')

def deploy_bts_periphery_contract(core_address, helper, parse):
bmc = sp.test_account("bmc")
_bts_periphery_contract = BTSPeriphery.BTSPeriphery(
bmc_address=bmc.address, bts_core_address=core_address, helper_contract=helper, parse_address=parse,
native_coin_name='NativeCoin', owner_address=admin.address)
return _bts_periphery_contract

def deploy_parse_contract():
_bts_parse_contract = ParseAddress.ParseAddress()
return _bts_parse_contract

def deploy_helper():
_bts_helper = Helper.Helper()
return _bts_helper

def deploy_bts_core_contract(_bts_owner_manager_contract):
_bts_core_contract = BTSCore.BTSCore(
owner_manager=_bts_owner_manager_contract,
_native_coin_name="Tok1",
_fee_numerator=sp.nat(1000),
_fixed_fee=sp.nat(10))
return _bts_core_contract

def deploy_bts_owner_manager_contract():
_bts_owner_manager_contract = BTSOwnerManager.BTSOwnerManager(admin.address)
return _bts_owner_manager_contract

bts_owner_manager_contract = deploy_bts_owner_manager_contract()
sc += bts_owner_manager_contract

bts_core_contract = deploy_bts_core_contract(bts_owner_manager_contract.address)
sc += bts_core_contract

bts_helper_contract = deploy_helper()
sc += bts_helper_contract

bts_parse_contract = deploy_parse_contract()
sc += bts_parse_contract

# deploy bts_periphery contract
bts_periphery_contract = deploy_bts_periphery_contract(bts_core_contract.address, bts_helper_contract.address,
bts_parse_contract.address)
sc += bts_periphery_contract

bts_core_contract.update_bts_periphery(bts_periphery_contract.address).run(sender=admin.address)

# Scenario 1: set token limit

# Test cases:
# 1: set_token_limit from non-bts_periphery contract
bts_periphery_contract.set_token_limit(sp.map({"Tok2": sp.nat(5), "BB": sp.nat(2)})).run(sender=admin.address,
valid=False,
exception='Unauthorized')

# 2: set token limit for Tok2 coin to 5 and BB coin to 2 from bts_periphery_contract
bts_periphery_contract.set_token_limit(sp.map({"Tok2": sp.nat(5), "BB": sp.nat(2)})).run(
sender=bts_core_contract.address)

# 3: verifying the value of token limit
sc.verify(bts_periphery_contract.data.token_limit["Tok2"] == sp.nat(5))

# 4: modify already set data
bts_periphery_contract.set_token_limit(sp.map({"Tok2": sp.nat(15), "BB": sp.nat(22)})).run(
sender=bts_core_contract.address)

# 5: verifying the value of token limit after change
sc.verify(bts_periphery_contract.data.token_limit["BB"] == sp.nat(22))

# Scenario 2: send service message

# Test cases:
# 1 : send_service_message called by bts-core
bts_periphery_contract.send_service_message(
sp.record(
_from=sp.address("tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnW"),
to="btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnW",
coin_details=[
sp.record(coin_name="Tok1", value=sp.nat(10), fee=sp.nat(2))
]
)).run(sender=bts_core_contract.address).run(sender=bts_core_contract.address)

# 2 : send_service_message called by non-bts-core
bts_periphery_contract.send_service_message(
sp.record(
_from=sp.address("tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnW"),
to="btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnW",
coin_details=[
sp.record(coin_name="Tok1", value=sp.nat(10), fee=sp.nat(2))
]
)).run(sender=admin, valid=False, exception='Unauthorized')

# 3: verify if request message is correct
sc.show(bts_periphery_contract.data.requests[1])
sc.verify_equal(
bts_periphery_contract.data.requests[1],
sp.record(
coin_details=[
sp.record(
coin_name='Tok1',
value=10,
fee=2
)
],
from_='tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnW',
to='btp://77.tezos/tz1e2HPzZWBsuExFSM4XDBtQiFnaUB5hiPnW'))

# 4: verify request data
sc.verify(bts_periphery_contract.data.number_of_pending_requests == 1)
sc.verify(bts_periphery_contract.data.serial_no == 1)
225 changes: 225 additions & 0 deletions smartpy/bts/contracts/tests/btscore_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
import smartpy as sp

BTSCore = sp.io.import_script_from_url("file:./contracts/src/bts_core.py")
BTSOwnerManager = sp.io.import_script_from_url("file:./contracts/src/bts_owner_manager.py")
BTSPeriphery = sp.io.import_script_from_url("file:./contracts/src/bts_periphery.py")
BMCHelper = sp.io.import_script_from_url("file:./contracts/src/helper.py")
ParseAddress = sp.io.import_script_from_url("file:./contracts/src/parse_address.py")


@sp.add_test("BTSCoreTest")
def test():
sc = sp.test_scenario()
sc.h1("BTSCore")

# test account
bob = sp.test_account("Bob")
owner = sp.test_account("owner")

# deploy BTSCore contract
bts_owner_manager = deploy_bts_owner_manager_contract(owner.address)
sc += bts_owner_manager
bts_core_contract = deploy_bts_core_contract(bts_owner_manager.address)
sc += bts_core_contract

helper_contract = deploy_helper_contract()
sc += helper_contract

parse_address = deploy_parse_address()
sc += parse_address

bts_periphery = deploy_bts_periphery_contract(bts_core_contract.address, helper_contract.address,
parse_address.address, owner.address)
sc += bts_periphery
fa2 = deploy_fa2_contract(bts_periphery.address)
sc += fa2

# Scenario 1: BTSCore Scenario

# Test cases:
# 1: verify owner manager
sc.verify_equal(bts_core_contract.data.bts_owner_manager, bts_owner_manager.address)

# 2: verify update_bts_periphery function
bts_core_contract.update_bts_periphery(bts_periphery.address).run(sender=owner.address)
sc.verify_equal(bts_core_contract.data.bts_periphery_address, sp.some(bts_periphery.address))

# Scenario 2: set_fee_ratio

# Test cases:
# 1: token doesn't exist
bts_core_contract.set_fee_ratio(name=sp.string("ABC"), fee_numerator=sp.nat(100), fixed_fee=sp.nat(10)).run(
sender=owner.address, valid=False, exception='TokenNotExists')

# 2: fee numerator is greater than denominator
bts_core_contract.set_fee_ratio(name=sp.string("BTSCOIN"), fee_numerator=sp.nat(10000), fixed_fee=sp.nat(10)).run(
sender=owner.address, valid=False, exception='InvalidSetting')

# 3: fixed fee is 0
bts_core_contract.set_fee_ratio(name=sp.string("BTSCOIN"), fee_numerator=sp.nat(100), fixed_fee=sp.nat(0)).run(
sender=owner.address, valid=False, exception='LessThan0')

# 4: valid condition
bts_core_contract.set_fee_ratio(name=sp.string("BTSCOIN"), fee_numerator=sp.nat(100), fixed_fee=sp.nat(10)).run(
sender=owner.address)

# 5: set_fee_ratio called by non-owner
bts_core_contract.set_fee_ratio(name=sp.string("BTSCOIN"), fee_numerator=sp.nat(100), fixed_fee=sp.nat(10)).run(
sender=bob.address, valid=False, exception='Unauthorized')

# 5: verify fees
sc.verify_equal(bts_core_contract.data.coin_details["BTSCOIN"].fee_numerator, 100)
sc.verify_equal(bts_core_contract.data.coin_details["BTSCOIN"].fixed_fee, 10)

# Scenario 2: register

# Test cases:
# 1: native coin (native_coin = BTSCOIN)
bts_core_contract.register(
name=sp.string("BTSCOIN"),
fee_numerator=sp.nat(10),
fixed_fee=sp.nat(2),
addr=sp.address("tz1VA29GwaSA814BVM7AzeqVzxztEjjxiMEc"),
token_metadata=sp.map({"token_metadata": sp.bytes("0x0dae11")}),
metadata=sp.big_map({"metadata": sp.bytes("0x0dae11")})
).run(sender=owner.address, valid=False, exception='ExistNativeCoin')

# 2: fee numerator is greater than denominator
bts_core_contract.register(
name=sp.string("new_coin1"),
fee_numerator=sp.nat(100000),
fixed_fee=sp.nat(2),
addr=fa2.address,
token_metadata=sp.map({"token_metadata": sp.bytes("0x0dae11")}),
metadata=sp.big_map({"metadata": sp.bytes("0x0dae11")})
).run(sender=owner.address, valid=False, exception='InvalidSetting')

# 3: valid case
bts_core_contract.register(
name=sp.string("new_coin"),
fee_numerator=sp.nat(10),
fixed_fee=sp.nat(2),
addr=fa2.address,
token_metadata=sp.map({"token_metadata": sp.bytes("0x0dae11")}),
metadata=sp.big_map({"metadata": sp.bytes("0x0dae11")})
).run(sender=owner.address)

# 4: verify the registered value
sc.verify_equal(bts_core_contract.data.coins_name, ['new_coin', 'BTSCOIN'])
sc.verify_equal(bts_core_contract.data.coin_details['new_coin'],
sp.record(addr=fa2.address, coin_type=2, fee_numerator=10, fixed_fee=2))

# 5: existing coin name
bts_core_contract.register(
name=sp.string("new_coin"),
fee_numerator=sp.nat(10),
fixed_fee=sp.nat(2),
addr=sp.address("tz1VA29GwaSA814BVM7AzeqVzxztEjjxiMEc"),
token_metadata=sp.map({"ff": sp.bytes("0x0dae11")}),
metadata=sp.big_map({"ff": sp.bytes("0x0dae11")})
).run(sender=owner.address, valid=False, exception="ExistCoin")

# 6: registered NON-NATIVE COIN_TYPE by non-owner
bts_core_contract.register(
name=sp.string("new_coin"),
fee_numerator=sp.nat(10),
fixed_fee=sp.nat(2),
addr=sp.address("tz1VA29GwaSA814BVM7AzeqVzxztEjjxiMEc"),
token_metadata=sp.map({"ff": sp.bytes("0x0dae11")}),
metadata=sp.big_map({"ff": sp.bytes("0x0dae11")})
).run(sender=bob.address, valid=False, exception="Unauthorized")

# 7: register NON-NATIVE COIN_TYPE
bts_core_contract.register(
name=sp.string("new_coin2"),
fee_numerator=sp.nat(10),
fixed_fee=sp.nat(2),
addr=fa2.address,
token_metadata=sp.map({"ff": sp.bytes("0x0dae11")}),
metadata=sp.big_map({"ff": sp.bytes("0x0dae11")})
).run(sender=owner.address, valid=False, exception="AddressExists")

# 8: register NATIVE WRAPPED COIN_TYPE
bts_core_contract.register(
name=sp.string("wrapped-coin"),
fee_numerator=sp.nat(10),
fixed_fee=sp.nat(2),
addr=sp.address("tz1ZZZZZZZZZZZZZZZZZZZZZZZZZZZZNkiRg"),
token_metadata=sp.map({"token_metadata": sp.bytes("0x0dae11")}),
metadata=sp.big_map({"metadata": sp.bytes("0x0dae11")})
).run(sender=owner.address)

# Scenario 3: contract getters

# Test cases:
# 1: verify coin_id functio
sc.verify_equal(bts_core_contract.coin_id('new_coin'), fa2.address)

# 2: verify is_valid_coin function
sc.verify_equal(bts_core_contract.is_valid_coin('new_coin'), True)
sc.verify_equal(bts_core_contract.is_valid_coin('not_valid'), False)

# 3: verify fee_ratio function
sc.verify_equal(bts_core_contract.fee_ratio('new_coin'), sp.record(fee_numerator=10, fixed_fee=2))

# 4: verify balance_of function
sc.verify_equal(
bts_core_contract.balance_of(
sp.record(owner=owner.address, coin_name='new_coin')
),
sp.record(usable_balance=0, locked_balance=0, refundable_balance=0, user_balance=0)
)

# 5: verify balance_of_batch function
bts_core_contract.balance_of_batch(
sp.record(owner=owner.address, coin_names=['new_coin', 'BTSCOIN'])
)
sc.verify_equal(
bts_core_contract.balance_of_batch(
sp.record(owner=owner.address, coin_names=['new_coin', 'BTSCOIN'])
),
[
sp.record(locked_balance=0, refundable_balance=0, usable_balance=0, user_balance=0),
sp.record(locked_balance=0, refundable_balance=0, usable_balance=0, user_balance=0)
]
)


def deploy_bts_core_contract(bts_owner_manager_contract):
bts_core_contract = BTSCore.BTSCore(
owner_manager=bts_owner_manager_contract,
_native_coin_name="BTSCOIN",
_fee_numerator=sp.nat(1000),
_fixed_fee=sp.nat(10)
)
return bts_core_contract


def deploy_bts_owner_manager_contract(owner):
bts_owner_manager_contract = BTSOwnerManager.BTSOwnerManager(owner)
return bts_owner_manager_contract


def deploy_bts_periphery_contract(core_address, helper, parse, owner):
bmc = sp.test_account("bmc")
bts_periphery_contract = BTSPeriphery.BTSPeriphery(
bmc_address=bmc.address, bts_core_address=core_address, helper_contract=helper, parse_address=parse,
native_coin_name='BTSCoin', owner_address=owner)
return bts_periphery_contract


def deploy_fa2_contract(admin_address):
fa2_contract = BTSCore.FA2_contract.SingleAssetToken(admin=admin_address,
metadata=sp.big_map({"ss": sp.bytes("0x0dae11")}),
token_metadata=sp.map({"ff": sp.bytes("0x0dae11")}))
return fa2_contract


def deploy_helper_contract():
helper_contract = BMCHelper.Helper()
return helper_contract


def deploy_parse_address():
parse_address = ParseAddress.ParseAddress()
return parse_address
Loading