Skip to content

Commit 211dd99

Browse files
authored
Merge pull request #57 from renproject/feat/cosmos-gas-estimation
feat: constant gas price conversion between micro and pico
2 parents 1e508d1 + aa3a64d commit 211dd99

File tree

5 files changed

+57
-151
lines changed

5 files changed

+57
-151
lines changed

chain/cosmos/cosmos_test.go

-139
Original file line numberDiff line numberDiff line change
@@ -1,140 +1 @@
11
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/cosmos/gas.go

+21-10
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,37 @@ import (
77
"github.com/renproject/pack"
88
)
99

10-
// A GasEstimator returns the gas-per-byte that is needed in order to confirm
11-
// transactions with an estimated maximum delay of one block. In distributed
12-
// networks that collectively build, sign, and submit transactions, it is
13-
// important that all nodes in the network have reached consensus on the
14-
// gas-per-byte.
10+
const (
11+
microToPico = 1000000
12+
)
13+
14+
// A GasEstimator returns the gas price that is needed in order to confirm
15+
// transactions with an estimated maximum delay of one block. As of now, Cosmos
16+
// compatible chains do not support transaction prioritisation in the mempool.
17+
// Hence we use constant gas price set in the micro-denomination (1e-6) of the
18+
// underlying token. However, the gas price as returned by the gas estimation
19+
// API is a 32-byte unsigned integer that represents the gas price in the
20+
// pico-denomination (1e-12).
1521
type GasEstimator struct {
16-
gasPerByte pack.U256
22+
gasPrice float64
1723
}
1824

1925
// NewGasEstimator returns a simple gas estimator that always returns the same
2026
// amount of gas-per-byte.
21-
func NewGasEstimator(gasPerByte pack.U256) gas.Estimator {
27+
func NewGasEstimator(gasPrice float64) gas.Estimator {
2228
return &GasEstimator{
23-
gasPerByte: gasPerByte,
29+
gasPrice: gasPrice,
2430
}
2531
}
2632

2733
// EstimateGas returns gas required per byte for Cosmos-compatible chains. This
2834
// value is used for both the price and cap, because Cosmos-compatible chains do
29-
// not have a distinct concept of cap.
35+
// not have a distinct concept of cap. As of now, Cosmos compatible chains do
36+
// not support transaction prioritisation in the mempool. Hence we use constant
37+
// gas price set in the micro-denomination (1e-6) of the underlying token.
38+
// However, the gas price as returned by the gas estimation API is a 32-byte
39+
// unsigned integer representing gas price in the pico-denomination (1e-12).
3040
func (gasEstimator *GasEstimator) EstimateGas(ctx context.Context) (pack.U256, pack.U256, error) {
31-
return gasEstimator.gasPerByte, gasEstimator.gasPerByte, nil
41+
gasPrice := pack.NewU256FromUint64(uint64(gasEstimator.gasPrice * microToPico))
42+
return gasPrice, gasPrice, nil
3243
}

chain/cosmos/gas_test.go

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package cosmos_test
2+
3+
import (
4+
"context"
5+
"math/rand"
6+
"testing/quick"
7+
"time"
8+
9+
"github.com/renproject/multichain/chain/cosmos"
10+
"github.com/renproject/pack"
11+
12+
. "github.com/onsi/ginkgo"
13+
. "github.com/onsi/gomega"
14+
)
15+
16+
var _ = Describe("Gas", func() {
17+
r := rand.New(rand.NewSource(time.Now().UnixNano()))
18+
Context("when estimating gas parameters", func() {
19+
It("should work", func() {
20+
f := func() bool {
21+
gasPriceMicro := r.Float64()
22+
gasEstimator := cosmos.NewGasEstimator(gasPriceMicro)
23+
gasPricePico, _, err := gasEstimator.EstimateGas(context.Background())
24+
Expect(err).NotTo(HaveOccurred())
25+
expectedGasPrice := pack.NewU256FromUint64(uint64(gasPriceMicro * 1000000))
26+
Expect(gasPricePico).To(Equal(expectedGasPrice))
27+
return true
28+
}
29+
Expect(quick.Check(f, nil)).To(Succeed())
30+
})
31+
})
32+
})

chain/cosmos/tx.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ func NewTxBuilder(options TxBuilderOptions, client *Client) account.TxBuilder {
6363

6464
// BuildTx consumes a list of MsgSend to build and return a cosmos transaction.
6565
// This transaction is unsigned, and must be signed before submitting to the
66-
// cosmos chain.
66+
// cosmos chain. Note that the gas price is represented in the pico-denomination
67+
// (1e-12) and must be converted back to the micro-denomination (1e-6).
6768
func (builder txBuilder) BuildTx(ctx context.Context, from, to address.Address, value, nonce, gasLimit, gasPrice, gasCap pack.U256, payload pack.Bytes) (account.Tx, error) {
6869
types.GetConfig().SetBech32PrefixForAccount(builder.client.hrp, builder.client.hrp+"pub")
6970

@@ -90,7 +91,7 @@ func (builder txBuilder) BuildTx(ctx context.Context, from, to address.Address,
9091

9192
fees := Coins{Coin{
9293
Denom: builder.client.opts.CoinDenom,
93-
Amount: pack.NewU64(gasPrice.Mul(gasLimit).Int().Uint64()),
94+
Amount: pack.NewU64(gasPrice.Mul(gasLimit).Div(pack.NewU256FromUint64(uint64(microToPico))).Int().Uint64()),
9495
}}
9596

9697
accountNumber, err := builder.client.AccountNumber(ctx, from)

go.sum

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBr
2525
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
2626
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
2727
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
28+
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
2829
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=
2930
github.com/99designs/keyring v1.1.3 h1:mEV3iyZWjkxQ7R8ia8GcG97vCX5zQQ7n4o8R2BylwQY=
3031
github.com/99designs/keyring v1.1.3/go.mod h1:657DQuMrBZRtuL/voxVyiyb6zpMehlm5vLB9Qwrv904=

0 commit comments

Comments
 (0)