Skip to content

Commit 8af0aeb

Browse files
committed
Terra integartion with Cosmos compat
1 parent 305a1bd commit 8af0aeb

34 files changed

+2869
-1
lines changed

chain/cosmos/address.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package cosmos
2+
3+
import (
4+
sdk "github.com/cosmos/cosmos-sdk/types"
5+
"github.com/renproject/multichain/compat/cosmoscompat"
6+
"github.com/renproject/pack"
7+
)
8+
9+
type addressDecoder struct {
10+
HRP string
11+
}
12+
13+
// NewAddressDecoder returns an implementation of the address decoder interface
14+
// from the Cosmos Compat API, and exposes the functionality to decode strings
15+
// into addresses.
16+
func NewAddressDecoder(HRP string) cosmoscompat.AddressDecoder {
17+
return addressDecoder{HRP: HRP}
18+
}
19+
20+
func (decoder addressDecoder) DecodeAddress(encoded pack.String) (cosmoscompat.Address, error) {
21+
sdk.GetConfig().SetBech32PrefixForAccount(decoder.HRP, decoder.HRP+"pub")
22+
addr, err := sdk.AccAddressFromBech32(encoded.String())
23+
if err != nil {
24+
return nil, err
25+
}
26+
27+
return cosmoscompat.Address(addr), nil
28+
}

chain/cosmos/address_test.go

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package cosmos_test
2+
3+
// import (
4+
// "github.com/renproject/multichain/chain/cosmos"
5+
// "github.com/renproject/pack"
6+
7+
// . "github.com/onsi/ginkgo"
8+
// . "github.com/onsi/gomega"
9+
// )
10+
11+
// var _ = Describe("Cosmos", func() {
12+
// Context("when decoding address", func() {
13+
// Context("when decoding terra address", func() {
14+
// It("should work", func() {
15+
// decoder := cosmos.NewAddressDecoder("terra")
16+
17+
// addrStr := "terra1ztez03dp94y2x55fkhmrvj37ck204geq33msma"
18+
// addr, err := decoder.DecodeAddress(pack.NewString(addrStr))
19+
20+
// Expect(err).ToNot(HaveOccurred())
21+
// Expect(addr.AccAddress().String()).Should(Equal(addrStr))
22+
// })
23+
// })
24+
// })
25+
// })

