initial basic dao mint in wasm

This commit is contained in:
x
2022-11-06 17:38:35 +00:00
parent d477eb3106
commit 14807bcde6
13 changed files with 424 additions and 35 deletions

View File

@@ -10,7 +10,7 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
darkfi-sdk = { path = "../../../../src/sdk" }
darkfi-serial = { path = "../../../../src/serial" }
darkfi-serial = { path = "../../../../src/serial", features = ["crypto"] }
# We need to disable random using "custom" which makes the crate a noop
# so the wasm32-unknown-unknown target is enabled.

View File

@@ -1,14 +1,23 @@
use darkfi_sdk::{
crypto::ContractId,
crypto::{ContractId, constants::MERKLE_DEPTH, MerkleNode, Nullifier},
db::{db_get, db_init, db_lookup, db_set},
define_contract,
error::ContractResult,
msg,
merkle::merkle_add,
pasta::pallas,
tx::ContractCall,
util::{set_return_data, put_object_bytes, get_object_bytes, get_object_size},
util::{get_object_bytes, get_object_size, put_object_bytes, set_return_data},
incrementalmerkletree::{bridgetree::BridgeTree, Tree}
};
use darkfi_serial::{deserialize, serialize, Encodable, SerialDecodable, SerialEncodable, WriteExt, ReadExt};
use darkfi_serial::{
deserialize, serialize, Encodable, ReadExt, SerialDecodable, SerialEncodable, WriteExt,
};
type MerkleTree = BridgeTree<MerkleNode, { MERKLE_DEPTH }>;
#[derive(Clone, SerialEncodable, SerialDecodable)]
pub struct DaoBulla(pub pallas::Base);
#[repr(u8)]
pub enum DaoFunction {
@@ -16,10 +25,23 @@ pub enum DaoFunction {
Mint = 0x01,
}
impl From<u8> for DaoFunction {
fn from(b: u8) -> Self {
match b {
0x00 => Self::Foo,
0x01 => Self::Mint,
_ => panic!("Invalid function ID: {:#04x?}", b),
}
}
}
#[derive(SerialEncodable, SerialDecodable)]
pub struct DaoMintParams {
pub a: u32,
pub b: u32
pub dao_bulla: DaoBulla,
}
#[derive(SerialEncodable, SerialDecodable)]
pub struct DaoMintUpdate {
pub dao_bulla: DaoBulla,
}
define_contract!(
@@ -30,7 +52,11 @@ define_contract!(
);
fn init_contract(cid: ContractId, _ix: &[u8]) -> ContractResult {
let db_handle = db_init(cid, "wagies")?;
let db_handle = db_init(cid, "info")?;
let dao_tree = MerkleTree::new(100);
let dao_tree_data = serialize(&dao_tree);
db_set(db_handle, &serialize(&"dao_tree".to_string()), &dao_tree_data)?;
Ok(())
}
@@ -46,10 +72,47 @@ fn get_metadata(_cid: ContractId, ix: &[u8]) -> ContractResult {
Ok(())
}
fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
let (call_idx, call): (u32, Vec<ContractCall>) = deserialize(ix)?;
assert!(call_idx < call.len() as u32);
let self_ = &call[call_idx as usize];
match DaoFunction::from(self_.data[0]) {
DaoFunction::Mint => {
let data = &self_.data[1..];
let params: DaoMintParams = deserialize(data)?;
// No checks in Mint. Just return the update.
let update = DaoMintUpdate { dao_bulla: params.dao_bulla };
let mut update_data = Vec::new();
update_data.write_u8(DaoFunction::Mint as u8);
update.encode(&mut update_data);
set_return_data(&update_data)?;
msg!("update is set!");
}
DaoFunction::Foo => {
unimplemented!();
}
}
Ok(())
}
fn process_update(cid: ContractId, update_data: &[u8]) -> ContractResult {
let db_handle = db_lookup(cid, "wagies")?;
db_set(db_handle, &serialize(&"jason_gulag".to_string()), &serialize(&110))?;
match DaoFunction::from(update_data[0]) {
DaoFunction::Mint => {
let data = &update_data[1..];
let update: DaoMintUpdate = deserialize(data)?;
let db_handle = db_lookup(cid, "info")?;
let node = MerkleNode::new(update.dao_bulla.0);
merkle_add(db_handle, &serialize(&"dao_tree".to_string()), &node)?;
}
DaoFunction::Foo => {
unimplemented!();
}
}
Ok(())
}

View File

@@ -1,8 +1,57 @@
use darkfi_sdk::{
crypto::ContractId,
db::{db_get, db_init, db_lookup, db_set},
define_contract,
error::ContractResult,
msg,
pasta::pallas,
tx::ContractCall,
util::{get_object_bytes, get_object_size, put_object_bytes, set_return_data},
};
use darkfi_serial::{
deserialize, serialize, Encodable, ReadExt, SerialDecodable, SerialEncodable, WriteExt,
};
#[repr(u8)]
pub enum MoneyFunction {
Bar = 0x01,
Foo = 0x00,
Mint = 0x01,
}
pub fn bar() {
println!("bar");
#[derive(SerialEncodable, SerialDecodable)]
pub struct MoneyMintParams {
pub a: u32,
pub b: u32,
}
define_contract!(
init: init_contract,
exec: process_instruction,
apply: process_update,
metadata: get_metadata
);
fn init_contract(cid: ContractId, _ix: &[u8]) -> ContractResult {
let db_handle = db_init(cid, "wagies")?;
Ok(())
}
fn get_metadata(_cid: ContractId, ix: &[u8]) -> ContractResult {
let zk_public_values: Vec<(String, Vec<pallas::Base>)> = Vec::new();
let signature_public_keys: Vec<pallas::Point> = Vec::new();
let mut metadata = Vec::new();
zk_public_values.encode(&mut metadata)?;
signature_public_keys.encode(&mut metadata)?;
set_return_data(&metadata)?;
Ok(())
}
fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
Ok(())
}
fn process_update(cid: ContractId, update_data: &[u8]) -> ContractResult {
let db_handle = db_lookup(cid, "wagies")?;
db_set(db_handle, &serialize(&"jason_gulag".to_string()), &serialize(&110))?;
Ok(())
}

View File

@@ -1,22 +1,69 @@
use darkfi::{
blockchain::Blockchain,
consensus::{TESTNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_TIMESTAMP},
crypto::{
coin::Coin,
keypair::{Keypair, PublicKey, SecretKey},
proof::{ProvingKey, VerifyingKey},
types::{DrkSpendHook, DrkUserData, DrkValue},
util::{pedersen_commitment_u64, poseidon_hash},
},
runtime::vm_runtime::Runtime,
zk::circuit::{BurnContract, MintContract},
zkas::decoder::ZkBinary,
Result,
};
use darkfi_sdk::{crypto::ContractId, pasta::pallas, tx::ContractCall};
use darkfi_serial::{serialize, deserialize, Decodable, Encodable, WriteExt};
use std::io::Cursor;
use darkfi_sdk::{
crypto::{constants::MERKLE_DEPTH, ContractId, MerkleNode},
tx::ContractCall,
};
use darkfi_serial::{deserialize, serialize, Decodable, Encodable, WriteExt};
use incrementalmerkletree::{bridgetree::BridgeTree, Tree};
use log::{debug, error};
use pasta_curves::{
arithmetic::CurveAffine,
group::{ff::Field, Curve, Group},
pallas,
};
use rand::rngs::OsRng;
use std::{
any::{Any, TypeId},
io::Cursor,
time::Instant,
};
use dao_contract::{DaoFunction, DaoMintParams};
use crate::{
contract::{dao, example, money},
note::EncryptedNote2,
schema::WalletCache,
tx::Transaction,
util::{sign, StateRegistry, ZkContractTable},
};
mod contract;
mod error;
mod note;
mod schema;
mod tx;
mod util;
fn show_dao_state(chain: &Blockchain, contract_id: &ContractId) -> Result<()> {
let db = chain.contracts.lookup(&chain.sled_db, contract_id, "info")?;
match db.get(&serialize(&"dao_tree".to_string())).expect("dao_tree") {
Some(value) => {
debug!(target: "demo", "DAO tree: {} bytes", value.len());
}
None => {
error!(target: "demo", "Missing DAO tree");
}
}
Ok(())
}
fn show_money_state(chain: &Blockchain, contract_id: &ContractId) -> Result<()> {
let db = chain.contracts.lookup(&chain.sled_db, contract_id, "wagies")?;
for obj in db.iter() {
let (key, value) = obj.unwrap();
@@ -43,19 +90,229 @@ async fn main() -> BoxResult<()> {
println!("wakie wakie young wagie");
//return Ok(());
//schema::schema().await?;
//return Ok(());
// =============================
// Initialize a dummy blockchain
// Setup initial program parameters
// =============================
// Money parameters
let xdrk_supply = 1_000_000;
let xdrk_token_id = pallas::Base::random(&mut OsRng);
// Governance token parameters
let gdrk_supply = 1_000_000;
let gdrk_token_id = pallas::Base::random(&mut OsRng);
// DAO parameters
let dao_proposer_limit = 110;
let dao_quorum = 110;
let dao_approval_ratio_quot = 1;
let dao_approval_ratio_base = 2;
// Initialize ZK binary table
let mut zk_bins = ZkContractTable::new();
debug!(target: "demo", "Loading dao-mint.zk");
let zk_dao_mint_bincode = include_bytes!("../proof/dao-mint.zk.bin");
let zk_dao_mint_bin = ZkBinary::decode(zk_dao_mint_bincode)?;
zk_bins.add_contract("dao-mint".to_string(), zk_dao_mint_bin, 13);
/*
debug!(target: "demo", "Loading money-transfer contracts");
{
let start = Instant::now();
let mint_pk = ProvingKey::build(11, &MintContract::default());
debug!("Mint PK: [{:?}]", start.elapsed());
let start = Instant::now();
let burn_pk = ProvingKey::build(11, &BurnContract::default());
debug!("Burn PK: [{:?}]", start.elapsed());
let start = Instant::now();
let mint_vk = VerifyingKey::build(11, &MintContract::default());
debug!("Mint VK: [{:?}]", start.elapsed());
let start = Instant::now();
let burn_vk = VerifyingKey::build(11, &BurnContract::default());
debug!("Burn VK: [{:?}]", start.elapsed());
zk_bins.add_native("money-transfer-mint".to_string(), mint_pk, mint_vk);
zk_bins.add_native("money-transfer-burn".to_string(), burn_pk, burn_vk);
}
debug!(target: "demo", "Loading dao-propose-main.zk");
let zk_dao_propose_main_bincode = include_bytes!("../proof/dao-propose-main.zk.bin");
let zk_dao_propose_main_bin = ZkBinary::decode(zk_dao_propose_main_bincode)?;
zk_bins.add_contract("dao-propose-main".to_string(), zk_dao_propose_main_bin, 13);
debug!(target: "demo", "Loading dao-propose-burn.zk");
let zk_dao_propose_burn_bincode = include_bytes!("../proof/dao-propose-burn.zk.bin");
let zk_dao_propose_burn_bin = ZkBinary::decode(zk_dao_propose_burn_bincode)?;
zk_bins.add_contract("dao-propose-burn".to_string(), zk_dao_propose_burn_bin, 13);
debug!(target: "demo", "Loading dao-vote-main.zk");
let zk_dao_vote_main_bincode = include_bytes!("../proof/dao-vote-main.zk.bin");
let zk_dao_vote_main_bin = ZkBinary::decode(zk_dao_vote_main_bincode)?;
zk_bins.add_contract("dao-vote-main".to_string(), zk_dao_vote_main_bin, 13);
debug!(target: "demo", "Loading dao-vote-burn.zk");
let zk_dao_vote_burn_bincode = include_bytes!("../proof/dao-vote-burn.zk.bin");
let zk_dao_vote_burn_bin = ZkBinary::decode(zk_dao_vote_burn_bincode)?;
zk_bins.add_contract("dao-vote-burn".to_string(), zk_dao_vote_burn_bin, 13);
let zk_dao_exec_bincode = include_bytes!("../proof/dao-exec.zk.bin");
let zk_dao_exec_bin = ZkBinary::decode(zk_dao_exec_bincode)?;
zk_bins.add_contract("dao-exec".to_string(), zk_dao_exec_bin, 13);
// State for money contracts
let cashier_signature_secret = SecretKey::random(&mut OsRng);
let cashier_signature_public = PublicKey::from_secret(cashier_signature_secret);
let faucet_signature_secret = SecretKey::random(&mut OsRng);
let faucet_signature_public = PublicKey::from_secret(faucet_signature_secret);
*/
// We use this to receive coins
let mut cache = WalletCache::new();
// Initialize a dummy blockchain
// TODO: This blockchain interface should perhaps be ValidatorState and Mutex/RwLock.
let db = sled::Config::new().temporary(true).open()?;
let blockchain = Blockchain::new(&db, *TESTNET_GENESIS_TIMESTAMP, *TESTNET_GENESIS_HASH_BYTES)?;
// ================================================================
// Load the wasm binary into memory and create an execution runtime
// Deploy the wasm contracts
// ================================================================
let dao_wasm_bytes = std::fs::read("dao_contract.wasm")?;
let dao_contract_id = ContractId::from(pallas::Base::from(1));
let money_wasm_bytes = std::fs::read("money_contract.wasm")?;
let money_contract_id = ContractId::from(pallas::Base::from(2));
// Block 1
// This has 2 transaction deploying the DAO and Money wasm contracts
// together with their ZK proofs.
{
let mut dao_runtime = Runtime::new(&dao_wasm_bytes, blockchain.clone(), dao_contract_id)?;
let mut money_runtime =
Runtime::new(&money_wasm_bytes, blockchain.clone(), money_contract_id)?;
// 1. exec() - zk and sig verify also
// ... none in this block
// 2. commit() - all apply() and deploy()
// Deploy function to initialize the smart contract state.
// Here we pass an empty payload, but it's possible to feed in arbitrary data.
dao_runtime.deploy(&[])?;
money_runtime.deploy(&[])?;
debug!(target: "demo", "Deployed DAO and money contracts");
}
// ================================================================
// DAO::mint()
// ================================================================
// Wallet
let dao_keypair = Keypair::random(&mut OsRng);
let dao_bulla_blind = pallas::Base::random(&mut OsRng);
let tx = {
let signature_secret = SecretKey::random(&mut OsRng);
// Create DAO mint tx
let builder = dao::mint::wallet::Builder {
dao_proposer_limit,
dao_quorum,
dao_approval_ratio_quot,
dao_approval_ratio_base,
gov_token_id: gdrk_token_id,
dao_pubkey: dao_keypair.public,
dao_bulla_blind,
signature_secret,
};
let (params, dao_mint_proofs) = builder.build(&zk_bins);
// Write the actual call data
let mut calldata = Vec::new();
// Selects which path executes in the contract.
calldata.write_u8(DaoFunction::Mint as u8)?;
params.encode(&mut calldata)?;
let calls = vec![ContractCall { contract_id: dao_contract_id, data: calldata }];
//let mut payload = Vec::new();
////// Write the actual payload data
//let call_index = 0;
//payload.write_u32(call_index)?;
//func_calls.encode(&mut payload)?;
let signatures = vec![];
//for func_call in &func_calls {
// let sign = sign([signature_secret].to_vec(), func_call);
// signatures.push(sign);
//}
//let tx = Transaction { func_calls, signatures };
let proofs = vec![dao_mint_proofs];
Transaction { calls, proofs, signatures }
};
//// Validator
// We can do all exec(), zk proof checks and signature verifies in parallel.
let mut updates = vec![];
// Validate all function calls in the tx
for (idx, call) in tx.calls.iter().enumerate() {
// So then the verifier will lookup the corresponding state_transition and apply
// functions based off the func_id
// Write the actual payload data
let mut payload = Vec::new();
// Call index
payload.write_u32(idx as u32)?;
// Actuall calldata
tx.calls.encode(&mut payload)?;
match call.contract_id {
dao_contract_id => {
debug!(target: "demo", "DAO::exec() contract called");
let mut runtime =
Runtime::new(&dao_wasm_bytes, blockchain.clone(), dao_contract_id)?;
let update = runtime.exec(&payload)?;
updates.push(update);
}
money_contract_id => {
debug!(target: "demo", "Money::exec() contract called");
let mut runtime =
Runtime::new(&money_wasm_bytes, blockchain.clone(), money_contract_id)?;
let update = runtime.exec(&payload)?;
updates.push(update);
}
_ => {}
}
}
//tx.zk_verify(&zk_bins).unwrap();
//tx.verify_sigs();
// Now we finished verification stage, just apply all changes
assert_eq!(tx.calls.len(), updates.len());
for (call, update) in tx.calls.iter().zip(updates.iter()) {
match call.contract_id {
dao_contract_id => {
debug!(target: "demo", "DAO::apply() contract called");
let mut runtime =
Runtime::new(&dao_wasm_bytes, blockchain.clone(), dao_contract_id)?;
runtime.apply(&update)?;
}
money_contract_id => {
debug!(target: "demo", "Money::apply() contract called");
let mut runtime =
Runtime::new(&money_wasm_bytes, blockchain.clone(), money_contract_id)?;
runtime.apply(&update)?;
}
_ => {}
}
}
/////////////////////////////////////////////////
// Old stuff
/////////////////////////////////////////////////
/*
let wasm_bytes = std::fs::read("dao_contract.wasm")?;
let dao_contract_id = ContractId::from(pallas::Base::from(1));
let mut runtime = Runtime::new(&wasm_bytes, blockchain.clone(), dao_contract_id)?;
@@ -105,8 +362,10 @@ async fn main() -> BoxResult<()> {
let mut decoder = Cursor::new(&metadata);
let zk_public_values: Vec<(String, Vec<pallas::Base>)> = Decodable::decode(&mut decoder)?;
let signature_public_keys: Vec<pallas::Point> = Decodable::decode(decoder)?;
*/
show_dao_state(&blockchain, &dao_contract_id)?;
show_money_state(&blockchain, &money_contract_id)?;
Ok(())
}

View File

@@ -174,6 +174,7 @@ pub async fn example() -> Result<()> {
}
pub async fn schema() -> Result<()> {
/*
// Example smart contract
//// TODO: this will be moved to a different file
example().await?;
@@ -1288,7 +1289,7 @@ pub async fn schema() -> Result<()> {
tx.verify_sigs();
//// Wallet
*/
Ok(())
}

View File

@@ -29,7 +29,7 @@ use crate::{
/// Internal wasm runtime API for sled trees
pub struct DbHandle {
contract_id: ContractId,
pub contract_id: ContractId,
tree: sled::Tree,
}

View File

@@ -19,5 +19,8 @@
/// Host functions for interacting with db backend
pub(crate) mod db;
/// Host functions for merkle tree functions
pub(crate) mod merkle;
/// Host functions for utilities
pub(crate) mod util;

View File

@@ -222,6 +222,12 @@ impl Runtime {
&ctx,
import::util::get_object_size,
),
"merkle_add_" => Function::new_typed_with_env(
&mut store,
&ctx,
import::merkle::merkle_add,
),
}
};

View File

@@ -54,6 +54,10 @@ lazy_static! {
pub struct MerkleNode(pallas::Base);
impl MerkleNode {
pub fn new(v: pallas::Base) -> Self {
Self(v)
}
/// Reference the raw inner base field element
pub fn inner(&self) -> pallas::Base {
self.0

View File

@@ -6,7 +6,7 @@ use super::{
util::{get_object_bytes, get_object_size},
};
type DbHandle = u32;
pub type DbHandle = u32;
type TxHandle = u32;
/// Only deploy() can call this. Creates a new database instance for this contract.

View File

@@ -34,6 +34,9 @@ pub mod log;
/// Crypto-related definitions
pub mod crypto;
/// Merkle
pub mod merkle;
/// Transaction structure
pub mod tx;

View File

@@ -6,5 +6,5 @@ use super::crypto::ContractId;
#[derive(SerialEncodable, SerialDecodable)]
pub struct ContractCall {
pub contract_id: ContractId,
pub calldata: Vec<u8>,
pub data: Vec<u8>,
}

View File

@@ -73,8 +73,6 @@ pub fn deserialize<T: Decodable>(data: &[u8]) -> Result<T, Error> {
/// Extensions of `Write` to encode data as per Bitcoin consensus.
pub trait WriteExt {
/// Output a platform-specific unsigned int (DANGEROUS AND UNDEFINED)
fn write_usize(&mut self, v: usize) -> Result<(), Error>;
/// Output a 128-bit unsigned int
fn write_u128(&mut self, v: u128) -> Result<(), Error>;
/// Output a 64-bit unsigned int
@@ -86,8 +84,6 @@ pub trait WriteExt {
/// Output an 8-bit unsigned int
fn write_u8(&mut self, v: u8) -> Result<(), Error>;
/// Output a platform-specific signed int (DANGEROUS AND UNDEFINED)
fn write_isize(&mut self, v: isize) -> Result<(), Error>;
/// Output a 128-bit signed int
fn write_i128(&mut self, v: i128) -> Result<(), Error>;
/// Output a 64-bit signed int
@@ -113,8 +109,6 @@ pub trait WriteExt {
/// Extensions of `Read` to decode data as per Bitcoin consensus.
pub trait ReadExt {
/// Read a platform-specific unsigned int (DANGEROUS AND UNDEFINED)
fn read_usize(&mut self) -> Result<usize, Error>;
/// Read a 128-bit unsigned int
fn read_u128(&mut self) -> Result<u128, Error>;
/// Read a 64-bit unsigned int
@@ -126,8 +120,6 @@ pub trait ReadExt {
/// Read an 8-bit unsigned int
fn read_u8(&mut self) -> Result<u8, Error>;
/// Read a platform-specific signed int (DANGEROUS AND UNDEFINED)
fn read_isize(&mut self) -> Result<isize, Error>;
/// Read a 128-bit signed int
fn read_i128(&mut self) -> Result<i128, Error>;
/// Read a 64-bit signed int
@@ -173,12 +165,10 @@ macro_rules! decoder_fn {
}
impl<W: Write> WriteExt for W {
encoder_fn!(write_usize, usize, usize_to_array_le);
encoder_fn!(write_u128, u128, u128_to_array_le);
encoder_fn!(write_u64, u64, u64_to_array_le);
encoder_fn!(write_u32, u32, u32_to_array_le);
encoder_fn!(write_u16, u16, u16_to_array_le);
encoder_fn!(write_isize, isize, isize_to_array_le);
encoder_fn!(write_i128, i128, i128_to_array_le);
encoder_fn!(write_i64, i64, i64_to_array_le);
encoder_fn!(write_i32, i32, i32_to_array_le);
@@ -205,12 +195,10 @@ impl<W: Write> WriteExt for W {
}
impl<R: Read> ReadExt for R {
decoder_fn!(read_usize, usize, slice_to_usize_le, usize::BITS as usize / 8);
decoder_fn!(read_u128, u128, slice_to_u128_le, 16);
decoder_fn!(read_u64, u64, slice_to_u64_le, 8);
decoder_fn!(read_u32, u32, slice_to_u32_le, 4);
decoder_fn!(read_u16, u16, slice_to_u16_le, 2);
decoder_fn!(read_isize, isize, slice_to_isize_le, isize::BITS as usize / 8);
decoder_fn!(read_i128, i128, slice_to_i128_le, 16);
decoder_fn!(read_i64, i64, slice_to_i64_le, 8);
decoder_fn!(read_i32, i32, slice_to_i32_le, 4);
@@ -264,14 +252,12 @@ impl_int_encodable!(u16, read_u16, write_u16);
impl_int_encodable!(u32, read_u32, write_u32);
impl_int_encodable!(u64, read_u64, write_u64);
impl_int_encodable!(u128, read_u128, write_u128);
impl_int_encodable!(usize, read_usize, write_usize);
impl_int_encodable!(i8, read_i8, write_i8);
impl_int_encodable!(i16, read_i16, write_i16);
impl_int_encodable!(i32, read_i32, write_i32);
impl_int_encodable!(i64, read_i64, write_i64);
impl_int_encodable!(i128, read_i128, write_i128);
impl_int_encodable!(isize, read_isize, write_isize);
/// Variable-integer encoding
#[derive(Debug, PartialEq, Eq)]
@@ -397,6 +383,21 @@ macro_rules! encode_payload {
}
// Implementations for some primitive types.
impl Encodable for usize {
#[inline]
fn encode<S: WriteExt>(&self, mut s: S) -> Result<usize, Error> {
s.write_u64(*self as u64)?;
Ok(8)
}
}
impl Decodable for usize {
#[inline]
fn decode<D: Read>(mut d: D) -> Result<Self, Error> {
Ok(ReadExt::read_u64(&mut d)? as usize)
}
}
impl Encodable for f64 {
#[inline]
fn encode<S: WriteExt>(&self, mut s: S) -> Result<usize, Error> {