From 3586fe1b072356ba4ac6a0f57cdfa2871e1e204c Mon Sep 17 00:00:00 2001 From: Enzo Cioppettini Date: Thu, 7 Nov 2019 11:13:22 -0300 Subject: [PATCH 1/4] update submodule --- chain-libs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain-libs b/chain-libs index e8b73f2..833ecec 160000 --- a/chain-libs +++ b/chain-libs @@ -1 +1 @@ -Subproject commit e8b73f2a072306af2b6af5ae495951547c55cbbf +Subproject commit 833ececb20cacc5e9677d54935a42bfe340b07f0 From 45eb3ae191bc7381abc1fe372eb5eb9edcd8101d Mon Sep 17 00:00:00 2001 From: Enzo Cioppettini Date: Thu, 7 Nov 2019 18:08:32 -0300 Subject: [PATCH 2/4] rewrite tests to the new api/binary format --- tests/test_get_block_messages.js | 17 ++-- tests/test_make_transaction.js | 54 +++++++++---- ...est_stake_pool_registration_certificate.js | 6 -- tests/web.rs | 79 ++++++++----------- 4 files changed, 82 insertions(+), 74 deletions(-) diff --git a/tests/test_get_block_messages.js b/tests/test_get_block_messages.js index 2c31d80..c26e629 100644 --- a/tests/test_get_block_messages.js +++ b/tests/test_get_block_messages.js @@ -3,8 +3,8 @@ import { expect } from 'chai'; const rust = import('../pkg/js_chain_libs'); const binaryBlock = - '02b6000200000098000000d900002ce400001d9612b541b99e5e748050744ef8810505cd099d408f50c41db24bd1f300107fb4b7c8d4e10f9adb12c9010dc6059cc8f3860855dc89031ac05f670f8935355eca85da2d5f25f4d538b7a2c99ef41d2127444428de19be327282473db763442ad61ddc805d7975ae4b2e726c3be024a68b53a9b78c41df70caf318f7ba5a96174d19c4731e898e2e8a6492c0883fc77b3b9bb80c340fea9e4d817c287b88cf2b120935aaf807b819c5494631102ec9467b520789891b6f303375d7dbcb49f0ce030f00000000728edf17265ee3f786834873385a236229bb7f55c0ed3aaecd922f2ce0f8d10bca1d319ac343f3af45eef3dea9333b32da72c58df71e935512134091f375740663259f4fc0a3d893f023b79c0dde20822ea821aff217f50fa0d1b955fc2ede1c2a7324df3397888f73844a06ee2a2e9fcfb070efbde8b6592326653e0dcd39f6729c13227cfe8a2252d48ab86570be1279ca8fde09ec1e03293304e466e363893626e5090a36e1a2a1418c2c5171cc6e5f61bdaf0bc652265710f41793eb8c19c2e6cd096588f2b73f813ce2816a5dc9a25d29f8b1343f545f5998fea2184052fed8d2594bdcc6dfe26e7d2af02410c96e9c2229f6ec9a892754b01b83ae8f84e1b56fcc59e040dc9b931a967cc233cd72a1149684ed416233a39d5624027508279520b5f46f5498fc9e26a876528e8600ef63ee6a986cf234365a595c8986114c3b76d4e24e773a8736c7aba5fa4adc0cf6d250e28bec26213a1443656ad902d2818ab129793c1c10aa1f7939651d2c883749992dc57d69b3afd79166db2b77ada12881d6eccafddcf86a555d04f9ca04d262ec0d38796d6e675d3a5b859bcf49d2ac8832f778ad73f3d3ea9e5c63c6d4234f57286c9053f5f8f97fa112261c86427a2847a0c5bae85f2969e6f5a8c99dda3891275cacf957c1606395bdc67e0096020101ff0000003a3529484ca251a5c84c7f536a3a1f9e2f6e6e3a3cc35b36c5e6797ebed34dea32a152da00858d5344770d819d53dd8dc5ee62e64a3acd325c05bd1a3c50c0b953f6f311d5380000003a3529440002b87743842c19a52bf93519b929251cb1c131c49ae09604951bdea6c44b49b7985d47d1afb451be5ac04083802b31db43d07de9850385eefa815278a92f99b408'; -const id = '2a75779788fbf81b08b24f46cea7858256eaa5ee1db51255d9a556926759469f'; + '02b6000200000099000000000000004300000001ced88d77dfb26551c9ea00a34c4abca03ca0ff8fa728134f3510b7c99002ba55e31f182243d4906e3ca8a6b0e3f4e361e9610d79baf5367672932f1d00cf0554e99b13904e99b0b4ad72c1f330c2ffaf00b554cbf6ef8c5f4d6406552d371cce7020ced4b032198d378369a5dc1d8d8f84a8287c6cbef4e3dc6679013113ac726d9c4c4d69ea617e4dc2590c77c8b190ba907b6884b50d885cd7382737dec800bae349261b442f4e36c7bdb3b60cb4ef173370d85ec40dad0ccefd8bc33ea409000000001a2359461e0df9d9b4129be41b8ac4e5eb72f2095039c192258d3773c45bfbeb3b0d9dda40ba31681b48b380b850038b2f3da308ae82e11d40949cf9a2bf61039743b7404bafb2a67770b16537c188f48720eba6f44aacd37815cdbe8ce0aae8ec41aca293c6390ab04de4ad292e0007d1f5fcf6c235da2cd800ee424d93631de86f985d7aad165545c0a9817aebcf8274c96daa0a4302419ef6748248d72a015e2798462e0521cbcfd14b825ce028ac135b60f0ec629855d01b4b1338ab1adda90984b9dcf0dd1d01df434c7f3c934e2466c5e1f9f121c10b3760360654f435b17efa34662e25880ff12520937685ad3700b251569ca2ddb6fe904e5ff7bda2116862f7c85d8024cfae12718d9a998ca257ad3c2497e030195d8ce637d02762fe3bdfd6159c7876257bb41042cda86d07c6d6cd2b851de783370e059424938e4508d6551f5dea86fad07231dcdd0cce6a35dc76834b8d0af20085f67f5d7b26622cea531128c1fb6b22a341e984d4ec731cbb791279111c1bec3df402308f3e0c0be65599e128cc5a0a2e413d4d040cea0dc59e309c1393247c7c233b24bb9901859ab20cde9040b2f682a72c28fe0f440be6e3e5fdc51c32553cd6b035e4b448603c3d3fd0eedda7a59ad569bdc359132246a0390ad0ede988e61f1afa421c009702000101ff00000000000003f2502238e65821d0b8341ea261b1756b8049cb3630eb3ebb0b54323b3cd010b8d285b7b0199dcc3b976ba44603685c707e56778efabf17617d7ab69a1465c4e8dccf00000000000003e802d1f4ecf972bb39a7217e46f2ca5f99213b3adc810dd859e223d2e14012954a5cccea45cdf48f908c39daaaa1d936c6ce842279a7ebbaea895e212a7fd3421802'; +const id = 'abdfc4fa669ca076a97da7dfa4133102db255e577fff8ad5d3a2763cf8ef64d1'; it('get block messages', async () => { const { @@ -34,13 +34,12 @@ it('get block messages', async () => { .id() .as_bytes() ) - ).to.eql('a5eccca550ef6f04a3b82c9ce9496a952be41f5e013997aa5aab0e854b27c251'); + ).to.eql('db4460c9b6fefad53574a56e20cda08e9664e2fa5b26133b9c20e9f18881d57b'); const transaction = block .fragments() .get(0) - .get_transaction() - .transaction(); + .get_transaction(); const inputs = transaction.inputs(); const outputs = transaction.outputs(); @@ -56,16 +55,16 @@ it('get block messages', async () => { .get_account() .to_address(AddressDiscrimination.Test) .to_string('addr') - ).to.eql('addr1sk39rfwgf3l4x636r70z7mnw8g7vxkekchn8jl476dx75v4p2tdqq30rhxj'); + ).to.eql('addr1s4gzyw8xtqsapwp5r63xrvt4dwqynjekxr4nawct2serk0xszzudyx327sp'); - expect(input.value().to_str()).to.eql('250000001100'); + expect(input.value().to_str()).to.eql('1010'); const output = outputs.get(0); expect(output.address().to_string('addr')).to.eql( - 'addr1skx4x3rhpkqe657a3hz7uchxfgav6vjuqk7350zsczu48ahnz82nspnkqkc' + 'addr1skmmqxvaesaew6aygcpkshrs0et80rh6hutkzlt6k6dpgewyarwv7kjsaw8' ); - expect(output.value().to_str()).to.equal('250000000000'); + expect(output.value().to_str()).to.equal('1000'); }); function hexStringToBytes(string) { diff --git a/tests/test_make_transaction.js b/tests/test_make_transaction.js index d40aa2f..a92962e 100644 --- a/tests/test_make_transaction.js +++ b/tests/test_make_transaction.js @@ -33,7 +33,6 @@ it('create transaction', async () => { Fee, PublicKey, Certificate, - TransactionFinalizer, Fragment, PrivateKey, Witness, @@ -41,29 +40,37 @@ it('create transaction', async () => { Hash, Account, StakeDelegation, + DelegationType, + InputOutputBuilder, + PayloadAuthData, + StakeDelegationAuthData, + AccountBindingSignature, + Payload, + Witnesses, // eslint-disable-next-line camelcase uint8array_to_hex } = await rust; + const singlePoolDelegation = DelegationType.full( + PoolId.from_hex(delegation.poolId) + ); + const certificate = Certificate.stake_delegation( StakeDelegation.new( - PoolId.from_hex(delegation.poolId), + singlePoolDelegation, PublicKey.from_bech32(delegation.stakeKey) ) ); - certificate.sign(PrivateKey.from_bech32(delegation.privateKey)); - - const txbuilder = TransactionBuilder.new_payload(certificate); + const iobuilder = InputOutputBuilder.empty(); const accountAddress = Address.from_string(inputAccount.address); const account = Account.from_address(accountAddress); const input = Input.from_account(account, Value.from_str(inputAccount.value)); + iobuilder.add_input(input); - txbuilder.add_input(input); - - txbuilder.add_output( + iobuilder.add_output( Address.from_string(outputAccount.address), Value.from_str(outputAccount.value) ); @@ -74,27 +81,44 @@ it('create transaction', async () => { Value.from_str('10') ); - const finalizedTx = txbuilder.finalize( + const IOs = iobuilder.seal_with_output_policy( + Payload.certificate(certificate), feeAlgorithm, OutputPolicy.one(accountAddress) ); - const finalizer = new TransactionFinalizer(finalizedTx); + const builderSetWitness = new TransactionBuilder() + .payload(certificate) + .set_ios(IOs.inputs(), IOs.outputs()); + + const txid = builderSetWitness.get_auth_data_for_witness(); const witness = Witness.for_account( Hash.from_hex(genesisHash), - finalizer.get_txid(), + txid, PrivateKey.from_bech32(inputAccount.privateKey), SpendingCounter.zero() ); - finalizer.set_witness(0, witness); + const witnesses = Witnesses.new(); + witnesses.add(witness); + + const builderSignCertificate = builderSetWitness.set_witnesses(witnesses); + + const signature = PayloadAuthData.for_stake_delegation( + StakeDelegationAuthData.new( + AccountBindingSignature.new( + PrivateKey.from_bech32(delegation.privateKey), + builderSignCertificate.get_auth_data() + ) + ) + ); - const signedTx = finalizer.build(); + const signedTx = builderSignCertificate.set_payload_auth(signature); - const message = Fragment.from_authenticated_transaction(signedTx); + const message = Fragment.from_transaction(signedTx); expect(uint8array_to_hex(message.as_bytes())).to.eql( - '00ff04cbc7ccdb9a51eea4e4c7088353c1a1902dcf685f739194dc9faff26b7f42e219541db50349e2bc1a5b1a73939b9d86fc45067117cc930c36afbb6fb0a9329d410102ff00000000000003e8cbc7ccdb9a51eea4e4c7088353c1a1902dcf685f739194dc9faff26b7f42e21905263a058b2e6817bcb058e1734da381a995e3335c3e150dd2d621b3c136557aa700000000000001f405cbc7ccdb9a51eea4e4c7088353c1a1902dcf685f739194dc9faff26b7f42e21900000000000001c702ad3b6faf39aad65bfd793f25e2ec37fdddf6c4ca36a3c8563b659da30c3e7cfc2f1f276aaaf2a089013f301e04c8aae0fb42a958997c93f0400b0b830d64970a' + '01410400cbc7ccdb9a51eea4e4c7088353c1a1902dcf685f739194dc9faff26b7f42e21901541db50349e2bc1a5b1a73939b9d86fc45067117cc930c36afbb6fb0a9329d410102ff00000000000003e8cbc7ccdb9a51eea4e4c7088353c1a1902dcf685f739194dc9faff26b7f42e21905263a058b2e6817bcb058e1734da381a995e3335c3e150dd2d621b3c136557aa700000000000001f405cbc7ccdb9a51eea4e4c7088353c1a1902dcf685f739194dc9faff26b7f42e21900000000000001c702ab84ad4220573f1d5037af717d0999aa3726d6f549cd1a9d12feefb20ba0e7211546fb38015196e0061fe2f87d241a54529f93a5c6d4c53be27b43057883e40ee3d1cbb9679be5d0604fac4db2d6768bb332fa053f9db9aee69f8356926e82b31cf20aaec905d3988e2e5b29bc39f25e45583c4a5d765db5f952dc0b38effa0c' ); }); diff --git a/tests/test_stake_pool_registration_certificate.js b/tests/test_stake_pool_registration_certificate.js index b214f4f..1986550 100644 --- a/tests/test_stake_pool_registration_certificate.js +++ b/tests/test_stake_pool_registration_certificate.js @@ -42,10 +42,4 @@ it('generates certificate', async () => { expect(poolRegistration.id().to_string()).to.eql( '6d7afbf4a8e0574a5dbbf35a71a42da86ebf679d864a03153446a4e671b26edc' ); - const certificate = Certificate.stake_pool_registration(poolRegistration); - certificate.sign( - PrivateKey.from_bech32( - 'ed25519_sk1mr52spjelqhs5hegxuymh8la04gkrxh5543cf4c7hx8x3mz3sadqdfdx5y' - ) - ); }); diff --git a/tests/web.rs b/tests/web.rs index 2d082b1..4597ec1 100644 --- a/tests/web.rs +++ b/tests/web.rs @@ -34,26 +34,27 @@ fn parse_bech32_normal_secret_key() { assert!(PrivateKey::from_bech32(key).is_ok()); } -fn mock_builder(input: u64, output: u64) -> TransactionBuilder { - let mut txbuilder = TransactionBuilder::new_no_payload(); +fn mock_io_builder(input: u64, output: u64) -> InputOutputBuilder { + let mut builder = InputOutputBuilder::empty(); + let txid = FragmentId::from_bytes(&[0]); let utxopointer = UtxoPointer::new(txid, 0, input.into()); let input = Input::from_utxo(&utxopointer); - txbuilder.add_input(input); + builder.add_input(input).unwrap(); let output_address = Address::from_string("ca1qh9u0nxmnfg7af8ycuygx57p5xgzmnmgtaeer9xun7hly6mlgt3pj2xk344") .unwrap(); - txbuilder.add_output(output_address, output.into()); - txbuilder + builder.add_output(output_address, output.into()).unwrap(); + builder } #[wasm_bindgen_test] fn transaction_builder_balance() { - let txbuilder = mock_builder(32, 20); + let iobuilder = mock_io_builder(32, 20); let fee_algorithm = Fee::linear_fee(2u64.into(), 0u64.into(), 0u64.into()); - let balance = txbuilder.get_balance(&fee_algorithm).unwrap(); + let balance = iobuilder.get_balance(None, &fee_algorithm).unwrap(); assert_eq!(balance.get_sign(), "positive"); assert_eq!(balance.get_value(), (32u64 - 20 - 2).into()); @@ -61,57 +62,47 @@ fn transaction_builder_balance() { #[wasm_bindgen_test] fn transaction_builder_finalize_good_case() { - let txbuilder = mock_builder(32, 20); + let iobuilder = mock_io_builder(32, 20); let fee_algorithm = Fee::linear_fee(2u64.into(), 0u64.into(), 0u64.into()); - let output_policy = OutputPolicy::forget(); - let transaction = txbuilder.seal_with_output_policy(&fee_algorithm, output_policy); - assert!(transaction.is_ok()) -} + let txbuilder = TransactionBuilder::new(); + let set_payload = txbuilder.no_payload(); -#[wasm_bindgen_test] -fn transaction_builder_finalize_not_enough_input() { - let txbuilder = mock_builder(30, 31); - - let fee_algorithm = Fee::linear_fee(2u64.into(), 0u64.into(), 0u64.into()); let output_policy = OutputPolicy::forget(); + let ios = iobuilder + .seal_with_output_policy(None, fee_algorithm, output_policy) + .unwrap(); - let transaction = txbuilder.seal_with_output_policy(&fee_algorithm, output_policy); - assert!(transaction.is_err()) -} - -#[wasm_bindgen_test] -fn transaction_finalizer() { - let txbuilder = mock_builder(10, 5); + let set_witness = set_payload.set_ios(ios.inputs(), ios.outputs()); - let tx = txbuilder.unchecked_finalize(); - - let mut finalizer = TransactionFinalizer::new(tx); let genesis_hash = Hash::from_bytes(&[0]); - let txid = finalizer.get_tx_sign_data_hash(); + + let txid = set_witness.get_auth_data_for_witness(); let key = PrivateKey::from_bech32("ed25519e_sk1lzkckzvwh7gn5f0krrmrxlpsywypu3kka2u82l3akm5gr8khra8suz6zv5jcwg8h6jy4pjs4dfvcrja07q9758xctp6cgkn5ykkgj9cts0mef").unwrap(); let witness = Witness::for_utxo(genesis_hash, txid, key); - assert!(finalizer.set_witness(0, witness).is_ok()); - assert!(finalizer.finalize().is_ok()) + + let mut witnesses = Witnesses::new(); + witnesses.add(witness); + + let add_auth_data = set_witness.set_witnesses(witnesses); + + let auth_data = PayloadAuthData::for_no_payload(); + + let transaction = add_auth_data.set_payload_auth(auth_data); + + assert!(transaction.is_ok()) } #[wasm_bindgen_test] -fn add_transaction_faucet_input() {} +fn io_builder_finalize_not_enough_input() { + let iobuilder = mock_io_builder(30, 31); -/* #[wasm_bindgen_test] -fn stake_delegation_certificate() { - let stake_pool_id = - StakePoolId::from_hex("541db50349e2bc1a5b1a73939b9d86fc45067117cc930c36afbb6fb0a9329d41") - .unwrap(); - let public_key = PublicKey::from_bech32( - "ed25519_pk1ycaqtzewdqtmevzcu9e5mgup4x27xv6u8c2sm5kkyxeuzdj402ns0uny5a", - ) - .unwrap(); - let certificate = Certificate::stake_delegation(stake_pool_id, public_key); - let mut txbuilder = mock_builder(30, 20); - assert!(txbuilder.set_certificate(certificate).is_ok()); + let fee_algorithm = Fee::linear_fee(2u64.into(), 0u64.into(), 0u64.into()); + let output_policy = OutputPolicy::forget(); + + let input_output = iobuilder.seal_with_output_policy(None, fee_algorithm, output_policy); + assert!(input_output.is_err()) } - */ #[wasm_bindgen_test] fn account_address_from_public_key() { From 4a474c4b8c64b17ec98fb297cedf4bfbbc3b35f0 Mon Sep 17 00:00:00 2001 From: Enzo Cioppettini Date: Thu, 7 Nov 2019 18:11:04 -0300 Subject: [PATCH 3/4] Rewrite TransactionBuilder to the new API Add Certificate signing schema Add Ratio delegation --- src/lib.rs | 777 ++++++----------------------------- src/transaction/iobuilder.rs | 178 ++++++++ src/transaction/mod.rs | 93 +++++ src/transaction/txbuilder.rs | 354 ++++++++++++++++ src/utils.rs | 33 ++ 5 files changed, 784 insertions(+), 651 deletions(-) create mode 100644 src/transaction/iobuilder.rs create mode 100644 src/transaction/mod.rs create mode 100644 src/transaction/txbuilder.rs diff --git a/src/lib.rs b/src/lib.rs index ddf2069..5269688 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,10 @@ +#[macro_use] +mod transaction; +#[macro_use] mod utils; use bech32::{Bech32, ToBase32 as _}; -use chain::{account, certificate, fee, key, transaction as tx, txbuilder, value}; +use chain::{account, certificate, fee, key, transaction as tx, value}; use chain_core::property::Block as _; use chain_core::property::Deserialize as _; use chain_core::property::Fragment as _; @@ -17,6 +20,8 @@ use std::ops::{Add, Sub}; use std::str::FromStr; use wasm_bindgen::prelude::*; +pub use transaction::*; + /// ED25519 signing key, either normal or extended #[wasm_bindgen] pub struct PrivateKey(key::EitherEd25519SecretKey); @@ -256,363 +261,18 @@ impl Into for AddressDiscrimination { } } -//-----------------------------------// -//-------- Transaction --------------// -//-----------------------------------// - -/// Type representing a unsigned transaction -#[wasm_bindgen] -pub struct Transaction(EitherTransaction); - -enum EitherTransaction { - NoCertificate(tx::Transaction), - Certificate(tx::Transaction), -} - -impl EitherTransaction { - fn id(&self) -> TransactionSignDataHash { - match &self { - EitherTransaction::NoCertificate(tx) => { - let finalizer_tx = tx::Transaction { - inputs: tx.inputs.clone(), - outputs: tx.outputs.clone(), - extra: None, - }; - chain::txbuilder::TransactionFinalizer::new(finalizer_tx).get_tx_sign_data_hash() - } - EitherTransaction::Certificate(tx) => { - let finalizer_tx = tx::Transaction { - inputs: tx.inputs.clone(), - outputs: tx.outputs.clone(), - extra: Some(tx.extra.clone()), - }; - chain::txbuilder::TransactionFinalizer::new(finalizer_tx).get_tx_sign_data_hash() - } - } - .into() - } - - fn inputs(&self) -> Vec { - match &self { - EitherTransaction::NoCertificate(tx) => tx.inputs.clone(), - EitherTransaction::Certificate(tx) => tx.inputs.clone(), - } - .to_vec() - } - - fn outputs(&self) -> Vec> { - match &self { - EitherTransaction::NoCertificate(ref tx) => tx.outputs.clone(), - EitherTransaction::Certificate(ref tx) => tx.outputs.clone(), - } - .to_vec() - } - - fn clone(&self) -> EitherTransaction { - match &self { - EitherTransaction::NoCertificate(tx) => EitherTransaction::NoCertificate(tx.clone()), - EitherTransaction::Certificate(tx) => EitherTransaction::Certificate(tx.clone()), - } - } -} - -impl From> for Transaction { - fn from(tx: tx::Transaction) -> Self { - Transaction(EitherTransaction::NoCertificate(tx)) - } -} - -impl From> for Transaction { - fn from(tx: tx::Transaction) -> Self { - Transaction(EitherTransaction::Certificate(tx)) - } -} - -macro_rules! impl_collection { - ($collection:ident, $type:ty) => { - #[wasm_bindgen] - pub struct $collection(Vec<$type>); - - #[wasm_bindgen] - impl $collection { - pub fn size(&self) -> usize { - self.0.len() - } - - pub fn get(&self, index: usize) -> $type { - self.0[index].clone() - } - } - - impl From> for $collection { - fn from(vec: Vec<$type>) -> $collection { - $collection(vec) - } - } - }; -} - impl_collection!(Outputs, Output); impl_collection!(Inputs, Input); impl_collection!(Fragments, Fragment); -#[wasm_bindgen] -impl Transaction { - /// Get the transaction id, needed to compute its signature - pub fn id(&self) -> TransactionSignDataHash { - self.0.id() - } - - /// Get collection of the inputs in the transaction (this allocates new copies of all the values) - pub fn inputs(&self) -> Inputs { - self.0 - .inputs() - .iter() - .map(|input| Input(input.clone())) - .collect::>() - .into() - } - - /// Get collection of the outputs in the transaction (this allocates new copies of all the values) - pub fn outputs(&self) -> Outputs { - self.0 - .outputs() - .iter() - .map(|output| Output(output.clone())) - .collect::>() - .into() - } - - pub fn clone(&self) -> Transaction { - Transaction(self.0.clone()) - } -} - -//-----------------------------------// -//--------TransactionBuilder---------// -//-----------------------------------// - -/// Builder pattern implementation for making a Transaction -/// -/// Example -/// -/// ```javascript -/// const txbuilder = new TransactionBuilder(); -/// -/// const account = Account.from_address(Address.from_string( -/// 'ca1qh9u0nxmnfg7af8ycuygx57p5xgzmnmgtaeer9xun7hly6mlgt3pj2xk344' -/// )); -/// -/// const input = Input.from_account(account, Value.from_str('1000')); -/// -/// txbuilder.add_input(input); -/// -/// txbuilder.add_output( -/// Address.from_string( -/// 'ca1q5nr5pvt9e5p009strshxndrsx5etcentslp2rwj6csm8sfk24a2w3swacn' -/// ), -/// Value.from_str('500') -/// ); -/// -/// const feeAlgorithm = Fee.linear_fee( -/// Value.from_str('20'), -/// Value.from_str('5'), -/// Value.from_str('0') -/// ); -/// -/// const finalizedTx = txbuilder.finalize( -/// feeAlgorithm, -/// OutputPolicy.one(accountInputAddress) -/// ); -/// ``` -#[wasm_bindgen] -pub struct TransactionBuilder(EitherTransactionBuilder); - -enum EitherTransactionBuilder { - TransactionBuilderNoExtra(txbuilder::TransactionBuilder), - TransactionBuilderCertificate(txbuilder::TransactionBuilder), -} - -impl From> for TransactionBuilder { - fn from(builder: txbuilder::TransactionBuilder) -> Self { - TransactionBuilder(EitherTransactionBuilder::TransactionBuilderNoExtra(builder)) - } -} - -impl From> for TransactionBuilder { - fn from(builder: txbuilder::TransactionBuilder) -> Self { - TransactionBuilder(EitherTransactionBuilder::TransactionBuilderCertificate( - builder, - )) - } -} - -#[wasm_bindgen] -impl TransactionBuilder { - #[wasm_bindgen(constructor)] - /// Deprecated. Use `new_no_payload()` instead - pub fn new() -> Self { - Self::new_no_payload() - } - - /// Create a TransactionBuilder for a transaction without certificate - pub fn new_no_payload() -> Self { - txbuilder::TransactionBuilder::no_payload().into() - } - - /// Create a TransactionBuilder for a transaction with certificate - pub fn new_payload(cert: Certificate) -> Self { - txbuilder::TransactionBuilder::new_payload(cert.0).into() - } - - /// Add input to the transaction - #[wasm_bindgen] - pub fn add_input(&mut self, input: Input) { - match &mut self.0 { - EitherTransactionBuilder::TransactionBuilderNoExtra(ref mut builder) => { - builder.add_input(&input.0) - } - EitherTransactionBuilder::TransactionBuilderCertificate(ref mut builder) => { - builder.add_input(&input.0) - } - } - } - - /// Add output to the transaction - #[wasm_bindgen] - pub fn add_output(&mut self, address: Address, value: Value) { - match &mut self.0 { - EitherTransactionBuilder::TransactionBuilderNoExtra(ref mut builder) => { - builder.add_output(address.0, value.0) - } - EitherTransactionBuilder::TransactionBuilderCertificate(ref mut builder) => { - builder.add_output(address.0, value.0) - } - } - } - - /// Estimate fee with the currently added inputs, outputs and certificate based on the given algorithm - #[wasm_bindgen] - pub fn estimate_fee(&self, fee: &Fee) -> Result { - let fee_algorithm = match fee.0 { - FeeVariant::Linear(fee_algorithm) => fee_algorithm, - }; - match &self.0 { - EitherTransactionBuilder::TransactionBuilderNoExtra(ref builder) => { - builder.estimate_fee(fee_algorithm) - } - EitherTransactionBuilder::TransactionBuilderCertificate(ref builder) => { - builder.estimate_fee(fee_algorithm) - } - } - .map_err(|e| JsValue::from_str(&format!("{:?}", e))) - .map(|value| value.into()) - } - - #[wasm_bindgen] - pub fn get_balance(&self, fee: &Fee) -> Result { - let fee_algorithm = match fee.0 { - FeeVariant::Linear(fee_algorithm) => fee_algorithm, - }; - match &self.0 { - EitherTransactionBuilder::TransactionBuilderNoExtra(ref builder) => { - builder.get_balance(fee_algorithm) - } - EitherTransactionBuilder::TransactionBuilderCertificate(ref builder) => { - builder.get_balance(fee_algorithm) - } - } - .map_err(|e| JsValue::from_str(&format!("{}", e))) - .map(|balance| balance.into()) - } - - #[wasm_bindgen] - pub fn get_balance_without_fee(&self) -> Result { - match &self.0 { - EitherTransactionBuilder::TransactionBuilderNoExtra(ref builder) => { - builder.get_balance_without_fee() - } - EitherTransactionBuilder::TransactionBuilderCertificate(ref builder) => { - builder.get_balance_without_fee() - } - } - .map(|balance| balance.into()) - .map_err(|e| JsValue::from_str(&format!("{}", e))) - } - - /// Get the Transaction with the current inputs and outputs without computing the fees nor adding a change address - #[wasm_bindgen] - pub fn unchecked_finalize(self) -> Transaction { - match self.0 { - EitherTransactionBuilder::TransactionBuilderNoExtra(builder) => builder.tx.into(), - EitherTransactionBuilder::TransactionBuilderCertificate(builder) => builder.tx.into(), - } - } - - /// Finalize the transaction by adding the change Address output - /// leaving enough for paying the minimum fee computed by the given algorithm - /// see the unchecked_finalize for the non-assisted version - /// - /// Example - /// - /// ```javascript - /// const feeAlgorithm = Fee.linear_fee( - /// Value.from_str('20'), Value.from_str('5'), Value.from_str('10') - /// ); - /// - /// const finalizedTx = txbuilder.finalize( - /// feeAlgorithm, - /// OutputPolicy.one(changeAddress) - /// ); - /// ``` - #[wasm_bindgen] - pub fn seal_with_output_policy( - self, - fee: &Fee, - output_policy: OutputPolicy, - ) -> Result { - let fee_algorithm = match fee.0 { - FeeVariant::Linear(fee_algorithm) => fee_algorithm, - }; - - match self.0 { - EitherTransactionBuilder::TransactionBuilderNoExtra(builder) => builder - .seal_with_output_policy(fee_algorithm, output_policy.0) - .map(|(_, tx)| tx.into()), - EitherTransactionBuilder::TransactionBuilderCertificate(builder) => builder - .seal_with_output_policy(fee_algorithm, output_policy.0) - .map(|(_, tx)| tx.into()), - } - .map_err(|e| JsValue::from_str(&format!("{}", e))) - } - - /// Deprecated: use `seal_with_output_policy` instead - pub fn finalize(self, fee: &Fee, output_policy: OutputPolicy) -> Result { - self.seal_with_output_policy(fee, output_policy) - } - - /* /// Get the current Transaction id, this will change when adding input, outputs and certificates - #[wasm_bindgen] - pub fn get_txid(&self) -> TransactionSignDataHash { - match &self.0 { - EitherTransactionBuilder::TransactionBuilderNoExtra(builder) => { - builder.tx.id().into() - } - EitherTransactionBuilder::TransactionBuilderCertificate(builder) => { - builder.tx.hash().into() - } - } - } */ -} - /// Helper to add change addresses when finalizing a transaction, there are currently two options /// * forget: use all the excess money as fee /// * one: send all the excess money to the given address #[wasm_bindgen] -pub struct OutputPolicy(txbuilder::OutputPolicy); +pub struct OutputPolicy(tx::OutputPolicy); -impl From for OutputPolicy { - fn from(output_policy: txbuilder::OutputPolicy) -> OutputPolicy { +impl From for OutputPolicy { + fn from(output_policy: tx::OutputPolicy) -> OutputPolicy { OutputPolicy(output_policy) } } @@ -621,248 +281,12 @@ impl From for OutputPolicy { impl OutputPolicy { /// don't do anything with the excess money in transaction pub fn forget() -> OutputPolicy { - txbuilder::OutputPolicy::Forget.into() + tx::OutputPolicy::Forget.into() } /// use the given address as the only change address pub fn one(address: Address) -> OutputPolicy { - txbuilder::OutputPolicy::One(address.0).into() - } -} - -/// Builder pattern implementation for signing a Transaction (adding witnesses) -/// Example (for an account as input) -/// -/// ```javascript -/// //finalizedTx could be the result of the finalize method on a TransactionBuilder object -/// const finalizer = new TransactionFinalizer(finalizedTx); -/// -/// const witness = Witness.for_account( -/// Hash.from_hex(genesisHashString), -/// finalizer.get_txid(), -/// inputAccountPrivateKey, -/// SpendingCounter.zero() -/// ); -/// -/// finalizer.set_witness(0, witness); -/// -/// const signedTx = finalizer.build(); -/// ``` -#[wasm_bindgen] -pub struct TransactionFinalizer(txbuilder::TransactionFinalizer); - -impl From for TransactionFinalizer { - fn from(finalizer: txbuilder::TransactionFinalizer) -> TransactionFinalizer { - TransactionFinalizer(finalizer) - } -} - -#[wasm_bindgen] -impl TransactionFinalizer { - #[wasm_bindgen(constructor)] - pub fn new(transaction: Transaction) -> Self { - match transaction.0 { - EitherTransaction::Certificate(tx) => { - txbuilder::TransactionFinalizer::new(tx::Transaction { - inputs: tx.inputs, - outputs: tx.outputs, - extra: Some(tx.extra), - }) - .into() - } - EitherTransaction::NoCertificate(tx) => { - txbuilder::TransactionFinalizer::new(tx::Transaction { - inputs: tx.inputs, - outputs: tx.outputs, - extra: None, - }) - .into() - } - } - } - - /// Set the witness for the corresponding index, the index corresponds to the order in which the inputs were added to the transaction - pub fn set_witness(&mut self, index: usize, witness: Witness) -> Result<(), JsValue> { - self.0 - .set_witness(index, witness.0) - .map_err(|e| JsValue::from_str(&format!("{}", e))) - } - - /// Deprecated: Use `get_tx_sign_data_hash` instead" - pub fn get_txid(&self) -> TransactionSignDataHash { - self.get_tx_sign_data_hash() - } - - pub fn get_tx_sign_data_hash(&self) -> TransactionSignDataHash { - self.0.get_tx_sign_data_hash().into() - } - - /// Deprecated: Use `get_tx_sign_data_hash` instead" - pub fn build(self) -> Result { - self.finalize() - } - - pub fn finalize(self) -> Result { - self.0 - .finalize() - .map(|auth_tx| match auth_tx.transaction.extra.clone() { - Some(extra) => AuthenticatedTransaction::from(tx::AuthenticatedTransaction { - transaction: auth_tx.transaction.replace_extra(extra.clone()), - witnesses: auth_tx.witnesses, - }), - None => AuthenticatedTransaction::from(tx::AuthenticatedTransaction { - transaction: auth_tx.transaction.replace_extra(tx::NoExtra), - witnesses: auth_tx.witnesses, - }), - }) - .map_err(|e| JsValue::from_str(&format!("{}", e))) - } -} - -/// Type for representing a Transaction with Witnesses (signatures) -#[wasm_bindgen] -pub struct AuthenticatedTransaction(AuthenticatedTransactionType); - -// This allows to circumvent the lack of generics when exposing the type to js -// I find this simpler as it requires only one level of pattern matching and but it -// also leads to more boilerplate -enum AuthenticatedTransactionType { - NoCertificate(tx::AuthenticatedTransaction), - PoolRegistration( - tx::AuthenticatedTransaction, - ), - PoolManagement(tx::AuthenticatedTransaction), - StakeDelegation( - tx::AuthenticatedTransaction, - ), - OwnerStakeDelegation( - tx::AuthenticatedTransaction, - ), -} - -impl From> - for AuthenticatedTransaction -{ - fn from(tx: tx::AuthenticatedTransaction) -> Self { - AuthenticatedTransaction(AuthenticatedTransactionType::NoCertificate(tx)) - } -} - -impl From> - for AuthenticatedTransaction -{ - fn from( - auth_tx: tx::AuthenticatedTransaction, - ) -> Self { - use certificate::Certificate; - let inner = match &auth_tx.transaction.extra { - Certificate::PoolRegistration(c) => { - AuthenticatedTransactionType::PoolRegistration(tx::AuthenticatedTransaction { - transaction: auth_tx.transaction.clone().replace_extra(c.clone()), - witnesses: auth_tx.witnesses, - }) - } - Certificate::PoolManagement(c) => { - AuthenticatedTransactionType::PoolManagement(tx::AuthenticatedTransaction { - transaction: auth_tx.transaction.clone().replace_extra(c.clone()), - witnesses: auth_tx.witnesses, - }) - } - Certificate::StakeDelegation(c) => { - AuthenticatedTransactionType::StakeDelegation(tx::AuthenticatedTransaction { - transaction: auth_tx.transaction.clone().replace_extra(c.clone()), - witnesses: auth_tx.witnesses, - }) - } - Certificate::OwnerStakeDelegation(c) => { - AuthenticatedTransactionType::OwnerStakeDelegation(tx::AuthenticatedTransaction { - transaction: auth_tx.transaction.clone().replace_extra(c.clone()), - witnesses: auth_tx.witnesses, - }) - } - }; - AuthenticatedTransaction(inner) - } -} - -#[wasm_bindgen] -impl AuthenticatedTransaction { - /// Get a copy of the inner Transaction, discarding the signatures - pub fn transaction(&self) -> Transaction { - match &self.0 { - AuthenticatedTransactionType::NoCertificate(auth_tx) => auth_tx - .transaction - .clone() - .replace_extra(tx::NoExtra) - .into(), - AuthenticatedTransactionType::PoolRegistration(auth_tx) => { - let cert = auth_tx.transaction.extra.clone(); - auth_tx - .transaction - .clone() - .replace_extra(certificate::Certificate::PoolRegistration(cert)) - .into() - } - AuthenticatedTransactionType::PoolManagement(auth_tx) => { - let cert = auth_tx.transaction.extra.clone(); - auth_tx - .transaction - .clone() - .replace_extra(certificate::Certificate::PoolManagement(cert)) - .into() - } - AuthenticatedTransactionType::StakeDelegation(auth_tx) => { - let cert = auth_tx.transaction.extra.clone(); - auth_tx - .transaction - .clone() - .replace_extra(certificate::Certificate::StakeDelegation(cert)) - .into() - } - AuthenticatedTransactionType::OwnerStakeDelegation(auth_tx) => { - let cert = auth_tx.transaction.extra.clone(); - auth_tx - .transaction - .clone() - .replace_extra(certificate::Certificate::OwnerStakeDelegation(cert)) - .into() - } - } - } - - pub fn witnesses(&self) -> Witnesses { - match &self.0 { - AuthenticatedTransactionType::NoCertificate(auth_tx) => auth_tx - .witnesses - .iter() - .map(|witness| Witness(witness.clone())) - .collect::>() - .into(), - AuthenticatedTransactionType::PoolRegistration(auth_tx) => auth_tx - .witnesses - .iter() - .map(|witness| Witness(witness.clone())) - .collect::>() - .into(), - AuthenticatedTransactionType::PoolManagement(auth_tx) => auth_tx - .witnesses - .iter() - .map(|witness| Witness(witness.clone())) - .collect::>() - .into(), - AuthenticatedTransactionType::StakeDelegation(auth_tx) => auth_tx - .witnesses - .iter() - .map(|witness| Witness(witness.clone())) - .collect::>() - .into(), - AuthenticatedTransactionType::OwnerStakeDelegation(auth_tx) => auth_tx - .witnesses - .iter() - .map(|witness| Witness(witness.clone())) - .collect::>() - .into(), - } + tx::OutputPolicy::One(address.0).into() } } @@ -969,7 +393,7 @@ impl Input { } pub fn value(&self) -> Value { - self.0.value.into() + self.0.value().into() } /// Get the inner UtxoPointer if the Input type is Utxo @@ -1218,13 +642,81 @@ impl From for StakeDelegation { } } +#[wasm_bindgen] +/// Set the choice of delegation: +/// +/// * No delegation +/// * Full delegation of this account to a specific pool +/// * Ratio of stake to multiple pools +pub struct DelegationType(chain::account::DelegationType); + +#[wasm_bindgen] +impl DelegationType { + pub fn non_delegated() -> Self { + Self(chain::account::DelegationType::NonDelegated) + } + + pub fn full(pool_id: PoolId) -> Self { + Self(chain::account::DelegationType::Full(pool_id.0)) + } + + pub fn ratio(r: DelegationRatio) -> Self { + Self(chain::account::DelegationType::Ratio(r.0)) + } +} + +/// Delegation Ratio type express a number of parts +/// and a list of pools and their individual parts +/// +/// E.g. parts: 7, pools: [(A,2), (B,1), (C,4)] means that +/// A is associated with 2/7 of the stake, B has 1/7 of stake and C +/// has 4/7 of the stake. +/// +/// It's invalid to have less than 2 elements in the array, +/// and by extension parts need to be equal to the sum of individual +/// pools parts. +#[wasm_bindgen] +pub struct DelegationRatio(chain::account::DelegationRatio); + +#[wasm_bindgen] +#[derive(Clone)] +pub struct PoolDelegationRatio { + pool: PoolId, + part: u8, +} + +#[wasm_bindgen] +impl PoolDelegationRatio { + //TODO: Add constructor attribute + pub fn new(pool: PoolId, part: u8) -> PoolDelegationRatio { + Self { pool, part } + } +} + +impl_collection!(PoolDelegationRatios, PoolDelegationRatio); + +#[wasm_bindgen] +impl DelegationRatio { + //TODO: Add constructor attribute + pub fn new(parts: u8, pools: PoolDelegationRatios) -> Option { + let pools = pools + .0 + .iter() + .map(|PoolDelegationRatio { pool, part }| (pool.0.clone(), *part)) + .collect(); + + // FIXME: It could be useful to return an error instea of an Option? + chain::account::DelegationRatio::new(parts, pools).map(Self) + } +} + #[wasm_bindgen] impl StakeDelegation { /// Create a stake delegation object from account (stake key) to pool_id - pub fn new(pool_id: PoolId, account: PublicKey) -> StakeDelegation { + pub fn new(delegation_type: DelegationType, account: PublicKey) -> StakeDelegation { certificate::StakeDelegation { account_id: tx::AccountIdentifier::from_single_account(account.0.into()), - pool_id: pool_id.0, + delegation: delegation_type.0, } .into() } @@ -1241,14 +733,6 @@ impl Certificate { pub fn stake_pool_registration(pool_registration: PoolRegistration) -> Certificate { certificate::Certificate::PoolRegistration(pool_registration.0).into() } - - // Prevent the warning on private_key, as I don't want an underscore in the js signature - #[allow(unused_variables)] - pub fn sign(&mut self, private_key: PrivateKey) { - // FIXME: NOP - // This is what the JCLI does, so I'll keep it just in case - () - } } #[wasm_bindgen] @@ -1304,6 +788,7 @@ impl TimeOffsetSeconds { } #[wasm_bindgen] +#[derive(Clone)] pub struct PoolId(chain::certificate::PoolId); impl From for PoolId { @@ -1414,12 +899,10 @@ impl Balance { } /// Algorithm used to compute transaction fees -/// Currently the only implementation if the Linear one +/// Currently the only implementation is the Linear one #[wasm_bindgen] pub struct Fee(FeeVariant); -use fee::FeeAlgorithm; - #[wasm_bindgen] impl Fee { /// Linear algorithm, this is formed by: `coefficient * (#inputs + #outputs) + constant + certificate * #certificate @@ -1431,15 +914,20 @@ impl Fee { ))) } - /// Compute the fee if possible (it can fail in case the values are out of range) - pub fn calculate(&self, tx: Transaction) -> Option { - use EitherTransaction::Certificate; - use EitherTransaction::NoCertificate; - match (&self.0, tx.0) { - (FeeVariant::Linear(algorithm), Certificate(ref tx)) => algorithm.calculate(tx), - (FeeVariant::Linear(algorithm), NoCertificate(ref tx)) => algorithm.calculate(tx), - } - .map(Value) + pub fn calculate(&self, tx: Transaction) -> Value { + let fee_algorithm = match &self.0 { + FeeVariant::Linear(algorithm) => algorithm, + }; + + use fee::FeeAlgorithm; + let v = map_payloads!(tx.0, tx, { + fee_algorithm.calculate( + tx.as_slice().payload().to_certificate_slice(), + tx.nb_inputs(), + tx.nb_outputs(), + ) + }); + Value(v) } } @@ -1548,54 +1036,34 @@ impl From for Fragment { #[wasm_bindgen] impl Fragment { - pub fn from_authenticated_transaction(tx: AuthenticatedTransaction) -> Fragment { - use chain::fragment; + pub fn from_transaction(tx: Transaction) -> Fragment { + use chain::fragment::Fragment as F; + use TaggedTransaction as T; match tx.0 { - AuthenticatedTransactionType::NoCertificate(auth_tx) => { - fragment::Fragment::Transaction(auth_tx) - } - AuthenticatedTransactionType::PoolRegistration(auth_tx) => { - fragment::Fragment::PoolRegistration(auth_tx) - } - AuthenticatedTransactionType::PoolManagement(auth_tx) => { - fragment::Fragment::PoolManagement(auth_tx) - } - AuthenticatedTransactionType::StakeDelegation(auth_tx) => { - fragment::Fragment::StakeDelegation(auth_tx) - } - AuthenticatedTransactionType::OwnerStakeDelegation(auth_tx) => { - fragment::Fragment::OwnerStakeDelegation(auth_tx) - } + T::NoExtra(auth_tx) => F::Transaction(auth_tx), + T::PoolRegistration(auth_tx) => F::PoolRegistration(auth_tx), + T::PoolRetirement(auth_tx) => F::PoolRetirement(auth_tx), + T::PoolUpdate(auth_tx) => F::PoolUpdate(auth_tx), + T::StakeDelegation(auth_tx) => F::StakeDelegation(auth_tx), + T::OwnerStakeDelegation(auth_tx) => F::OwnerStakeDelegation(auth_tx), } .into() } - /// Deprecated: Use `from_authenticated_transaction` instead - pub fn from_generated_transaction(tx: AuthenticatedTransaction) -> Self { - Self::from_authenticated_transaction(tx) - } - /// Get a Transaction if the Fragment represents one - pub fn get_transaction(self) -> Result { + pub fn get_transaction(self) -> Result { + use chain::fragment::Fragment as F; + use TaggedTransaction as T; match self.0 { - chain::fragment::Fragment::Transaction(auth) => { - Ok(AuthenticatedTransactionType::NoCertificate(auth)) - } - chain::fragment::Fragment::OwnerStakeDelegation(auth) => { - Ok(AuthenticatedTransactionType::OwnerStakeDelegation(auth)) - } - chain::fragment::Fragment::StakeDelegation(auth) => { - Ok(AuthenticatedTransactionType::StakeDelegation(auth)) - } - chain::fragment::Fragment::PoolRegistration(auth) => { - Ok(AuthenticatedTransactionType::PoolRegistration(auth)) - } - chain::fragment::Fragment::PoolManagement(auth) => { - Ok(AuthenticatedTransactionType::PoolManagement(auth)) - } + F::Transaction(auth) => Ok(T::NoExtra(auth)), + F::OwnerStakeDelegation(auth) => Ok(T::OwnerStakeDelegation(auth)), + F::StakeDelegation(auth) => Ok(T::StakeDelegation(auth)), + F::PoolRegistration(auth) => Ok(T::PoolRegistration(auth)), + F::PoolRetirement(auth) => Ok(T::PoolRetirement(auth)), + F::PoolUpdate(auth) => Ok(T::PoolUpdate(auth)), _ => Err(JsValue::from_str("Invalid fragment type")), } - .map(AuthenticatedTransaction) + .map(Transaction) } pub fn as_bytes(&self) -> Result, JsValue> { @@ -1639,9 +1107,16 @@ impl Fragment { } } - pub fn is_pool_management(&self) -> bool { + pub fn is_pool_retirement(&self) -> bool { + match self.0 { + chain::fragment::Fragment::PoolRetirement(_) => true, + _ => false, + } + } + + pub fn is_pool_update(&self) -> bool { match self.0 { - chain::fragment::Fragment::PoolManagement(_) => true, + chain::fragment::Fragment::PoolUpdate(_) => true, _ => false, } } diff --git a/src/transaction/iobuilder.rs b/src/transaction/iobuilder.rs new file mode 100644 index 0000000..818d622 --- /dev/null +++ b/src/transaction/iobuilder.rs @@ -0,0 +1,178 @@ +use crate::{ + certificate, tx, Address, Balance, Certificate, Fee, FeeVariant, Input, Inputs, OutputPolicy, + Outputs, Value, +}; +use wasm_bindgen::prelude::*; + +macro_rules! map_payload { + ($payload:expr, $with:ident, $body:expr) => { + match $payload { + TaggedPayload::NoPayload => { + let $with = tx::NoExtra; + $body + } + TaggedPayload::Certificate(cert) => { + use certificate::Certificate as C; + match cert { + C::OwnerStakeDelegation($with) => $body, + C::StakeDelegation($with) => $body, + C::PoolRegistration($with) => $body, + C::PoolUpdate($with) => $body, + C::PoolRetirement($with) => $body, + } + } + } + }; +} + +#[wasm_bindgen] +pub struct Payload(TaggedPayload); + +enum TaggedPayload { + NoPayload, + Certificate(certificate::Certificate), +} + +#[wasm_bindgen] +impl Payload { + pub fn no_payload() -> Self { + Self(TaggedPayload::NoPayload) + } + + pub fn certificate(certificate: &Certificate) -> Self { + Self(TaggedPayload::Certificate(certificate.0.clone())) + } +} + +#[wasm_bindgen] +pub struct InputOutputBuilder(tx::InputOutputBuilder); + +#[wasm_bindgen] +impl InputOutputBuilder { + // TODO: Add constructor attribute + #[wasm_bindgen] + pub fn empty() -> InputOutputBuilder { + InputOutputBuilder(tx::InputOutputBuilder::empty()) + } + + /// Add input to the transaction + #[wasm_bindgen] + pub fn add_input(&mut self, input: &Input) -> Result<(), JsValue> { + self.0 + .add_input(&input.0) + .map_err(|e| JsValue::from_str(&format!("{:?}", e))) + } + + /// Add output to the transaction + #[wasm_bindgen] + pub fn add_output(&mut self, address: Address, value: Value) -> Result<(), JsValue> { + self.0 + .add_output(address.0, value.0) + .map_err(|e| JsValue::from_str(&format! {"{:?}", e})) + } + + /// Estimate fee with the currently added inputs, outputs and certificate based on the given algorithm + #[wasm_bindgen] + pub fn estimate_fee(&self, fee: &Fee, payload: &Payload) -> Value { + let fee_algorithm = match fee.0 { + FeeVariant::Linear(fee_algorithm) => fee_algorithm, + }; + use tx::Payload as _; + map_payload! { + &payload.0, + p, + Value(self.0.estimate_fee(p.payload_data().borrow(), &fee_algorithm)) + } + } + + #[wasm_bindgen] + pub fn get_balance(&self, payload: &Payload, fee: &Fee) -> Result { + let fee_algorithm = match fee.0 { + FeeVariant::Linear(fee_algorithm) => fee_algorithm, + }; + use tx::Payload as _; + let balance = map_payload!( + &payload.0, + payload, + self.0 + .get_balance(payload.payload_data().borrow(), &fee_algorithm) + ); + + balance + .map_err(|e| JsValue::from_str(&format!("{}", e))) + .map(|balance| balance.into()) + } + + #[wasm_bindgen] + pub fn get_balance_without_fee(&self) -> Result { + self.0 + .get_balance_without_fee() + .map(|balance| balance.into()) + .map_err(|e| JsValue::from_str(&format!("{}", e))) + } + + #[wasm_bindgen] + pub fn build(self) -> InputOutput { + InputOutput(self.0.build()) + } + + #[wasm_bindgen] + pub fn seal(self, payload: &Payload, fee_algorithm: Fee) -> Result { + use tx::Payload as _; + let fee_algorithm = match fee_algorithm.0 { + FeeVariant::Linear(algo) => algo, + }; + + map_payload!( + &payload.0, + p, + self.0.seal(p.payload_data().borrow(), &fee_algorithm) + ) + .map_err(|e| JsValue::from_str(&format!("{:?}", e))) + .map(InputOutput) + } + + // Seal the transaction by passing fee rule and the output policy + // + // Along with the transaction, this return the balance unassigned to output policy + // if any + // + // TODO: Add Balance and unassigned inputs, this is, the real signature of + // <(Balance, Vec>, InputOutput) + pub fn seal_with_output_policy( + self, + payload: &Payload, + fee_algorithm: Fee, + policy: OutputPolicy, + ) -> Result { + use tx::Payload as _; + + let fee_algorithm = match fee_algorithm.0 { + FeeVariant::Linear(algo) => algo, + }; + map_payload!( + &payload.0, + p, + self.0 + .seal_with_output_policy(p.payload_data().borrow(), &fee_algorithm, policy.0,) + ) + .map_err(|e| JsValue::from_str(&format!("{:?}", e))) + .map(|(_balance, _unassigned, io)| InputOutput(io)) + } +} + +#[wasm_bindgen] +pub struct InputOutput(tx::InputOutput); + +#[wasm_bindgen] +impl InputOutput { + pub fn inputs(&self) -> Inputs { + let v: Vec<_> = self.0.inputs.iter().map(|i| (*i).clone().into()).collect(); + Inputs(v) + } + + pub fn outputs(&self) -> Outputs { + let v: Vec<_> = self.0.outputs.iter().map(|i| (*i).clone().into()).collect(); + Outputs(v) + } +} diff --git a/src/transaction/mod.rs b/src/transaction/mod.rs new file mode 100644 index 0000000..10b27e6 --- /dev/null +++ b/src/transaction/mod.rs @@ -0,0 +1,93 @@ +mod iobuilder; +mod txbuilder; +use super::certificate; +use super::tx; +use crate::{Input, Inputs, Output, Outputs, TransactionSignDataHash}; +pub use iobuilder::*; +pub use txbuilder::*; +use wasm_bindgen::prelude::*; + +//-----------------------------------// +//-------- Transaction --------------// +//-----------------------------------// + +/// Type representing a unsigned transaction +#[wasm_bindgen] +#[derive(Clone)] +pub struct Transaction(pub(crate) TaggedTransaction); + +#[macro_export] +macro_rules! map_payloads { + ($x:expr, $with:ident, $body:expr) => { + match $x { + $crate::transaction::TaggedTransaction::NoExtra($with) => $body, + $crate::transaction::TaggedTransaction::OwnerStakeDelegation($with) => $body, + $crate::transaction::TaggedTransaction::StakeDelegation($with) => $body, + $crate::transaction::TaggedTransaction::PoolRegistration($with) => $body, + $crate::transaction::TaggedTransaction::PoolUpdate($with) => $body, + $crate::transaction::TaggedTransaction::PoolRetirement($with) => $body, + } + }; +} + +#[derive(Clone)] +pub enum TaggedTransaction { + NoExtra(tx::Transaction), + StakeDelegation(tx::Transaction), + OwnerStakeDelegation(tx::Transaction), + PoolRegistration(tx::Transaction), + PoolRetirement(tx::Transaction), + PoolUpdate(tx::Transaction), +} + +impl TaggedTransaction { + fn id(&self) -> TransactionSignDataHash { + map_payloads!(self, tx, tx.hash().into()) + } + + fn inputs(&self) -> Vec { + map_payloads!(self, tx, tx.as_slice().inputs().iter().collect()) + } + + fn outputs(&self) -> Vec> { + map_payloads!(self, tx, tx.as_slice().outputs().iter().collect()) + } +} + +impl From> for Transaction { + fn from(tx: tx::Transaction) -> Self { + Transaction(TaggedTransaction::NoExtra(tx)) + } +} + +#[wasm_bindgen] +impl Transaction { + /// Get the transaction id, needed to compute its signature + pub fn id(&self) -> TransactionSignDataHash { + self.0.id() + } + + /// Get collection of the inputs in the transaction (this allocates new copies of all the values) + pub fn inputs(&self) -> Inputs { + self.0 + .inputs() + .iter() + .map(|input| Input(input.clone())) + .collect::>() + .into() + } + + /// Get collection of the outputs in the transaction (this allocates new copies of all the values) + pub fn outputs(&self) -> Outputs { + self.0 + .outputs() + .iter() + .map(|output| Output(output.clone())) + .collect::>() + .into() + } + + pub fn clone(&self) -> Transaction { + Transaction(self.0.clone()) + } +} diff --git a/src/transaction/txbuilder.rs b/src/transaction/txbuilder.rs new file mode 100644 index 0000000..5cf2edc --- /dev/null +++ b/src/transaction/txbuilder.rs @@ -0,0 +1,354 @@ +use crate::*; +use wasm_bindgen::prelude::*; + +macro_rules! transition_from_to { + ($from:ident, $to:ident, $builder:expr, $with:ident, $body:expr) => { + match $builder { + $from::NoExtra($with) => $to::NoExtra($body), + $from::OwnerStakeDelegation($with) => $to::OwnerStakeDelegation($body), + $from::StakeDelegation($with) => $to::StakeDelegation($body), + $from::PoolRegistration($with) => $to::PoolRegistration($body), + $from::PoolUpdate($with) => $to::PoolUpdate($body), + $from::PoolRetirement($with) => $to::PoolRetirement($body), + } + }; +} + +macro_rules! for_all_payloads { + ($from:ident, $builder:expr, $with:ident, $body:expr) => { + match $builder { + $from::NoExtra($with) => $body, + $from::OwnerStakeDelegation($with) => $body, + $from::StakeDelegation($with) => $body, + $from::PoolRegistration($with) => $body, + $from::PoolUpdate($with) => $body, + $from::PoolRetirement($with) => $body, + } + }; +} + +//--------TransactionBuilder---------// +//-----------------------------------// + +/// Builder pattern implementation for making a Transaction +/// +/// Example +/// +/// ```javascript +/// ``` +#[wasm_bindgen] +pub struct TransactionBuilder(tx::TxBuilder); + +#[wasm_bindgen] +pub struct TransactionBuilderSetIOs(TaggedTransactionBuilderSetIOs); + +enum TaggedTransactionBuilderSetIOs { + NoExtra(tx::TxBuilderState>), + StakeDelegation(tx::TxBuilderState>), + OwnerStakeDelegation(tx::TxBuilderState>), + PoolRegistration(tx::TxBuilderState>), + PoolRetirement(tx::TxBuilderState>), + PoolUpdate(tx::TxBuilderState>), +} + +#[wasm_bindgen] +pub struct TransactionBuilderSetWitness(TaggedTransactionBuilderSetWitness); + +enum TaggedTransactionBuilderSetWitness { + NoExtra(tx::TxBuilderState>), + StakeDelegation(tx::TxBuilderState>), + OwnerStakeDelegation(tx::TxBuilderState>), + PoolRegistration(tx::TxBuilderState>), + PoolRetirement(tx::TxBuilderState>), + PoolUpdate(tx::TxBuilderState>), +} + +#[wasm_bindgen] +pub struct TransactionBuilderSetAuthData(TaggedTransactionBuilderSetAuthData); + +enum TaggedTransactionBuilderSetAuthData { + NoExtra(tx::TxBuilderState>), + StakeDelegation(tx::TxBuilderState>), + OwnerStakeDelegation(tx::TxBuilderState>), + PoolRegistration(tx::TxBuilderState>), + PoolRetirement(tx::TxBuilderState>), + PoolUpdate(tx::TxBuilderState>), +} + +#[wasm_bindgen] +impl TransactionBuilder { + #[wasm_bindgen(constructor)] + pub fn new() -> Self { + TransactionBuilder(tx::TxBuilder::new()) + } + + pub fn payload(self, cert: &Certificate) -> TransactionBuilderSetIOs { + TransactionBuilderSetIOs(match &cert.0 { + certificate::Certificate::PoolRegistration(p) => { + TaggedTransactionBuilderSetIOs::PoolRegistration(self.0.set_payload(&p)) + } + certificate::Certificate::PoolRetirement(p) => { + TaggedTransactionBuilderSetIOs::PoolRetirement(self.0.set_payload(&p)) + } + certificate::Certificate::PoolUpdate(p) => { + TaggedTransactionBuilderSetIOs::PoolUpdate(self.0.set_payload(&p)) + } + certificate::Certificate::StakeDelegation(p) => { + TaggedTransactionBuilderSetIOs::StakeDelegation(self.0.set_payload(&p)) + } + certificate::Certificate::OwnerStakeDelegation(p) => { + TaggedTransactionBuilderSetIOs::OwnerStakeDelegation(self.0.set_payload(&p)) + } + }) + } + + pub fn no_payload(self) -> TransactionBuilderSetIOs { + TransactionBuilderSetIOs(TaggedTransactionBuilderSetIOs::NoExtra( + self.0.set_nopayload(), + )) + } +} + +#[wasm_bindgen] +impl TransactionBuilderSetIOs { + pub fn set_ios(self, inputs: &Inputs, outputs: &Outputs) -> TransactionBuilderSetWitness { + let inputs: Vec<_> = inputs.0.iter().map(|i| i.0.clone()).collect(); + let outputs: Vec<_> = outputs.0.iter().map(|i| i.0.clone()).collect(); + + let tagged = transition_from_to!( + TaggedTransactionBuilderSetIOs, + TaggedTransactionBuilderSetWitness, + self.0, + builder, + builder.set_ios(&inputs[..], &outputs[..]) + ); + TransactionBuilderSetWitness(tagged) + } +} + +/// Example (for an account as input) +/// +/// ```javascript +/// ``` + +#[wasm_bindgen] +impl TransactionBuilderSetWitness { + pub fn get_auth_data_for_witness(&self) -> TransactionSignDataHash { + let hash = for_all_payloads!( + TaggedTransactionBuilderSetWitness, + &self.0, + builder, + builder.get_auth_data_for_witness().hash() + ); + + TransactionSignDataHash(hash) + } + + pub fn set_witnesses(self, witnesses: &Witnesses) -> TransactionBuilderSetAuthData { + let witnesses: Vec<_> = witnesses.0.iter().map(|w| w.0.clone()).collect(); + + let tagged = transition_from_to!( + TaggedTransactionBuilderSetWitness, + TaggedTransactionBuilderSetAuthData, + self.0, + builder, + builder.set_witnesses(&witnesses[..]) + ); + + TransactionBuilderSetAuthData(tagged) + } +} + +macro_rules! try_map_builder { + ($payload:ident, $builder:expr, $with:ident, $body:expr) => { + match $builder { + TaggedTransactionBuilderSetAuthData::$payload($with) => $body, + _ => return Err(JsValue::from_str("Invalid auth type")), + } + }; +} + +#[wasm_bindgen] +impl TransactionBuilderSetAuthData { + pub fn get_auth_data(&self) -> TransactionBindingAuthData { + for_all_payloads!( + TaggedTransactionBuilderSetAuthData, + &self.0, + builder, + TransactionBindingAuthData(builder.get_auth_data().0.clone().to_owned()) + ) + } + + /// Set the authenticated data + pub fn set_payload_auth(self, auth: PayloadAuthData) -> Result { + use super::TaggedTransaction as T; + use TaggedPayloadAuthData as P; + + let tx = match auth.0 { + P::NoPayload(a) => try_map_builder!( + NoExtra, + self.0, + builder, + T::NoExtra(builder.set_payload_auth(&a)) + ), + P::OwnerStakeDelegation(a) => try_map_builder!( + OwnerStakeDelegation, + self.0, + builder, + T::OwnerStakeDelegation(builder.set_payload_auth(&a)) + ), + P::StakeDelegation(a) => try_map_builder!( + StakeDelegation, + self.0, + builder, + T::StakeDelegation(builder.set_payload_auth(&a)) + ), + P::PoolRegistration(a) => try_map_builder!( + PoolRegistration, + self.0, + builder, + T::PoolRegistration(builder.set_payload_auth(&a)) + ), + P::PoolRetirement(a) => try_map_builder!( + PoolRetirement, + self.0, + builder, + T::PoolRetirement(builder.set_payload_auth(&a)) + ), + P::PoolUpdate(a) => try_map_builder!( + PoolUpdate, + self.0, + builder, + T::PoolUpdate(builder.set_payload_auth(&a)) + ), + }; + Ok(Transaction(tx)) + } +} + +#[wasm_bindgen] +pub struct TransactionBindingAuthData(Vec); + +#[wasm_bindgen] +pub struct PayloadAuthData(TaggedPayloadAuthData); + +pub enum TaggedPayloadAuthData { + NoPayload(::Auth), + OwnerStakeDelegation(::Auth), + StakeDelegation(::Auth), + PoolRegistration(::Auth), + PoolRetirement(::Auth), + PoolUpdate(::Auth), +} + +#[wasm_bindgen] +impl PayloadAuthData { + pub fn for_no_payload() -> PayloadAuthData { + Self(TaggedPayloadAuthData::NoPayload(())) + } + + pub fn for_owner_stake_delegation() -> PayloadAuthData { + Self(TaggedPayloadAuthData::OwnerStakeDelegation(())) + } + + pub fn for_stake_delegation(auth_data: StakeDelegationAuthData) -> PayloadAuthData { + Self(TaggedPayloadAuthData::StakeDelegation(auth_data.0)) + } + + pub fn for_pool_registration(auth_data: PoolRegistrationAuthData) -> PayloadAuthData { + Self(TaggedPayloadAuthData::PoolRegistration(auth_data.0)) + } + + pub fn for_pool_retirement(auth_data: PoolRetirementAuthData) -> PayloadAuthData { + Self(TaggedPayloadAuthData::PoolRegistration(auth_data.0)) + } + + pub fn for_pool_update(auth_data: PoolUpdateAuthData) -> PayloadAuthData { + Self(TaggedPayloadAuthData::PoolUpdate(auth_data.0)) + } +} + +#[wasm_bindgen] +pub struct StakeDelegationAuthData(::Auth); + +#[wasm_bindgen] +impl StakeDelegationAuthData { + pub fn new(signature: AccountBindingSignature) -> StakeDelegationAuthData { + StakeDelegationAuthData(signature.0) + } +} + +#[wasm_bindgen] +#[derive(Clone)] +pub struct IndexedSignature { + index: u16, + signature: AccountBindingSignature, +} + +crate::impl_collection!(IndexSignatures, IndexedSignature); + +#[wasm_bindgen] +impl IndexedSignature { + pub fn new(index: u16, signature: AccountBindingSignature) -> IndexedSignature { + Self { index, signature } + } +} + +#[wasm_bindgen] +#[derive(Clone)] +pub struct AccountBindingSignature(tx::AccountBindingSignature); + +#[wasm_bindgen] +impl AccountBindingSignature { + pub fn new(private_key: &PrivateKey, auth_data: &TransactionBindingAuthData) -> Self { + Self(tx::AccountBindingSignature::new( + &private_key.0, + &tx::TransactionBindingAuthData(auth_data.0.as_slice()), + )) + } +} + +#[wasm_bindgen] +pub struct PoolRegistrationAuthData(::Auth); + +#[wasm_bindgen] +impl PoolRegistrationAuthData { + pub fn new(signatures: IndexSignatures) -> Self { + let signatures = signatures + .0 + .iter() + .map(|IndexedSignature { index, signature }| (*index, signature.0.clone())) + .collect(); + Self(certificate::PoolOwnersSigned { signatures }) + } +} + +#[wasm_bindgen] +pub struct PoolRetirementAuthData(::Auth); + +#[wasm_bindgen] +impl PoolRetirementAuthData { + pub fn new(signatures: IndexSignatures) -> Self { + let signatures = signatures + .0 + .iter() + .map(|IndexedSignature { index, signature }| (*index, signature.0.clone())) + .collect(); + Self(certificate::PoolOwnersSigned { signatures }) + } +} + +#[wasm_bindgen] +pub struct PoolUpdateAuthData(::Auth); + +#[wasm_bindgen] +impl PoolUpdateAuthData { + pub fn new(signatures: IndexSignatures) -> Self { + let signatures = signatures + .0 + .iter() + .map(|IndexedSignature { index, signature }| (*index, signature.0.clone())) + .collect(); + Self(certificate::PoolOwnersSigned { signatures }) + } +} diff --git a/src/utils.rs b/src/utils.rs index b1d7929..b709059 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -8,3 +8,36 @@ pub fn set_panic_hook() { #[cfg(feature = "console_error_panic_hook")] console_error_panic_hook::set_once(); } + +#[macro_export] +macro_rules! impl_collection { + ($collection:ident, $type:ty) => { + #[wasm_bindgen] + pub struct $collection(Vec<$type>); + + #[wasm_bindgen] + impl $collection { + pub fn new() -> $collection { + Self(vec![]) + } + + pub fn size(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> $type { + self.0[index].clone() + } + + pub fn add(&mut self, item: $type) { + self.0.push(item); + } + } + + impl From> for $collection { + fn from(vec: Vec<$type>) -> $collection { + $collection(vec) + } + } + }; +} From 6a957093e2b0f0f7cf9248edbdf2167f08dc74d8 Mon Sep 17 00:00:00 2001 From: Enzo Cioppettini Date: Fri, 8 Nov 2019 15:00:43 -0300 Subject: [PATCH 4/4] rename and remove redundant macro --- src/transaction/iobuilder.rs | 50 +++++++--------- src/transaction/txbuilder.rs | 113 +++++++++++++++-------------------- 2 files changed, 71 insertions(+), 92 deletions(-) diff --git a/src/transaction/iobuilder.rs b/src/transaction/iobuilder.rs index 818d622..49ecffa 100644 --- a/src/transaction/iobuilder.rs +++ b/src/transaction/iobuilder.rs @@ -4,8 +4,11 @@ use crate::{ }; use wasm_bindgen::prelude::*; +// Map payloads regardless of which type in a static way. +// This is needed because Payload can't be made into a Trait object, so +// it is not possible to use an FnOnce that takes any Payload macro_rules! map_payload { - ($payload:expr, $with:ident, $body:expr) => { + ($payload:expr, |$with:ident| $body:expr) => { match $payload { TaggedPayload::NoPayload => { let $with = tx::NoExtra; @@ -55,7 +58,7 @@ impl InputOutputBuilder { InputOutputBuilder(tx::InputOutputBuilder::empty()) } - /// Add input to the transaction + /// Add input to the IO Builder #[wasm_bindgen] pub fn add_input(&mut self, input: &Input) -> Result<(), JsValue> { self.0 @@ -63,7 +66,7 @@ impl InputOutputBuilder { .map_err(|e| JsValue::from_str(&format!("{:?}", e))) } - /// Add output to the transaction + /// Add output to the IO Builder #[wasm_bindgen] pub fn add_output(&mut self, address: Address, value: Value) -> Result<(), JsValue> { self.0 @@ -78,11 +81,10 @@ impl InputOutputBuilder { FeeVariant::Linear(fee_algorithm) => fee_algorithm, }; use tx::Payload as _; - map_payload! { - &payload.0, - p, - Value(self.0.estimate_fee(p.payload_data().borrow(), &fee_algorithm)) - } + map_payload!(&payload.0, |payload| Value( + self.0 + .estimate_fee(payload.payload_data().borrow(), &fee_algorithm) + )) } #[wasm_bindgen] @@ -91,12 +93,9 @@ impl InputOutputBuilder { FeeVariant::Linear(fee_algorithm) => fee_algorithm, }; use tx::Payload as _; - let balance = map_payload!( - &payload.0, - payload, - self.0 - .get_balance(payload.payload_data().borrow(), &fee_algorithm) - ); + let balance = map_payload!(&payload.0, |payload| self + .0 + .get_balance(payload.payload_data().borrow(), &fee_algorithm)); balance .map_err(|e| JsValue::from_str(&format!("{}", e))) @@ -115,7 +114,7 @@ impl InputOutputBuilder { pub fn build(self) -> InputOutput { InputOutput(self.0.build()) } - + /// Seal the transaction by passing fee rule #[wasm_bindgen] pub fn seal(self, payload: &Payload, fee_algorithm: Fee) -> Result { use tx::Payload as _; @@ -123,16 +122,14 @@ impl InputOutputBuilder { FeeVariant::Linear(algo) => algo, }; - map_payload!( - &payload.0, - p, - self.0.seal(p.payload_data().borrow(), &fee_algorithm) - ) + map_payload!(&payload.0, |payload| self + .0 + .seal(payload.payload_data().borrow(), &fee_algorithm)) .map_err(|e| JsValue::from_str(&format!("{:?}", e))) .map(InputOutput) } - // Seal the transaction by passing fee rule and the output policy + /// Seal the transaction by passing fee rule and the output policy // // Along with the transaction, this return the balance unassigned to output policy // if any @@ -150,12 +147,11 @@ impl InputOutputBuilder { let fee_algorithm = match fee_algorithm.0 { FeeVariant::Linear(algo) => algo, }; - map_payload!( - &payload.0, - p, - self.0 - .seal_with_output_policy(p.payload_data().borrow(), &fee_algorithm, policy.0,) - ) + map_payload!(&payload.0, |payload| self.0.seal_with_output_policy( + payload.payload_data().borrow(), + &fee_algorithm, + policy.0, + )) .map_err(|e| JsValue::from_str(&format!("{:?}", e))) .map(|(_balance, _unassigned, io)| InputOutput(io)) } diff --git a/src/transaction/txbuilder.rs b/src/transaction/txbuilder.rs index 5cf2edc..fe0fe24 100644 --- a/src/transaction/txbuilder.rs +++ b/src/transaction/txbuilder.rs @@ -2,7 +2,7 @@ use crate::*; use wasm_bindgen::prelude::*; macro_rules! transition_from_to { - ($from:ident, $to:ident, $builder:expr, $with:ident, $body:expr) => { + ($builder: expr, $from:ident -> $to:ident, |$with:ident| $body:expr) => { match $builder { $from::NoExtra($with) => $to::NoExtra($body), $from::OwnerStakeDelegation($with) => $to::OwnerStakeDelegation($body), @@ -15,7 +15,7 @@ macro_rules! transition_from_to { } macro_rules! for_all_payloads { - ($from:ident, $builder:expr, $with:ident, $body:expr) => { + ($from:ident, $builder:expr, |$with:ident| $body:expr) => { match $builder { $from::NoExtra($with) => $body, $from::OwnerStakeDelegation($with) => $body, @@ -116,11 +116,10 @@ impl TransactionBuilderSetIOs { let outputs: Vec<_> = outputs.0.iter().map(|i| i.0.clone()).collect(); let tagged = transition_from_to!( - TaggedTransactionBuilderSetIOs, - TaggedTransactionBuilderSetWitness, self.0, - builder, - builder.set_ios(&inputs[..], &outputs[..]) + TaggedTransactionBuilderSetIOs -> + TaggedTransactionBuilderSetWitness, + |builder| builder.set_ios(&inputs[..], &outputs[..]) ); TransactionBuilderSetWitness(tagged) } @@ -134,12 +133,9 @@ impl TransactionBuilderSetIOs { #[wasm_bindgen] impl TransactionBuilderSetWitness { pub fn get_auth_data_for_witness(&self) -> TransactionSignDataHash { - let hash = for_all_payloads!( - TaggedTransactionBuilderSetWitness, - &self.0, - builder, + let hash = for_all_payloads!(TaggedTransactionBuilderSetWitness, &self.0, |builder| { builder.get_auth_data_for_witness().hash() - ); + }); TransactionSignDataHash(hash) } @@ -148,35 +144,22 @@ impl TransactionBuilderSetWitness { let witnesses: Vec<_> = witnesses.0.iter().map(|w| w.0.clone()).collect(); let tagged = transition_from_to!( - TaggedTransactionBuilderSetWitness, - TaggedTransactionBuilderSetAuthData, self.0, - builder, - builder.set_witnesses(&witnesses[..]) + TaggedTransactionBuilderSetWitness -> + TaggedTransactionBuilderSetAuthData, + |builder| builder.set_witnesses(&witnesses[..]) ); TransactionBuilderSetAuthData(tagged) } } -macro_rules! try_map_builder { - ($payload:ident, $builder:expr, $with:ident, $body:expr) => { - match $builder { - TaggedTransactionBuilderSetAuthData::$payload($with) => $body, - _ => return Err(JsValue::from_str("Invalid auth type")), - } - }; -} - #[wasm_bindgen] impl TransactionBuilderSetAuthData { pub fn get_auth_data(&self) -> TransactionBindingAuthData { - for_all_payloads!( - TaggedTransactionBuilderSetAuthData, - &self.0, - builder, + for_all_payloads!(TaggedTransactionBuilderSetAuthData, &self.0, |builder| { TransactionBindingAuthData(builder.get_auth_data().0.clone().to_owned()) - ) + }) } /// Set the authenticated data @@ -185,42 +168,42 @@ impl TransactionBuilderSetAuthData { use TaggedPayloadAuthData as P; let tx = match auth.0 { - P::NoPayload(a) => try_map_builder!( - NoExtra, - self.0, - builder, - T::NoExtra(builder.set_payload_auth(&a)) - ), - P::OwnerStakeDelegation(a) => try_map_builder!( - OwnerStakeDelegation, - self.0, - builder, - T::OwnerStakeDelegation(builder.set_payload_auth(&a)) - ), - P::StakeDelegation(a) => try_map_builder!( - StakeDelegation, - self.0, - builder, - T::StakeDelegation(builder.set_payload_auth(&a)) - ), - P::PoolRegistration(a) => try_map_builder!( - PoolRegistration, - self.0, - builder, - T::PoolRegistration(builder.set_payload_auth(&a)) - ), - P::PoolRetirement(a) => try_map_builder!( - PoolRetirement, - self.0, - builder, - T::PoolRetirement(builder.set_payload_auth(&a)) - ), - P::PoolUpdate(a) => try_map_builder!( - PoolUpdate, - self.0, - builder, - T::PoolUpdate(builder.set_payload_auth(&a)) - ), + P::NoPayload(a) => match self.0 { + TaggedTransactionBuilderSetAuthData::NoExtra(builder) => { + T::NoExtra(builder.set_payload_auth(&a)) + } + _ => return Err(JsValue::from_str(&"Invalid auth type".to_owned())), + }, + P::StakeDelegation(a) => match self.0 { + TaggedTransactionBuilderSetAuthData::StakeDelegation(builder) => { + T::StakeDelegation(builder.set_payload_auth(&a)) + } + _ => return Err(JsValue::from_str(&"Invalid auth type".to_owned())), + }, + P::OwnerStakeDelegation(a) => match self.0 { + TaggedTransactionBuilderSetAuthData::OwnerStakeDelegation(builder) => { + T::OwnerStakeDelegation(builder.set_payload_auth(&a)) + } + _ => return Err(JsValue::from_str(&"Invalid auth type".to_owned())), + }, + P::PoolRegistration(a) => match self.0 { + TaggedTransactionBuilderSetAuthData::PoolRegistration(builder) => { + T::PoolRegistration(builder.set_payload_auth(&a)) + } + _ => return Err(JsValue::from_str(&"Invalid auth type".to_owned())), + }, + P::PoolRetirement(a) => match self.0 { + TaggedTransactionBuilderSetAuthData::PoolRetirement(builder) => { + T::PoolRetirement(builder.set_payload_auth(&a)) + } + _ => return Err(JsValue::from_str(&"Invalid auth type".to_owned())), + }, + P::PoolUpdate(a) => match self.0 { + TaggedTransactionBuilderSetAuthData::PoolUpdate(builder) => { + T::PoolUpdate(builder.set_payload_auth(&a)) + } + _ => return Err(JsValue::from_str(&"Invalid auth type".to_owned())), + }, }; Ok(Transaction(tx)) }