chain/cosmos/cosmos.go

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package cosmos
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/cosmos/cosmos-sdk/codec"
7+
"github.com/cosmos/cosmos-sdk/simapp"
8+
sdk "github.com/cosmos/cosmos-sdk/types"
9+
"github.com/cosmos/cosmos-sdk/x/auth"
10+
"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
11+
12+
"github.com/renproject/multichain/compat/cosmoscompat"
13+
"github.com/renproject/pack"
14+
15+
"github.com/tendermint/tendermint/crypto/secp256k1"
16+
"github.com/tendermint/tendermint/crypto/tmhash"
17+
)
18+
19+
// NewClient returns returns a new Client with default codec
20+
func NewClient(opts cosmoscompat.ClientOptions) cosmoscompat.Client {
21+
return cosmoscompat.NewClient(opts, simapp.MakeCodec())
22+
}
23+
24+
type txBuilder struct {
25+
auth.TxBuilder
26+
cdc *codec.Codec
27+
}
28+
29+
// NewTxBuilder returns an implementation of the transaction builder interface
30+
// from the Cosmos Compat API, and exposes the functionality to build simple
31+
// Cosmos based transactions.
32+
func NewTxBuilder(options cosmoscompat.TxOptions) cosmoscompat.TxBuilder {
33+
cdc := simapp.MakeCodec()
34+
35+
return txBuilder{
36+
TxBuilder: auth.NewTxBuilder(
37+
utils.GetTxEncoder(cdc),
38+
options.AccountNumber.Uint64(),
39+
options.SequenceNumber.Uint64(),
40+
options.Gas.Uint64(),
41+
0,
42+
false,
43+
options.ChainID.String(),
44+
options.Memo.String(),
45+
options.Fees.Coins(), sdk.DecCoins{},
46+
),
47+
cdc: simapp.MakeCodec(),
48+
}
49+
}
50+
51+
// WithCodec replace codec with custom one
52+
func (builder txBuilder) WithCodec(cdc *codec.Codec) cosmoscompat.TxBuilder {
53+
builder.WithTxEncoder(utils.GetTxEncoder(cdc))
54+
builder.cdc = cdc
55+
return builder
56+
}
57+
58+
func (builder txBuilder) BuildTx(sendMsgs []cosmoscompat.MsgSend) (cosmoscompat.Tx, error) {
59+
sdkMsgs := []sdk.Msg{}
60+
for _, sendMsg := range sendMsgs {
61+
sdkMsgs = append(sdkMsgs, sendMsg.Msg())
62+
}
63+
64+
signMsg, err := builder.BuildSignMsg(sdkMsgs)
65+
if err != nil {
66+
return nil, err
67+
}
68+
69+
return &Tx{cdc: builder.cdc, signMsg: signMsg}, nil
70+
}
71+
72+
// Tx represents a simple Terra transaction that implements the Cosmos Compat
73+
// API.
74+
type Tx struct {
75+
cdc *codec.Codec
76+
signMsg auth.StdSignMsg
77+
signatures []auth.StdSignature
78+
}
79+
80+
// Hash return txhash bytes
81+
func (tx *Tx) Hash() (pack.Bytes32, error) {
82+
if len(tx.signatures) == 0 {
83+
return pack.Bytes32{}, fmt.Errorf("please do tx.Sign() first to get a hash")
84+
}
85+
86+
txBytes, err := tx.Serialize()
87+
if err != nil {
88+
return pack.Bytes32{}, err
89+
}
90+
91+
hashBytes := pack.Bytes32{}
92+
hashBytes.Unmarshal(tmhash.Sum(txBytes), 32)
93+
return hashBytes, nil
94+
}
95+
96+
// SigBytes that need to be signed before this transaction can be
97+
// submitted.
98+
func (tx *Tx) SigBytes() pack.Bytes {
99+
return tx.signMsg.Bytes()
100+
}
101+
102+
// Sign the transaction by injecting signatures and the serialized pubkey of
103+
// the signer.
104+
func (tx *Tx) Sign(signatures []cosmoscompat.StdSignature) error {
105+
var stdSignatures []auth.StdSignature
106+
for _, sig := range signatures {
107+
var pubKey secp256k1.PubKeySecp256k1
108+
copy(pubKey[:], sig.PubKey[:secp256k1.PubKeySecp256k1Size])
109+
110+
stdSignatures = append(stdSignatures, auth.StdSignature{
111+
Signature: sig.Signature,
112+
PubKey: pubKey,
113+
})
114+
}
115+
116+
signers := make(map[string]bool)
117+
for _, msg := range tx.signMsg.Msgs {
118+
for _, signer := range msg.GetSigners() {
119+
fmt.Println("SIBONG", signer.String())
120+
signers[signer.String()] = true
121+
}
122+
}
123+
124+
for _, sig := range stdSignatures {
125+
signer := sdk.AccAddress(sig.Address()).String()
126+
if _, ok := signers[signer]; !ok {
127+
return fmt.Errorf("wrong signer: %s", signer)
128+
}
129+
}
130+
131+
if len(signers) != len(stdSignatures) {
132+
return fmt.Errorf("insufficient signers")
133+
}
134+
135+
fmt.Println("SIBONG", stdSignatures)
136+
tx.signatures = stdSignatures
137+
return nil
138+
}
139+
140+
// Serialize the transaction.
141+
func (tx *Tx) Serialize() (pack.Bytes, error) {
142+
txBytes, err := tx.cdc.MarshalBinaryLengthPrefixed(auth.NewStdTx(tx.signMsg.Msgs, tx.signMsg.Fee, tx.signatures, tx.signMsg.Memo))
143+
if err != nil {
144+
return pack.Bytes{}, err
145+
}
146+
147+
return txBytes, nil
148+
}

