Skip to content

Commit

Permalink
[XTZ] Add more precise fee and gas estimations
Browse files Browse the repository at this point in the history
Those are accessible through account/TxBuilderRequest API
  • Loading branch information
gagbo committed Aug 21, 2020
1 parent 5e1aa1c commit 7787ace
Show file tree
Hide file tree
Showing 14 changed files with 457 additions and 6 deletions.
2 changes: 2 additions & 0 deletions core/idl/wallet/tezos/tezos_like_wallet.djinni
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ TezosLikeAccount = interface +c {
getEstimatedGasLimit(address: string, callback: Callback<BigInt>);
# Get fees from network
getFees(callback: Callback<BigInt>);
# Get gas price from network
getGasPrice(callback: Callback<BigInt>);
# Get originated accounts by current account
getOriginatedAccounts(): list<TezosLikeOriginatedAccount>;
}
Expand Down
3 changes: 3 additions & 0 deletions core/src/api/TezosLikeAccount.hpp

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions core/src/jni/jni/TezosLikeAccount.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ CJNIEXPORT void JNICALL Java_co_ledger_core_TezosLikeAccount_00024CppProxy_nativ
} JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, )
}

CJNIEXPORT void JNICALL Java_co_ledger_core_TezosLikeAccount_00024CppProxy_native_1getGasPrice(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef, jobject j_callback)
{
try {
DJINNI_FUNCTION_PROLOGUE1(jniEnv, nativeRef);
const auto& ref = ::djinni::objectFromHandleAddress<::ledger::core::api::TezosLikeAccount>(nativeRef);
ref->getGasPrice(::djinni_generated::BigIntCallback::toCpp(jniEnv, j_callback));
} JNI_TRANSLATE_EXCEPTIONS_RETURN(jniEnv, )
}

CJNIEXPORT jobject JNICALL Java_co_ledger_core_TezosLikeAccount_00024CppProxy_native_1getOriginatedAccounts(JNIEnv* jniEnv, jobject /*this*/, jlong nativeRef)
{
try {
Expand Down
2 changes: 1 addition & 1 deletion core/src/net/HttpClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,4 @@ namespace ledger {
}


}
}
5 changes: 5 additions & 0 deletions core/src/wallet/tezos/TezosLikeAccount.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ namespace ledger {
void getFees(const std::shared_ptr<api::BigIntCallback> & callback) override;
FuturePtr<BigInt> getFees();

void getGasPrice(const std::shared_ptr<api::BigIntCallback> & callback) override;
FuturePtr<BigInt> getGasPrice();

FuturePtr<BigInt> estimateGasLimit(const std::shared_ptr<TezosLikeTransactionApi>& tx, double adjustment_factor = 1.1);

std::shared_ptr<api::Keychain> getAccountKeychain() override;

private:
Expand Down
47 changes: 45 additions & 2 deletions core/src/wallet/tezos/TezosLikeAccount2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,15 +369,34 @@ namespace ledger {
tx->setCounter(std::make_shared<BigInt>(++BigInt(saved_counter)));
}
return explorer->getCurrentBlock();
}).flatMapPtr<api::TezosLikeTransaction>(self->getMainExecutionContext(), [self, explorer, tx, senderAddress] (const std::shared_ptr<Block> &block) {
}).flatMapPtr<TezosLikeTransactionApi>(self->getMainExecutionContext(), [self, explorer, tx, senderAddress] (const std::shared_ptr<Block> &block) {
tx->setBlockHash(block->hash);
if (senderAddress.find("KT1") == 0) {
// HACK: KT Operation we use forge endpoint
return explorer->forgeKTOperation(tx).mapPtr<api::TezosLikeTransaction>(self->getMainExecutionContext(), [tx] (const std::vector<uint8_t> &rawTx) {
return explorer->forgeKTOperation(tx).mapPtr<TezosLikeTransactionApi>(self->getMainExecutionContext(), [tx] (const std::vector<uint8_t> &rawTx) {
tx->setRawTx(rawTx);
return tx;
});
}
return FuturePtr<TezosLikeTransactionApi>::successful(tx);
}).flatMapPtr<api::TezosLikeTransaction>(self->getMainExecutionContext(), [self, request] (const std::shared_ptr<TezosLikeTransactionApi> &tx) {
if (request.gasLimit->toInt() == 0) {
auto filledTx = tx;
auto gasPrice_fut = request.fees->toInt() == 0 ?
self->getGasPrice()
:
FuturePtr<BigInt>::successful(request.fees);

return gasPrice_fut.flatMapPtr<api::TezosLikeTransaction>(self->getMainExecutionContext(), [self, filledTx] (const std::shared_ptr<BigInt>&gasPrice) -> FuturePtr<api::TezosLikeTransaction> {
return self->estimateGasLimit(filledTx).flatMapPtr<api::TezosLikeTransaction>(self->getMainExecutionContext(), [filledTx, gasPrice] (const std::shared_ptr<BigInt> &gas) -> FuturePtr<api::TezosLikeTransaction> {
// 0.000001 comes from the gasPrice being in picoTez
const auto fees = std::make_shared<BigInt>(static_cast<int64_t>(1 + gas->toInt64() * static_cast<double>(gasPrice->toInt64()) * 0.000001));
filledTx->setGasLimit(gas);
filledTx->setFees(fees);
return FuturePtr<api::TezosLikeTransaction>::successful(filledTx);
});
});
}
return FuturePtr<api::TezosLikeTransaction>::successful(tx);
});
});
Expand Down Expand Up @@ -422,6 +441,30 @@ namespace ledger {
return _explorer->getFees();
}

