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: add fee estimate from peer #2

Merged
merged 3 commits into from
Aug 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
26 changes: 26 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,24 @@ export declare function updateStoreOwnership(storeInfo: DataStoreInfo, newOwnerP
* @returns {Vec<CoinSpend>} The coin spends that the owner can sign to melt the store.
*/
export declare function meltStore(storeInfo: DataStoreInfo, ownerPublicKey: Buffer): Array<CoinSpend>
/**
* Signs a message using a private key.
*
* @param {Buffer} message - The message to be signed.
* @param {Buffer} privateKey - The private key to sign the message with.
* @returns {Promise<Buffer>} The signed message as a Buffer.
*/
export declare function signMessage(message: Buffer, privateKey: Buffer): Buffer

/**
* Verifies a signed message using a public key.
*
* @param {Buffer} sig - The signature to verify.
* @param {Buffer} publicKey - The public key to verify the signature with.
* @param {Buffer} message - The original message that was signed.
* @returns {Promise<boolean>} Whether the signature is valid.
*/
export declare function verifySignedMessage(sig: Buffer, publicKey: Buffer, message: Buffer): boolean
export declare class Peer {
/**
* Creates a new Peer instance.
Expand Down Expand Up @@ -350,6 +368,14 @@ export declare class Peer {
* @returns {Promise<SyncStoreResponse>} The sync store response.
*/
syncStore(storeInfo: DataStoreInfo, lastHeight: number | undefined | null, lastHeaderHash: Buffer, withHistory: boolean): Promise<SyncStoreResponse>
/**
* Retrieves the fee estimate for a given target time.
*
* @param {Peer} peer - The peer connection to the Chia node.
* @param {number} targetTimeSeconds - The target time in seconds for the fee estimate.
* @returns {Promise<number>} The estimated fee in mojos per CLVM cost.
*/
getFeeEstimate(peer: Peer, targetTimeSeconds: BigInt): Promise<BigInt>
/**
* Synchronizes a store using its launcher ID.
*
Expand Down
59 changes: 57 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ mod wallet;
use std::sync::Arc;

use chia::bls::derive_keys::master_to_wallet_unhardened;
use chia::bls::{SecretKey as RustSecretKey, Signature as RustSignature};
use chia::bls::{SecretKey as RustSecretKey, Signature as RustSignature, sign, verify};
use chia::client::Peer as RustPeer;
use chia::client::Error as ClientError;
use chia::{bls::PublicKey as RustPublicKey, traits::Streamable};
use chia_protocol::Coin as RustCoin;
use chia_protocol::{Bytes, Coin as RustCoin};
use chia_protocol::CoinSpend as RustCoinSpend;
use chia_protocol::Program as RustProgram;
use chia_protocol::SpendBundle as RustSpendBundle;
Expand All @@ -31,6 +32,7 @@ pub use debug::*;
pub use drivers::*;
pub use merkle_tree::*;
use napi::bindgen_prelude::*;
use std::time::{SystemTime, UNIX_EPOCH};
pub use puzzles::*;
pub use puzzles_info::*;
use puzzles_info::{
Expand Down Expand Up @@ -675,6 +677,34 @@ impl Peer {
Ok(Self(Arc::new(peer)))
}

#[napi]
/// Retrieves the fee estimate for a given target time.
///
/// @param {Peer} peer - The peer connection to the Chia node.
/// @param {BigInt} targetTimeSeconds - The target time in seconds from the current time for the fee estimate.
/// @returns {Promise<BigInt>} The estimated fee in mojos per CLVM cost.
pub async fn get_fee_estimate(&self, target_time_seconds: BigInt) -> napi::Result<BigInt> {
// Convert the target_time_seconds BigInt to u64
let target_time_seconds_u64: u64 = target_time_seconds.get_u64().1;

// Get the current time as a Unix timestamp in seconds
let current_time = SystemTime::now().duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_secs();

// Calculate the target Unix timestamp
let target_timestamp = current_time + target_time_seconds_u64;

// Call the Rust get_fee_estimate function with the calculated Unix timestamp
match wallet::get_fee_estimate(&self.0.clone(), target_timestamp).await {
Ok(fee_estimate) => Ok(BigInt::from(fee_estimate)),
Err(ClientError::Rejection(error_message)) => {
Err(napi::Error::from_reason(format!("Fee estimate rejection: {}", error_message)))
}
Err(e) => Err(napi::Error::from_reason(format!("Failed to request fee estimates: {:?}", e))),
}
}

#[napi]
/// Retrieves all coins that are unspent on the chain. Note that coins part of spend bundles that are pending in the mempool will also be included.
///
Expand Down Expand Up @@ -1205,6 +1235,31 @@ pub fn update_store_ownership(
Ok(res.to_js())
}

#[napi]
pub fn sign_message(
message: Buffer,
private_key: Buffer,
) -> Result<Buffer> {
let sk = RustSecretKey::from_js(private_key);
let signed = sign(&sk, &message);

// Convert the Vec<u8> to a Buffer
Ok(Buffer::from(signed.to_bytes().to_vec()))
}

#[napi]
pub fn verify_signed_message(
signature: Buffer,
public_key: Buffer,
message: Buffer,
) -> Result<bool> {
let sig = RustSignature::from_js(signature);
let pk = RustPublicKey::from_js(public_key);
let is_valid = verify(&sig, &pk, &message);

Ok(is_valid)
}

#[napi]
/// Melts a store. The 1 mojo change will be used as a fee.
///
Expand Down
26 changes: 26 additions & 0 deletions src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,32 @@ pub async fn get_header_hash(
.map(|resp| resp.header_hash())
}

pub async fn get_fee_estimate(
peer: &Peer,
target_time_seconds: u64
) -> Result<u64, ClientError<String>> {
let time_targets = vec![target_time_seconds];

// Call the request_fee_estimates function with the specified time target
let fee_estimate_group = peer
.request_fee_estimates(time_targets)
.await
.map_err(|e| ClientError::Rejection(format!("Request failed: {:?}", e)))?;

// Check if there is an error in the response
if let Some(error_message) = fee_estimate_group.error {
return Err(ClientError::Rejection(error_message));
}

// Extract the first fee estimate from the response
if let Some(first_estimate) = fee_estimate_group.estimates.first() {
return Ok(first_estimate.estimated_fee_rate.mojos_per_clvm_cost);
}

// If no estimates are found, return a custom error
Err(ClientError::Rejection("No fee estimates available".to_string()))
}

pub async fn is_coin_spent(
peer: &Peer,
coin_id: Bytes32,
Expand Down