chain/cosmos/cosmos_suite_test.go

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package cosmos_test
2+
3+
// import (
4+
// "testing"
5+
6+
// . "github.com/onsi/ginkgo"
7+
// . "github.com/onsi/gomega"
8+
// )
9+
10+
// func TestCosmos(t *testing.T) {
11+
// RegisterFailHandler(Fail)
12+
// RunSpecs(t, "Cosmos Suite")
13+
// }

chain/cosmos/cosmos_test.go

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package cosmos_test
2+
3+
// import (
4+
// "encoding/hex"
5+
// "os"
6+
// "strings"
7+
// "time"
8+
9+
// "github.com/tendermint/tendermint/crypto/secp256k1"
10+
11+
// sdk "github.com/cosmos/cosmos-sdk/types"
12+
// "github.com/terra-project/core/app"
13+
14+
// "github.com/renproject/multichain/chain/cosmos"
15+
// "github.com/renproject/multichain/compat/cosmoscompat"
16+
// "github.com/renproject/pack"
17+
18+
// . "github.com/onsi/ginkgo"
19+
// . "github.com/onsi/gomega"
20+
// )
21+
22+
// var _ = Describe("Cosmos", func() {
23+
// Context("when submitting transactions", func() {
24+
// Context("when sending LUNA to multiple addresses", func() {
25+
// It("should work", func() {
26+
// // Load private key, and assume that the associated address has
27+
// // funds to spend. You can do this by setting TERRA_PK to the
28+
// // value specified in the `./multichaindeploy/.env` file.
29+
// pkEnv := os.Getenv("TERRA_PK")
30+
// if pkEnv == "" {
31+
// panic("TERRA_PK is undefined")
32+
// }
33+
34+
// addrEnv := os.Getenv("TERRA_ADDRESS")
35+
// if addrEnv == "" {
36+
// panic("TERRA_ADDRESS is undefined")
37+
// }
38+
39+
// // pkEnv := "a96e62ed3955e65be32703f12d87b6b5cf26039ecfa948dc5107a495418e5330"
40+
// // addrEnv := "terra10s4mg25tu6termrk8egltfyme4q7sg3hl8s38u"
41+
42+
// pkBz, err := hex.DecodeString(pkEnv)
43+
// Expect(err).ToNot(HaveOccurred())
44+
45+
// var pk secp256k1.PrivKeySecp256k1
46+
// copy(pk[:], pkBz)
47+
48+
// addr := cosmoscompat.Address(pk.PubKey().Address())
49+
50+
// decoder := cosmos.NewAddressDecoder("terra")
51+
// expectedAddr, err := decoder.DecodeAddress(pack.NewString(addrEnv))
52+
// Expect(err).ToNot(HaveOccurred())
53+
// Expect(addr).Should(Equal(expectedAddr))
54+
55+
// pk1 := secp256k1.GenPrivKey()
56+
// pk2 := secp256k1.GenPrivKey()
57+
58+
// recipient1 := sdk.AccAddress(pk1.PubKey().Address())
59+
// recipient2 := sdk.AccAddress(pk2.PubKey().Address())
60+
61+
// msgs := []cosmoscompat.MsgSend{
62+
// {
63+
// FromAddress: cosmoscompat.Address(addr),
64+
// ToAddress: cosmoscompat.Address(recipient1),
65+
// Amount: cosmoscompat.Coins{
66+
// {
67+
// Denom: "uluna",
68+
// Amount: pack.U64(1000000),
69+
// },
70+
// },
71+
// },
72+
// {
73+
// FromAddress: cosmoscompat.Address(addr),
74+
// ToAddress: cosmoscompat.Address(recipient2),
75+
// Amount: cosmoscompat.Coins{
76+
// {
77+
// Denom: "uluna",
78+
// Amount: pack.U64(2000000),
79+
// },
80+
// },
81+
// },
82+
// }
83+
84+
// client := cosmoscompat.NewClient(cosmoscompat.DefaultClientOptions(), app.MakeCodec())
85+
// account, err := client.Account(addr)
86+
// Expect(err).NotTo(HaveOccurred())
87+
88+
// txBuilder := cosmos.NewTxBuilder(cosmoscompat.TxOptions{
89+
// AccountNumber: account.AccountNumber,
90+
// SequenceNumber: account.SequenceNumber,
91+
// Gas: 200000,
92+
// ChainID: "testnet",
93+
// Memo: "multichain",
94+
// Fees: cosmoscompat.Coins{
95+
// {
96+
// Denom: "uluna",
97+
// Amount: pack.U64(3000),
98+
// },
99+
// },
100+
// }).WithCodec(app.MakeCodec())
101+
102+
// tx, err := txBuilder.BuildTx(msgs)
103+
// Expect(err).NotTo(HaveOccurred())
104+
105+
// sigBytes, err := pk.Sign(tx.SigBytes())
106+
// Expect(err).NotTo(HaveOccurred())
107+
108+
// pubKey := pk.PubKey().(secp256k1.PubKeySecp256k1)
109+
// err = tx.Sign([]cosmoscompat.StdSignature{
110+
// {
111+
// Signature: pack.NewBytes(sigBytes),
112+
// PubKey: pack.NewBytes(pubKey[:]),
113+
// },
114+
// })
115+
// Expect(err).NotTo(HaveOccurred())
116+
117+
// txHash, err := client.SubmitTx(tx, pack.NewString("sync"))
118+
// Expect(err).NotTo(HaveOccurred())
119+
120+
// for {
121+
// // Loop until the transaction has at least a few
122+
// // confirmations. This implies that the transaction is
123+
// // definitely valid, and the test has passed. We were
124+
// // successfully able to use the multichain to construct and
125+
// // submit a Bitcoin transaction!
126+
// _, err := client.Tx(txHash)
127+
// if err == nil {
128+
// break
129+
// }
130+
131+
// if !strings.Contains(err.Error(), "not found") {
132+
// Expect(err).NotTo(HaveOccurred())
133+
// }
134+
135+
// time.Sleep(10 * time.Second)
136+
// }
137+
// })
138+
// })
139+
// })
140+
// })