void TezosLikeAccount::getGasPrice(const std::shared_ptr<api::BigIntCallback> & callback) {
getGasPrice().mapPtr<api::BigInt>(getMainExecutionContext(), [] (const std::shared_ptr<BigInt> &gasPrice) -> std::shared_ptr<api::BigInt>
{
if (!gasPrice) {
throw make_exception(api::ErrorCode::RUNTIME_ERROR, "Failed to retrieve gasPrice from network");
}
return std::make_shared<api::BigIntImpl>(*gasPrice);
}).callback(getMainExecutionContext(), callback);
}

FuturePtr<BigInt> TezosLikeAccount::getGasPrice() {
return _explorer->getGasPrice();
}

FuturePtr<BigInt> TezosLikeAccount::estimateGasLimit(const std::shared_ptr<TezosLikeTransactionApi>& tx, double adjustment_factor) {
return _explorer->getEstimatedGasLimit(tx).flatMapPtr<BigInt>(
getMainExecutionContext(),
[adjustment_factor](const std::shared_ptr<BigInt>& consumedGas){
auto adjusted_gas = static_cast<int64_t>(1 + consumedGas->toInt64() * adjustment_factor);
return Future<std::shared_ptr<BigInt>>::successful(
std::make_shared<BigInt>(adjusted_gas));
});
}

