-
Notifications
You must be signed in to change notification settings - Fork 126
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #50 from renproject/fix/utxo-gas-estimation
UTXO Gas Estimation
- Loading branch information
Showing
12 changed files
with
313 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,54 @@ | ||
package bitcoincash | ||
|
||
import "github.com/renproject/multichain/chain/bitcoin" | ||
import ( | ||
"context" | ||
"fmt" | ||
"math" | ||
|
||
"github.com/renproject/pack" | ||
) | ||
|
||
const ( | ||
bchToSatoshis = 1e8 | ||
kilobyteToByte = 1024 | ||
) | ||
|
||
// A GasEstimator returns the SATs-per-byte that is needed in order to confirm | ||
// transactions with an estimated maximum delay of one block. In distributed | ||
// networks that collectively build, sign, and submit transactions, it is | ||
// important that all nodes in the network have reached consensus on the | ||
// SATs-per-byte. | ||
type GasEstimator = bitcoin.GasEstimator | ||
type GasEstimator struct { | ||
client Client | ||
fallbackGas pack.U256 | ||
} | ||
|
||
// NewGasEstimator returns a simple gas estimator that always returns the given | ||
// number of SATs-per-byte. | ||
var NewGasEstimator = bitcoin.NewGasEstimator | ||
func NewGasEstimator(client Client, fallbackGas pack.U256) GasEstimator { | ||
return GasEstimator{ | ||
client: client, | ||
fallbackGas: fallbackGas, | ||
} | ||
} | ||
|
||
// EstimateGas returns the number of SATs-per-byte (for both price and cap) that | ||
// is needed in order to confirm transactions with a minimal delay. It is the | ||
// responsibility of the caller to know the number of bytes in their | ||
// transaction. This method calls the `estimatefee` RPC call to the node, which | ||
// based on a conservative (considering longer history) strategy returns the | ||
// estimated BCH per kilobyte of data in the transaction. An error will be | ||
// returned if the node hasn't observed enough blocks to make an estimate. | ||
func (gasEstimator GasEstimator) EstimateGas(ctx context.Context) (pack.U256, pack.U256, error) { | ||
feeRate, err := gasEstimator.client.EstimateFeeLegacy(ctx, int64(0)) | ||
if err != nil { | ||
return gasEstimator.fallbackGas, gasEstimator.fallbackGas, err | ||
} | ||
|
||
if feeRate <= 0.0 { | ||
return gasEstimator.fallbackGas, gasEstimator.fallbackGas, fmt.Errorf("invalid fee rate: %v", feeRate) | ||
} | ||
|
||
satsPerByte := uint64(math.Ceil(feeRate * bchToSatoshis / kilobyteToByte)) | ||
return pack.NewU256FromUint64(satsPerByte), pack.NewU256FromUint64(satsPerByte), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,31 @@ | ||
package bitcoincash_test | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/renproject/multichain/chain/bitcoincash" | ||
"github.com/renproject/pack" | ||
|
||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
var _ = Describe("Gas", func() { | ||
Context("when estimating bitcoincash network fee", func() { | ||
It("should work", func() { | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
defer cancel() | ||
|
||
client := bitcoincash.NewClient(bitcoincash.DefaultClientOptions()) | ||
|
||
fallbackGas := uint64(123) | ||
gasEstimator := bitcoincash.NewGasEstimator(client, pack.NewU256FromUint64(fallbackGas)) | ||
gasPrice, _, err := gasEstimator.EstimateGas(ctx) | ||
if err != nil { | ||
Expect(gasPrice).To(Equal(pack.NewU256FromUint64(fallbackGas))) | ||
} else { | ||
Expect(gasPrice.Int().Uint64()).To(BeNumerically(">", 0)) | ||
} | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,52 @@ | ||
package dogecoin_test | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/renproject/multichain/chain/dogecoin" | ||
"github.com/renproject/pack" | ||
|
||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
var _ = Describe("Gas", func() { | ||
Context("when estimating dogecoin network fee", func() { | ||
It("should work", func() { | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
defer cancel() | ||
|
||
client := dogecoin.NewClient(dogecoin.DefaultClientOptions()) | ||
|
||
// estimate fee to include tx within 1 block. | ||
fallback1 := uint64(123) | ||
gasEstimator1 := dogecoin.NewGasEstimator(client, 1, pack.NewU256FromUint64(fallback1)) | ||
gasPrice1, _, err := gasEstimator1.EstimateGas(ctx) | ||
if err != nil { | ||
Expect(gasPrice1).To(Equal(pack.NewU256FromUint64(fallback1))) | ||
} | ||
|
||
// estimate fee to include tx within 10 blocks. | ||
fallback2 := uint64(234) | ||
gasEstimator2 := dogecoin.NewGasEstimator(client, 10, pack.NewU256FromUint64(fallback2)) | ||
gasPrice2, _, err := gasEstimator2.EstimateGas(ctx) | ||
if err != nil { | ||
Expect(gasPrice2).To(Equal(pack.NewU256FromUint64(fallback2))) | ||
} | ||
|
||
// estimate fee to include tx within 100 blocks. | ||
fallback3 := uint64(345) | ||
gasEstimator3 := dogecoin.NewGasEstimator(client, 100, pack.NewU256FromUint64(fallback3)) | ||
gasPrice3, _, err := gasEstimator3.EstimateGas(ctx) | ||
if err != nil { | ||
Expect(gasPrice3).To(Equal(pack.NewU256FromUint64(fallback3))) | ||
} | ||
|
||
// expect fees in this order at the very least. | ||
if err == nil { | ||
Expect(gasPrice1.GreaterThanEqual(gasPrice2)).To(BeTrue()) | ||
Expect(gasPrice2.GreaterThanEqual(gasPrice3)).To(BeTrue()) | ||
} | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,57 @@ | ||
package zcash | ||
|
||
import "github.com/renproject/multichain/chain/bitcoin" | ||
import ( | ||
"context" | ||
"fmt" | ||
"math" | ||
|
||
// GasEstimator re-exports bitcoin.GasEstimator | ||
type GasEstimator = bitcoin.GasEstimator | ||
"github.com/renproject/pack" | ||
) | ||
|
||
// NewGasEstimator re-exports bitcoin.NewGasEstimator | ||
var NewGasEstimator = bitcoin.NewGasEstimator | ||
const ( | ||
multiplier = 1e8 | ||
kilobyteToByte = 1024 | ||
) | ||
|
||
// A GasEstimator returns the SATs-per-byte that is needed in order to confirm | ||
// transactions with an estimated maximum delay of one block. In distributed | ||
// networks that collectively build, sign, and submit transactions, it is | ||
// important that all nodes in the network have reached consensus on the | ||
// SATs-per-byte. | ||
type GasEstimator struct { | ||
client Client | ||
numBlocks int64 | ||
fallbackGas pack.U256 | ||
} | ||
|
||
// NewGasEstimator returns a simple gas estimator that always returns the given | ||
// number of SATs-per-byte. | ||
func NewGasEstimator(client Client, numBlocks int64, fallbackGas pack.U256) GasEstimator { | ||
return GasEstimator{ | ||
client: client, | ||
numBlocks: numBlocks, | ||
fallbackGas: fallbackGas, | ||
} | ||
} | ||
|
||
// EstimateGas returns the number of SATs-per-byte (for both price and cap) that | ||
// is needed in order to confirm transactions with an estimated maximum delay of | ||
// `numBlocks` block. It is the responsibility of the caller to know the number | ||
// of bytes in their transaction. This method calls the `estimatesmartfee` RPC | ||
// call to the node, which based on a conservative (considering longer history) | ||
// strategy returns the estimated BTC per kilobyte of data in the transaction. | ||
// An error will be returned if the bitcoin node hasn't observed enough blocks | ||
// to make an estimate for the provided target `numBlocks`. | ||
func (gasEstimator GasEstimator) EstimateGas(ctx context.Context) (pack.U256, pack.U256, error) { | ||
feeRate, err := gasEstimator.client.EstimateFeeLegacy(ctx, gasEstimator.numBlocks) | ||
if err != nil { | ||
return gasEstimator.fallbackGas, gasEstimator.fallbackGas, err | ||
} | ||
|
||
if feeRate <= 0.0 { | ||
return gasEstimator.fallbackGas, gasEstimator.fallbackGas, fmt.Errorf("invalid fee rate: %v", feeRate) | ||
} | ||
|
||
satsPerByte := uint64(math.Ceil(feeRate * multiplier / kilobyteToByte)) | ||
return pack.NewU256FromUint64(satsPerByte), pack.NewU256FromUint64(satsPerByte), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,52 @@ | ||
package zcash_test | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/renproject/multichain/chain/zcash" | ||
"github.com/renproject/pack" | ||
|
||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
var _ = Describe("Gas", func() { | ||
Context("when estimating zcash network fee", func() { | ||
It("should work", func() { | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
defer cancel() | ||
|
||
client := zcash.NewClient(zcash.DefaultClientOptions()) | ||
|
||
// estimate fee to include tx within 1 block. | ||
fallback1 := uint64(123) | ||
gasEstimator1 := zcash.NewGasEstimator(client, 1, pack.NewU256FromUint64(fallback1)) | ||
gasPrice1, _, err := gasEstimator1.EstimateGas(ctx) | ||
if err != nil { | ||
Expect(gasPrice1).To(Equal(pack.NewU256FromUint64(fallback1))) | ||
} | ||
|
||
// estimate fee to include tx within 10 blocks. | ||
fallback2 := uint64(234) | ||
gasEstimator2 := zcash.NewGasEstimator(client, 10, pack.NewU256FromUint64(fallback2)) | ||
gasPrice2, _, err := gasEstimator2.EstimateGas(ctx) | ||
if err != nil { | ||
Expect(gasPrice2).To(Equal(pack.NewU256FromUint64(fallback2))) | ||
} | ||
|
||
// estimate fee to include tx within 100 blocks. | ||
fallback3 := uint64(345) | ||
gasEstimator3 := zcash.NewGasEstimator(client, 100, pack.NewU256FromUint64(fallback3)) | ||
gasPrice3, _, err := gasEstimator3.EstimateGas(ctx) | ||
if err != nil { | ||
Expect(gasPrice3).To(Equal(pack.NewU256FromUint64(fallback3))) | ||
} | ||
|
||
// expect fees in this order at the very least. | ||
if err == nil { | ||
Expect(gasPrice1.GreaterThanEqual(gasPrice2)).To(BeTrue()) | ||
Expect(gasPrice2.GreaterThanEqual(gasPrice3)).To(BeTrue()) | ||
} | ||
}) | ||
}) | ||
}) |
Oops, something went wrong.