chain/terra/address.go

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package terra
2+
3+
import (
4+
"github.com/renproject/multichain/chain/cosmos"
5+
"github.com/renproject/multichain/compat/cosmoscompat"
6+
)
7+
8+
// NewAddressDecoder returns an implementation of the address decoder interface
9+
// from the Cosmos Compat API, and exposes the functionality to decode strings
10+
// into addresses.
11+
func NewAddressDecoder() cosmoscompat.AddressDecoder {
12+
return cosmos.NewAddressDecoder("terra")
13+
}

chain/terra/address_test.go

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package terra_test
2+
3+
import (
4+
"github.com/renproject/multichain/chain/terra"
5+
"github.com/renproject/pack"
6+
7+
. "github.com/onsi/ginkgo"
8+
. "github.com/onsi/gomega"
9+
)
10+
11+
var _ = Describe("Terra", func() {
12+
Context("when decoding address", func() {
13+
Context("when decoding Terra address", func() {
14+
It("should work", func() {
15+
decoder := terra.NewAddressDecoder()
16+
17+
addrStr := "terra1ztez03dp94y2x55fkhmrvj37ck204geq33msma"
18+
addr, err := decoder.DecodeAddress(pack.NewString(addrStr))
19+
20+
Expect(err).ToNot(HaveOccurred())
21+
Expect(addr.AccAddress().String()).Should(Equal(addrStr))
22+
})
23+
})
24+
})
25+
})

0 commit comments

Comments
 (0)