std::shared_ptr<api::Keychain> TezosLikeAccount::getAccountKeychain() {
return _keychain;
}
Expand Down
184 changes: 183 additions & 1 deletion core/src/wallet/tezos/api_impl/TezosLikeTransactionApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
#include <api/TezosCurve.hpp>
#include <tezos/TezosLikeExtendedPublicKey.h>
#include <api/TezosConfigurationDefaults.hpp>
#include <rapidjson/document.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>
namespace ledger {
namespace core {

Expand Down Expand Up @@ -328,6 +331,185 @@ namespace ledger {
}
return writer.toByteArray();
}

std::vector<uint8_t> TezosLikeTransactionApi::serializeForDryRun(const std::vector<uint8_t>& chainId) {
BytesWriter writer;
writer.writeByteArray(serialize());
writer.writeByteArray(chainId);
return writer.toByteArray();
}

std::string TezosLikeTransactionApi::serializeJsonForDryRun(const std::string &chainID)
{
using namespace rapidjson;

Value vString(kStringType);
Document tx;
tx.SetObject();
Document::AllocatorType &allocator = tx.GetAllocator();

// Chain Id
vString.SetString(chainID.c_str(), static_cast<SizeType>(chainID.length()), allocator);
tx.AddMember("chain_id", vString, allocator);

// Operation
Value opObject(kObjectType);
{
// Branch
const auto hash = _block->getHash();
vString.SetString(hash.c_str(), static_cast<SizeType>(hash.length()), allocator);
opObject.AddMember("branch", vString, allocator);

// Fake sign
static const auto bogusSignature =
"edsigtkpiSSschcaCt9pUVrpNPf7TTcgvgDEDD6NCEHMy8NNQJCGnMfLZzYoQj74yLjo9wx6MPVV29"
"CvVzgi7qEcEUok3k7AuMg";
vString.SetString(
bogusSignature,
static_cast<SizeType>(std::strlen(bogusSignature)),
allocator);
opObject.AddMember("signature", vString, allocator);

Value opContents(kArrayType);
{
if (_needReveal) {
Value revealOp(kObjectType);
{
static const auto transaction_type = "reveal";
vString.SetString(
transaction_type,
static_cast<SizeType>(std::strlen(transaction_type)),
allocator);
revealOp.AddMember("kind", vString, allocator);

const auto source = _sender->toBase58();
vString.SetString(
source.c_str(), static_cast<SizeType>(source.length()), allocator);
revealOp.AddMember("source", vString, allocator);

if (_revealedPubKey.empty()) {
throw make_exception(
api::ErrorCode::UNSUPPORTED_OPERATION,
"Json serialization of reveal operation is available only if "
"revealed_pubkey is set.");
}
const auto pub_key = _revealedPubKey ;
vString.SetString(
source.c_str(), static_cast<SizeType>(source.length()), allocator);
revealOp.AddMember("public_key", vString, allocator);

static const auto fee = "257000";
vString.SetString(fee, static_cast<SizeType>(std::strlen(fee)), allocator);
revealOp.AddMember("fee", vString, allocator);

const auto counter = _counter->toString();
vString.SetString(
counter.c_str(), static_cast<SizeType>(counter.length()), allocator);
revealOp.AddMember("counter", vString, allocator);

static const auto storage = "1000";
vString.SetString(
storage, static_cast<SizeType>(std::strlen(storage)), allocator);
revealOp.AddMember("storage_limit", vString, allocator);

static const auto gas = "100000";
vString.SetString(gas, static_cast<SizeType>(std::strlen(gas)), allocator);
revealOp.AddMember("gas_limit", vString, allocator);

}
opContents.PushBack(revealOp, allocator);
}

Value innerOp(kObjectType);
{
switch (_type) {
case api::TezosOperationTag::OPERATION_TAG_TRANSACTION: {
static const auto transaction_type = "transaction";
vString.SetString(
transaction_type,
static_cast<SizeType>(std::strlen(transaction_type)),
allocator);
innerOp.AddMember("kind", vString, allocator);

const auto source = _sender->toBase58();
vString.SetString(
source.c_str(), static_cast<SizeType>(source.length()), allocator);
innerOp.AddMember("source", vString, allocator);

const auto destination = _receiver->toBase58();
vString.SetString(
destination.c_str(), static_cast<SizeType>(destination.length()), allocator);
innerOp.AddMember("destination", vString, allocator);

static const auto fee = "1";
vString.SetString(fee, static_cast<SizeType>(std::strlen(fee)), allocator);
innerOp.AddMember("fee", vString, allocator);

const auto counter = _counter->toString();
vString.SetString(
counter.c_str(), static_cast<SizeType>(counter.length()), allocator);
innerOp.AddMember("counter", vString, allocator);

const auto amount = _value->toBigInt()->toString(10);
vString.SetString(
amount.c_str(), static_cast<SizeType>(amount.length()), allocator);
innerOp.AddMember("amount", vString, allocator);

static const auto storage = "1000";
vString.SetString(
storage, static_cast<SizeType>(std::strlen(storage)), allocator);
innerOp.AddMember("storage_limit", vString, allocator);

static const auto gas = "100000";
vString.SetString(gas, static_cast<SizeType>(std::strlen(gas)), allocator);
innerOp.AddMember("gas_limit", vString, allocator);
break;
}
case api::TezosOperationTag::OPERATION_TAG_ORIGINATION: {
throw make_exception(
api::ErrorCode::UNSUPPORTED_OPERATION,
"Json serialization of origination operation is unavailable.");
break;
}
case api::TezosOperationTag::OPERATION_TAG_DELEGATION: {
throw make_exception(
api::ErrorCode::UNSUPPORTED_OPERATION,
"Json serialization of delegation operation is unavailable.");
break;
}
default:
throw make_exception(
api::ErrorCode::UNSUPPORTED_OPERATION,
"Json serialization of unknown operation type is unavailable.");
break;
}
}
opContents.PushBack(innerOp, allocator);
}

opObject.AddMember("contents", opContents, allocator);
}

tx.AddMember("operation", opObject, allocator);

// Example of valid payload in raw string
/*
R"json({"chain_id": "NetXdQprcVkpaWU", "operation": {
"branch": "BLq1UohguxXEdrvgxc4a4utkD1J8K4GTz2cypJqdN2nq8m1jbqW",
"contents": [{"kind": "transaction",
"source": "tz1fizckUHrisN2JXZRWEBvtq4xRQwPhoirQ",
"destination": "tz1fizckUHrisN2JXZRWEBvtq4xRQwPhoirQ", "amount":
"1432", "counter": "2531425", "fee": "1289", "gas_limit": "100000",
"storage_limit": "1000"}],
"signature":
"edsigtkpiSSschcaCt9pUVrpNPf7TTcgvgDEDD6NCEHMy8NNQJCGnMfLZzYoQj74yLjo9wx6MPVV29CvVzgi7qEcEUok3k7AuMg"}})json"
*/
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
tx.Accept(writer);
return buffer.GetString();
}

TezosLikeTransactionApi &TezosLikeTransactionApi::setFees(const std::shared_ptr<BigInt> &fees) {
if (!fees) {
throw make_exception(api::ErrorCode::INVALID_ARGUMENT,
Expand Down Expand Up @@ -433,4 +615,4 @@ namespace ledger {
return _needReveal;
}
}
}
}
6 changes: 6 additions & 0 deletions core/src/wallet/tezos/api_impl/TezosLikeTransactionApi.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ namespace ledger {
std::vector<uint8_t> serialize() override;
std::vector<uint8_t> serializeWithType(api::TezosOperationTag type);

/// Serialize the transaction as json for Tezos Node run_operation JSON RPC endpoint
std::vector<uint8_t> serializeForDryRun(const std::vector<uint8_t>& chainID);

/// Serialize the transaction as json for Tezos Node run_operation JSON RPC endpoint
std::string serializeJsonForDryRun(const std::string& chainID);

std::chrono::system_clock::time_point getDate() override;

std::shared_ptr<api::BigInt> getCounter() override;
Expand Down
Loading

0 comments on commit 7787ace

Please sign in to comment.