Zebra Raw Tx deserialization #44
8 changed files with 253 additions and 25 deletions
|
@ -31,9 +31,17 @@ use zcash_primitives::{
|
||||||
sapling::DiversifierKey
|
sapling::DiversifierKey
|
||||||
},
|
},
|
||||||
zip339::{Count, Mnemonic},
|
zip339::{Count, Mnemonic},
|
||||||
transaction::components::sapling::{
|
transaction::components::{
|
||||||
|
transparent::{
|
||||||
|
Bundle as TransparentBundle,
|
||||||
|
TxIn,
|
||||||
|
TxOut,
|
||||||
|
Authorized
|
||||||
|
},
|
||||||
|
sapling::{
|
||||||
GrothProofBytes,
|
GrothProofBytes,
|
||||||
OutputDescription
|
OutputDescription
|
||||||
|
}
|
||||||
},
|
},
|
||||||
sapling::{
|
sapling::{
|
||||||
PaymentAddress,
|
PaymentAddress,
|
||||||
|
@ -220,6 +228,81 @@ impl Hua {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct Htx {
|
||||||
|
txid: Vec<u8>,
|
||||||
|
locktime: u32,
|
||||||
|
expiry: u32,
|
||||||
|
t_bundle: HTBundle
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<RW> ToHaskell<RW> for Htx {
|
||||||
|
fn to_haskell<W: Write>(&self, writer: &mut W, _tag: PhantomData<RW>) -> Result<()> {
|
||||||
|
self.serialize(writer)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct HTBundle {
|
||||||
|
vin: Vec<HTxIn>,
|
||||||
|
vout: Vec<HTxOut>,
|
||||||
|
coinbase: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<RW> ToHaskell<RW> for HTBundle {
|
||||||
|
fn to_haskell<W: Write>(&self, writer: &mut W, _tag: PhantomData<RW>) -> Result<()> {
|
||||||
|
self.serialize(writer)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HTBundle {
|
||||||
|
pub fn from_bundle(b: &TransparentBundle<Authorized>) -> HTBundle {
|
||||||
|
HTBundle { vin: b.vin.iter().map(HTxIn::pack).collect() , vout: b.vout.iter().map(HTxOut::pack).collect(), coinbase: b.is_coinbase()}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct HTxIn {
|
||||||
|
outpoint: u32,
|
||||||
|
script: Vec<u8>,
|
||||||
|
sequence: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<RW> ToHaskell<RW> for HTxIn {
|
||||||
|
fn to_haskell<W: Write>(&self, writer: &mut W, _tag: PhantomData<RW>) -> Result<()> {
|
||||||
|
self.serialize(writer)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HTxIn {
|
||||||
|
pub fn pack(t: &TxIn<Authorized>) -> HTxIn {
|
||||||
|
return HTxIn { outpoint: t.prevout.n(), script: t.script_sig.0.clone(), sequence: t.sequence}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(BorshSerialize, BorshDeserialize)]
|
||||||
|
pub struct HTxOut {
|
||||||
|
amt: i64,
|
||||||
|
script: Vec<u8>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<RW> ToHaskell<RW> for HTxOut {
|
||||||
|
fn to_haskell<W: Write>(&self, writer: &mut W, _tag: PhantomData<RW>) -> Result<()> {
|
||||||
|
self.serialize(writer)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HTxOut {
|
||||||
|
pub fn pack(t: &TxOut) -> HTxOut {
|
||||||
|
return HTxOut { amt: i64::from_le_bytes(t.value.to_i64_le_bytes()) , script: t.script_pubkey.0.clone() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(BorshSerialize, BorshDeserialize)]
|
#[derive(BorshSerialize, BorshDeserialize)]
|
||||||
pub struct Hufvk {
|
pub struct Hufvk {
|
||||||
net: u8,
|
net: u8,
|
||||||
|
@ -555,6 +638,29 @@ pub extern "C" fn rust_wrapper_orchard_note_decrypt(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn rust_wrapper_tx_read(
|
||||||
|
tx: *const u8,
|
||||||
|
tx_len: usize,
|
||||||
|
out: *mut u8,
|
||||||
|
out_len: &mut usize
|
||||||
|
){
|
||||||
|
let tx_input: Vec<u8> = marshall_from_haskell_var(tx, tx_len, RW);
|
||||||
|
let mut tx_reader = Cursor::new(tx_input);
|
||||||
|
let parsed_tx = Transaction::read(&mut tx_reader, Nu5);
|
||||||
|
match parsed_tx {
|
||||||
|
Ok(t) => {
|
||||||
|
let h = Htx {txid: t.txid().as_ref().to_vec(), locktime: t.lock_time(), expiry: u32::from(t.expiry_height()), t_bundle: HTBundle::from_bundle(t.transparent_bundle().unwrap()) };
|
||||||
|
marshall_to_haskell_var(&h, out, out_len, RW);
|
||||||
|
},
|
||||||
|
Err(_e) => {
|
||||||
|
let h0 = Htx {txid: vec![0], locktime: 0, expiry: 0, t_bundle: HTBundle {vin: vec![HTxIn {outpoint: 0, script: vec![0], sequence: 0}], vout: vec![HTxOut {amt: 0, script: vec![0]}], coinbase: true} };
|
||||||
|
marshall_to_haskell_var(&h0, out, out_len, RW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn rust_wrapper_tx_parse(
|
pub extern "C" fn rust_wrapper_tx_parse(
|
||||||
tx: *const u8,
|
tx: *const u8,
|
||||||
|
|
|
@ -123,6 +123,13 @@ import ZcashHaskell.Types
|
||||||
-> `()'
|
-> `()'
|
||||||
#}
|
#}
|
||||||
|
|
||||||
|
{# fun unsafe rust_wrapper_tx_read as rustWrapperTxRead
|
||||||
|
{ toBorshVar* `BS.ByteString'&
|
||||||
|
, getVarBuffer `Buffer RawZebraTx'&
|
||||||
|
}
|
||||||
|
-> `()'
|
||||||
|
#}
|
||||||
|
|
||||||
{# fun unsafe rust_wrapper_gen_seed_phrase as rustWrapperGenSeedPhrase
|
{# fun unsafe rust_wrapper_gen_seed_phrase as rustWrapperGenSeedPhrase
|
||||||
{ getVarBuffer `Buffer Phrase'& } -> `()'
|
{ getVarBuffer `Buffer Phrase'& } -> `()'
|
||||||
#}
|
#}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
{-# LANGUAGE DeriveGeneric #-}
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
{-# LANGUAGE DuplicateRecordFields #-}
|
{-# LANGUAGE DuplicateRecordFields #-}
|
||||||
{-# LANGUAGE GeneralisedNewtypeDeriving #-}
|
{-# LANGUAGE GeneralisedNewtypeDeriving #-}
|
||||||
|
{-# LANGUAGE StandaloneDeriving #-}
|
||||||
{-# LANGUAGE DeriveAnyClass #-}
|
{-# LANGUAGE DeriveAnyClass #-}
|
||||||
{-# LANGUAGE DerivingVia #-}
|
{-# LANGUAGE DerivingVia #-}
|
||||||
{-# LANGUAGE UndecidableInstances #-}
|
{-# LANGUAGE UndecidableInstances #-}
|
||||||
|
@ -40,6 +41,7 @@ import qualified GHC.Generics as GHC
|
||||||
import qualified Generics.SOP as SOP
|
import qualified Generics.SOP as SOP
|
||||||
import Haskoin.Address (Address)
|
import Haskoin.Address (Address)
|
||||||
import Haskoin.Crypto.Keys.Extended (XPrvKey)
|
import Haskoin.Crypto.Keys.Extended (XPrvKey)
|
||||||
|
import qualified Haskoin.Transaction.Common as H
|
||||||
|
|
||||||
-- * General
|
-- * General
|
||||||
--
|
--
|
||||||
|
@ -89,6 +91,19 @@ data ZcashNet
|
||||||
|
|
||||||
type AccountId = Int
|
type AccountId = Int
|
||||||
|
|
||||||
|
-- | Function to get the Base58 prefix for encoding a 'TransparentAddress'
|
||||||
|
getTransparentPrefix :: ZcashNet -> TransparentType -> (Word8, Word8)
|
||||||
|
getTransparentPrefix n t =
|
||||||
|
case t of
|
||||||
|
P2SH ->
|
||||||
|
case n of
|
||||||
|
MainNet -> (0x1c, 0xbd)
|
||||||
|
_ -> (0x1c, 0xba)
|
||||||
|
P2PKH ->
|
||||||
|
case n of
|
||||||
|
MainNet -> (0x1c, 0xb8)
|
||||||
|
_ -> (0x1d, 0x25)
|
||||||
|
|
||||||
-- ** Constants
|
-- ** Constants
|
||||||
-- | Type for coin types on the different networks
|
-- | Type for coin types on the different networks
|
||||||
data CoinType
|
data CoinType
|
||||||
|
@ -104,7 +119,23 @@ getValue c =
|
||||||
TestNetCoin -> 1
|
TestNetCoin -> 1
|
||||||
RegTestNetCoin -> 1
|
RegTestNetCoin -> 1
|
||||||
|
|
||||||
-- | Constants for Sapling Human-readable part
|
-- | A Zcash transaction
|
||||||
|
data Transaction = Transaction
|
||||||
|
{ tx_id :: !HexString
|
||||||
|
, tx_height :: !Int
|
||||||
|
, tx_conf :: !Int
|
||||||
|
, tx_expiry :: !Int
|
||||||
|
, tx_transpBundle :: !(Maybe TransparentBundle)
|
||||||
|
} deriving (Prelude.Show, Eq, Read)
|
||||||
|
|
||||||
|
-- | The transparent portion of a Zcash transaction
|
||||||
|
data TransparentBundle = TransparentBundle
|
||||||
|
{ tb_vin :: ![H.TxIn]
|
||||||
|
, tb_vout :: ![H.TxOut]
|
||||||
|
, tb_coinbase :: !Bool
|
||||||
|
} deriving (Eq, Prelude.Show, Read)
|
||||||
|
|
||||||
|
-- *** Constants for Sapling Human-readable part
|
||||||
sapExtSpendingKeyHrp = "secret-extended-key-main" :: String
|
sapExtSpendingKeyHrp = "secret-extended-key-main" :: String
|
||||||
|
|
||||||
sapExtFullViewingKeyHrp = "zxviews" :: String
|
sapExtFullViewingKeyHrp = "zxviews" :: String
|
||||||
|
@ -117,7 +148,7 @@ sapTestExtFullViewingKeyHrp = "zxviewtestsapling" :: String
|
||||||
|
|
||||||
sapTestPaymentAddressHrp = "ztestsapling" :: String
|
sapTestPaymentAddressHrp = "ztestsapling" :: String
|
||||||
|
|
||||||
-- | Constants for Unified Human-readable part
|
-- *** Constants for Unified Human-readable part
|
||||||
uniPaymentAddressHrp = "u" :: T.Text
|
uniPaymentAddressHrp = "u" :: T.Text
|
||||||
|
|
||||||
uniFullViewingKeyHrp = "uview" :: T.Text
|
uniFullViewingKeyHrp = "uview" :: T.Text
|
||||||
|
@ -130,19 +161,6 @@ uniTestFullViewingKeyHrp = "uviewtest" :: T.Text
|
||||||
|
|
||||||
uniTestIncomingViewingKeyHrp = "uivktest" :: T.Text
|
uniTestIncomingViewingKeyHrp = "uivktest" :: T.Text
|
||||||
|
|
||||||
-- | Function to get the Base58 prefix for encoding a 'TransparentAddress'
|
|
||||||
getTransparentPrefix :: ZcashNet -> TransparentType -> (Word8, Word8)
|
|
||||||
getTransparentPrefix n t =
|
|
||||||
case t of
|
|
||||||
P2SH ->
|
|
||||||
case n of
|
|
||||||
MainNet -> (0x1c, 0xbd)
|
|
||||||
_ -> (0x1c, 0xba)
|
|
||||||
P2PKH ->
|
|
||||||
case n of
|
|
||||||
MainNet -> (0x1c, 0xb8)
|
|
||||||
_ -> (0x1d, 0x25)
|
|
||||||
|
|
||||||
-- * RPC
|
-- * RPC
|
||||||
-- | A type to model Zcash RPC calls
|
-- | A type to model Zcash RPC calls
|
||||||
data RpcCall = RpcCall
|
data RpcCall = RpcCall
|
||||||
|
@ -217,6 +235,41 @@ data RawTxResponse = RawTxResponse
|
||||||
} deriving (Prelude.Show, Eq, Read)
|
} deriving (Prelude.Show, Eq, Read)
|
||||||
|
|
||||||
-- ** `zebrad`
|
-- ** `zebrad`
|
||||||
|
data ZebraTxResponse = ZebraTxResponse
|
||||||
|
{ ztr_blockheight :: !Int
|
||||||
|
, ztr_conf :: !Int
|
||||||
|
, ztr_hex :: !HexString
|
||||||
|
} deriving (Prelude.Show, Eq, Read)
|
||||||
|
|
||||||
|
instance FromJSON ZebraTxResponse where
|
||||||
|
parseJSON =
|
||||||
|
withObject "ZebraTxResponse" $ \obj -> do
|
||||||
|
hex <- obj .: "hex"
|
||||||
|
height <- obj .: "height"
|
||||||
|
c <- obj .: "confirmations"
|
||||||
|
pure $ ZebraTxResponse height c hex
|
||||||
|
|
||||||
|
-- | Type to represent a raw deserialized Zebra transaction
|
||||||
|
data RawZebraTx = RawZebraTx
|
||||||
|
{ zt_id :: !HexString
|
||||||
|
, zt_locktime :: !Word32
|
||||||
|
, zt_expiry :: !Word32
|
||||||
|
, zt_tBundle :: !RawTBundle
|
||||||
|
} deriving stock (Eq, Prelude.Show, GHC.Generic)
|
||||||
|
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
|
||||||
|
deriving anyclass (Data.Structured.Show)
|
||||||
|
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawZebraTx
|
||||||
|
|
||||||
|
-- | Type for a raw deserialized Zebra transparent bundle
|
||||||
|
data RawTBundle = RawTBundle
|
||||||
|
{ ztb_vin :: ![RawTxIn]
|
||||||
|
, ztb_vout :: ![RawTxOut]
|
||||||
|
, ztb_coinbase :: !Bool
|
||||||
|
} deriving stock (Eq, Prelude.Show, GHC.Generic)
|
||||||
|
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
|
||||||
|
deriving anyclass (Data.Structured.Show)
|
||||||
|
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawTBundle
|
||||||
|
|
||||||
-- | Type for the response from the `zebrad` RPC method `getinfo`
|
-- | Type for the response from the `zebrad` RPC method `getinfo`
|
||||||
data ZebraGetInfo = ZebraGetInfo
|
data ZebraGetInfo = ZebraGetInfo
|
||||||
{ zgi_build :: !T.Text
|
{ zgi_build :: !T.Text
|
||||||
|
@ -268,6 +321,24 @@ data TransparentAddress = TransparentAddress
|
||||||
, ta_bytes :: !HexString
|
, ta_bytes :: !HexString
|
||||||
} deriving (Eq, Prelude.Show, Read)
|
} deriving (Eq, Prelude.Show, Read)
|
||||||
|
|
||||||
|
-- | Wrapper types for transparent elements
|
||||||
|
data RawTxIn = RawTxIn
|
||||||
|
{ rti_outpoint :: !Word32
|
||||||
|
, rti_script :: !BS.ByteString
|
||||||
|
, rti_seq :: !Word32
|
||||||
|
} deriving stock (Eq, Prelude.Show, GHC.Generic)
|
||||||
|
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
|
||||||
|
deriving anyclass (Data.Structured.Show)
|
||||||
|
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawTxIn
|
||||||
|
|
||||||
|
data RawTxOut = RawTxOut
|
||||||
|
{ rto_amt :: !Word64
|
||||||
|
, rto_script :: !BS.ByteString
|
||||||
|
} deriving stock (Eq, Prelude.Show, GHC.Generic)
|
||||||
|
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
|
||||||
|
deriving anyclass (Data.Structured.Show)
|
||||||
|
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct RawTxOut
|
||||||
|
|
||||||
-- * Sapling
|
-- * Sapling
|
||||||
-- | A spending key for Sapling
|
-- | A spending key for Sapling
|
||||||
newtype SaplingSpendingKey =
|
newtype SaplingSpendingKey =
|
||||||
|
|
|
@ -22,11 +22,13 @@ import C.Zcash
|
||||||
, rustWrapperBech32Encode
|
, rustWrapperBech32Encode
|
||||||
, rustWrapperF4Jumble
|
, rustWrapperF4Jumble
|
||||||
, rustWrapperF4UnJumble
|
, rustWrapperF4UnJumble
|
||||||
|
, rustWrapperTxRead
|
||||||
)
|
)
|
||||||
import Control.Exception (SomeException(..), try)
|
import Control.Exception (SomeException(..), try)
|
||||||
import Control.Monad.IO.Class
|
import Control.Monad.IO.Class
|
||||||
import Data.Aeson
|
import Data.Aeson
|
||||||
import qualified Data.ByteString as BS
|
import qualified Data.ByteString as BS
|
||||||
|
import Data.HexString (HexString(..))
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Data.Text.Encoding as E
|
import qualified Data.Text.Encoding as E
|
||||||
import Foreign.Rust.Marshall.Variable
|
import Foreign.Rust.Marshall.Variable
|
||||||
|
@ -34,13 +36,7 @@ import Network.HTTP.Client (HttpException(..))
|
||||||
import Network.HTTP.Simple
|
import Network.HTTP.Simple
|
||||||
import ZcashHaskell.Types
|
import ZcashHaskell.Types
|
||||||
|
|
||||||
import Foreign.C.Types
|
-- * Utility functions
|
||||||
import Foreign.Marshal.Array (allocaArray, peekArray)
|
|
||||||
import Foreign.Ptr (Ptr)
|
|
||||||
|
|
||||||
import Data.Word
|
|
||||||
|
|
||||||
-- |
|
|
||||||
-- | Decode the given bytestring using Bech32
|
-- | Decode the given bytestring using Bech32
|
||||||
decodeBech32 :: BS.ByteString -> RawData
|
decodeBech32 :: BS.ByteString -> RawData
|
||||||
decodeBech32 = withPureBorshVarBuffer . rustWrapperBech32Decode
|
decodeBech32 = withPureBorshVarBuffer . rustWrapperBech32Decode
|
||||||
|
@ -57,6 +53,7 @@ f4Jumble = withPureBorshVarBuffer . rustWrapperF4Jumble
|
||||||
f4UnJumble :: BS.ByteString -> BS.ByteString
|
f4UnJumble :: BS.ByteString -> BS.ByteString
|
||||||
f4UnJumble = withPureBorshVarBuffer . rustWrapperF4UnJumble
|
f4UnJumble = withPureBorshVarBuffer . rustWrapperF4UnJumble
|
||||||
|
|
||||||
|
-- * Node interaction
|
||||||
-- | Make a Zcash RPC call
|
-- | Make a Zcash RPC call
|
||||||
makeZcashCall ::
|
makeZcashCall ::
|
||||||
(MonadIO m, FromJSON a)
|
(MonadIO m, FromJSON a)
|
||||||
|
@ -102,3 +99,11 @@ makeZebraCall host port m params = do
|
||||||
case result zebraResp of
|
case result zebraResp of
|
||||||
Nothing -> return $ Left "Empty response from Zebra"
|
Nothing -> return $ Left "Empty response from Zebra"
|
||||||
Just zR -> return $ Right zR
|
Just zR -> return $ Right zR
|
||||||
|
|
||||||
|
readZebraTransaction :: HexString -> Maybe RawZebraTx
|
||||||
|
readZebraTransaction hex =
|
||||||
|
if BS.length (hexBytes $ zt_id rawTx) < 1
|
||||||
|
then Nothing
|
||||||
|
else Just rawTx
|
||||||
|
where
|
||||||
|
rawTx = (withPureBorshVarBuffer . rustWrapperTxRead) $ hexBytes hex
|
||||||
|
|
|
@ -71,6 +71,7 @@ import ZcashHaskell.Types
|
||||||
, UnifiedAddress(..)
|
, UnifiedAddress(..)
|
||||||
, UnifiedFullViewingKey(..)
|
, UnifiedFullViewingKey(..)
|
||||||
, ZcashNet(..)
|
, ZcashNet(..)
|
||||||
|
, ZebraTxResponse(..)
|
||||||
, decodeHexText
|
, decodeHexText
|
||||||
, getValue
|
, getValue
|
||||||
)
|
)
|
||||||
|
@ -765,6 +766,12 @@ main = do
|
||||||
let bscAdr = SaplingReceiver $ BS.pack cAdr
|
let bscAdr = SaplingReceiver $ BS.pack cAdr
|
||||||
let ca = genSaplingInternalAddress (SaplingSpendingKey $ BS.pack sk)
|
let ca = genSaplingInternalAddress (SaplingSpendingKey $ BS.pack sk)
|
||||||
fromMaybe (SaplingReceiver "") ca `shouldBe` bscAdr
|
fromMaybe (SaplingReceiver "") ca `shouldBe` bscAdr
|
||||||
|
describe "Zebra response processing: " $ do
|
||||||
|
it "Raw transaction" $ do
|
||||||
|
let h =
|
||||||
|
hexString
|
||||||
|
"0400008085202f8900010829d200000000001976a91484ae5002305847e7176362d7c12c19c5bdbbaf8088ac0000000023392a00f02cd200000000000192331caef004cc758fb666bed1908e61daa82d5c9835c0544afd8369589d350b04a7488a9870983860779ca2e0079a286fe71f60d5c583c3427d24ff968bad3246c1c838b90f465becc1ddfea5839b730ec219d577ed182f6da8f493350b422c86943b7c8ff42de8aee0fe01f4b91c8bb204008f06f85c3dffdb622632d2d4e8b8f0c7457cfa0f4238c7ef4c8903a89559e9307c26e844747ccb9b8dd5e7e83637983746b2fec3de051312306eb8b15db4766b3ef5fe3086d53d388cf2b3b209389ff3644e47d6bfdbe2fafef1bc2311093ad0b49f4600925f55328da337e73f01f83097acd8f2aca7a85f28e75fb4efec6551e026a1ebb35c25efde455cc44002bb8cc79288ed738423432558ebb583874aa5c356abe5be794e1bfaeaf6a7eccf67e5d938751a3a351bc21d4422d2ff0f36f5b30759d79b1ef2d83618d9c1769694454002d2f2be74de3ac10d39829369c87a70e1e9769e7d5ae7c865282a04487a8ae4cf5beeecaea6a3be1c864bdd8d61df88f08a76ac49d28a3a069d2c0d02068a10e88674b39c9d03da49256d914319d267c0d1db08ee7777668e90a94c50a065977222ee620f2291f6ca3fa464fafe8fc3fedf64a836eef5a2ca16aaae5573ee082a77f046d388750fa4ce3853c846ae3f338741c7976f72db4ade4abd4211e8d335ec8c83309bc7d7140a99dfb64a29839b9acc74de4ac0949bcbec4e76be9096a45ab6ca19b165f4097e24ab92d7b58694b0897789c3cdcca2b3d4b0a9da153fafe68f940031b6548d3c37c1301faa9adcfc41c417e613c0838340e28801f72610289d7435910fd276ca243d119541e0a121d263fdda149ac40f293e6fee6d5ddc32532ad947548eb5d20a5bfea97543965fe09313f1a5a78ce51ecac9c36b54cb573780da15d197f5ffacf1fa0d2b5495057a29104d610936c1898d1058f6f7b90e614bc2e3ff56b1e75aa4708128e3782f602dbdd29ece268311965592ddd536ea63841ea953b20677e0dd911852d23b85a3382420d22cd276b216e81638540b04966210a9308e8f9fb46958c967e3c2e36ae081a95cec8865a87d85d5689f660fe6c616ebfc2dab0f6e41d3e8c2906405fb98a506d90a8e8c6201d520a0deaa65e92e91f965288128101427d58e0b1e3ad8a49526feed27f3bcc6d505591483e2e4cc4a9b678d63f3abc905f26f91083bc595b89ff0b6cc3caa9d93013127ab7b30fbe18fad6f7f380fd6d5668fb6c3fdea3771fdd3004994e5752275ff7b186f9ad95f9d7ff01263f1165de34c1ae867e8954d66186880a90d73eace4dc1b8b17c76815242342821b4fab93755c3dc24e60aafd1cd3e283a7414de3af18c61328d92e9141916b8bb816de024a5a047a66508340a3287f698a41804e297916ff04f2921a0eeb8fcc5690c7fc024f57ab1fb6c6bc9a0caf9bf9e0e9aad64ceb2634bedbda6716235e4b93b67cd07ae06fde6abd2893143b55628be83fd4b347ce407dabf28e288f99d23b031376bfc1b1552cac1557e4730b03be581a92feae7d39fa2cf1c565a6cbe59a83b64b90ef8fc73ff6f8b9562d77fae1221df8f5ddb029f12ae80c3f128b87e56f78224b875af54a2fa1434749bb2e1c7ad9331497a71015ae0fc63903f36023e7f34b97c6ec5976ba3740845e5870c85f1b2042cdca86620881e08595215332de7d5828844e9e44124e42e1c60f6821cb71640c6643b01681553c932d310632a8b21154445176eb1a9a3c87dff22508bdbe4f1500e19131a072c42ff1d106ade135722a9e37e95e7e93917378e7907aae4be92dab78b1cd5a771d6064f6e3afc26ff84943a84de7f6ca6b0ab5993d1013b061da4053d77398cbeb329a6ae16f76493f85df1164b4f1fdff69bf113c8f18274a4ce6a05dd4c1ccbacb8d2c3760210e312c3a344294b43b23d06b7ce7263d3178e4fd530ba5838dc0e517b7d6fff2a0d9c4d69105a8fdab3f0c51a219c1ec10337b7cf05f8f3b1fb0a09f600308e5c21ae6ae06d6f87a6766d29e3a34f331f520d80524d580bd54b25716b6b937534233b856e022d20e53779b3a4a3615a3d62d1824c2bfa906e7804d629cc6712a3aee8c3703e99ec807cdb2d381acf126d63b83a2ce1d8f5cb768270bf41ae5637976acbaad8a1fa52cfb7a2f012966f3d29867cf2c28e504043a09eeff91917f6e96dc35a7df124074da73a20b87c7c8e2196f344cc08bd4c2406daaf6064488b5f9983131d90141fba82b13b0b1ff60565be66d53c36df3a9b4c772bffd428b34f94060ad32c59c9c029eba5fabd7a01b4e7252406c0ce7bb93c831034b100cc71090b37a436f96ce902973e2dca9594886b602ed6142697413aa448652529fe688a2e62fa96f8031ade066bb2bdc682f0ae3a526c7ad3c5d01e243b999a58aa5f6816dcd7a0cdd49202e128b99436f71e7fb7033bf96d8e3930e39e024530ec4b7932d334e54a66bfc3630b472336b6719d5a38e6e9bed938f71fe49e0af0b20c5db5408cabb3227b1690e904ea3116ee568330f56a5a698b914570962da4d831f5f5acde9acb257d272d0cd14e3133c89307f2d1575e32b8cc1582d1e4a680d35a1a2cace6233dfb4b0a7fea26f41785e1ac6007dd20d8b6dc3bd6857fa487c52b39f86647a67931b33910b746331305199d20ecd2e4d3b454226a134240831ea5a35c1e2d603c48eea209868b839c79a9318b6fd1078bc0f2bb9b0e931b64d63fbbcbf22b41e3cf7bee5cecb3c0e7b3ae39cf736fce8645ab33becbc9586a9154e29dd88f42ec7deecb2a4c08ac020ce54607f8006d2aa05a689ea688419215f0a10043820d85965a0001f102915fa6b2edfc4d6db7011a725db79b3974e9c1fc1636781bc9609359cfb0c5c921b83fc1115f7ed2568e49991ef93f8b8ff93a0d778251f0bcaa00ad64de8438d40aa05adbd1d1d1d2bca05ea9471a2c1a3733e92bcdf896d47dbe41b9f0d8b8b75de1ccd7cd7b7802fc01c4536a1a7b52ce70736e2cdfc547b58401023e34a608c1b09d0f13ab83d7b3fcde0e050c8cb4635508ddc143a9e6edb1e5a489a48ae0f4d5b0cede7d1b0ed8177709edbd61d859f6d9bad93a4c640684b7b8d994d8f5c0c8773da2b7a5b57d28b58d3f00c53430671d4af1537a262e8ea44a1b943c9bfc5082ad86d6690de32bb6527c815da065061bf79562d292e3d4799aa0df968fb939f64203f541dd4d006e5bd0b34b39215a972c36b229fc2f8e7f10e154b369d7b8f85f89daaaba6ec9836ad748dd79be4a58210341a458202a16e152ca2b0338a116a8490a7fa52c02"
|
||||||
|
readZebraTransaction h `shouldNotBe` Nothing
|
||||||
|
|
||||||
-- | Properties
|
-- | Properties
|
||||||
prop_PhraseLength :: Property
|
prop_PhraseLength :: Property
|
||||||
|
|
20
zebrablock.json
Normal file
20
zebrablock.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"result": {
|
||||||
|
"hash": "0041ee9cb0e256a73c92bb72d830143c402ea350152f56f19f74d23cf51418fb",
|
||||||
|
"confirmations": 3583,
|
||||||
|
"height": 2767099,
|
||||||
|
"tx": [
|
||||||
|
"d169ec3eda57dc750edfc1aa6b8ffb4ed2065780bfd5964de34b529503ec372f",
|
||||||
|
"987fcdb9bd37cbb5b205a8336de60d043f7028bebaa372828d81f3da296c7ef9"
|
||||||
|
],
|
||||||
|
"trees": {
|
||||||
|
"sapling": {
|
||||||
|
"size": 129349
|
||||||
|
},
|
||||||
|
"orchard": {
|
||||||
|
"size": 39382
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": 123
|
||||||
|
}
|
4
zebrahexblock.json
Normal file
4
zebrahexblock.json
Normal file
File diff suppressed because one or more lines are too long
8
zebratx.json
Normal file
8
zebratx.json
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"result": {
|
||||||
|
"hex": "0400008085202f8900010829d200000000001976a91484ae5002305847e7176362d7c12c19c5bdbbaf8088ac0000000023392a00f02cd200000000000192331caef004cc758fb666bed1908e61daa82d5c9835c0544afd8369589d350b04a7488a9870983860779ca2e0079a286fe71f60d5c583c3427d24ff968bad3246c1c838b90f465becc1ddfea5839b730ec219d577ed182f6da8f493350b422c86943b7c8ff42de8aee0fe01f4b91c8bb204008f06f85c3dffdb622632d2d4e8b8f0c7457cfa0f4238c7ef4c8903a89559e9307c26e844747ccb9b8dd5e7e83637983746b2fec3de051312306eb8b15db4766b3ef5fe3086d53d388cf2b3b209389ff3644e47d6bfdbe2fafef1bc2311093ad0b49f4600925f55328da337e73f01f83097acd8f2aca7a85f28e75fb4efec6551e026a1ebb35c25efde455cc44002bb8cc79288ed738423432558ebb583874aa5c356abe5be794e1bfaeaf6a7eccf67e5d938751a3a351bc21d4422d2ff0f36f5b30759d79b1ef2d83618d9c1769694454002d2f2be74de3ac10d39829369c87a70e1e9769e7d5ae7c865282a04487a8ae4cf5beeecaea6a3be1c864bdd8d61df88f08a76ac49d28a3a069d2c0d02068a10e88674b39c9d03da49256d914319d267c0d1db08ee7777668e90a94c50a065977222ee620f2291f6ca3fa464fafe8fc3fedf64a836eef5a2ca16aaae5573ee082a77f046d388750fa4ce3853c846ae3f338741c7976f72db4ade4abd4211e8d335ec8c83309bc7d7140a99dfb64a29839b9acc74de4ac0949bcbec4e76be9096a45ab6ca19b165f4097e24ab92d7b58694b0897789c3cdcca2b3d4b0a9da153fafe68f940031b6548d3c37c1301faa9adcfc41c417e613c0838340e28801f72610289d7435910fd276ca243d119541e0a121d263fdda149ac40f293e6fee6d5ddc32532ad947548eb5d20a5bfea97543965fe09313f1a5a78ce51ecac9c36b54cb573780da15d197f5ffacf1fa0d2b5495057a29104d610936c1898d1058f6f7b90e614bc2e3ff56b1e75aa4708128e3782f602dbdd29ece268311965592ddd536ea63841ea953b20677e0dd911852d23b85a3382420d22cd276b216e81638540b04966210a9308e8f9fb46958c967e3c2e36ae081a95cec8865a87d85d5689f660fe6c616ebfc2dab0f6e41d3e8c2906405fb98a506d90a8e8c6201d520a0deaa65e92e91f965288128101427d58e0b1e3ad8a49526feed27f3bcc6d505591483e2e4cc4a9b678d63f3abc905f26f91083bc595b89ff0b6cc3caa9d93013127ab7b30fbe18fad6f7f380fd6d5668fb6c3fdea3771fdd3004994e5752275ff7b186f9ad95f9d7ff01263f1165de34c1ae867e8954d66186880a90d73eace4dc1b8b17c76815242342821b4fab93755c3dc24e60aafd1cd3e283a7414de3af18c61328d92e9141916b8bb816de024a5a047a66508340a3287f698a41804e297916ff04f2921a0eeb8fcc5690c7fc024f57ab1fb6c6bc9a0caf9bf9e0e9aad64ceb2634bedbda6716235e4b93b67cd07ae06fde6abd2893143b55628be83fd4b347ce407dabf28e288f99d23b031376bfc1b1552cac1557e4730b03be581a92feae7d39fa2cf1c565a6cbe59a83b64b90ef8fc73ff6f8b9562d77fae1221df8f5ddb029f12ae80c3f128b87e56f78224b875af54a2fa1434749bb2e1c7ad9331497a71015ae0fc63903f36023e7f34b97c6ec5976ba3740845e5870c85f1b2042cdca86620881e08595215332de7d5828844e9e44124e42e1c60f6821cb71640c6643b01681553c932d310632a8b21154445176eb1a9a3c87dff22508bdbe4f1500e19131a072c42ff1d106ade135722a9e37e95e7e93917378e7907aae4be92dab78b1cd5a771d6064f6e3afc26ff84943a84de7f6ca6b0ab5993d1013b061da4053d77398cbeb329a6ae16f76493f85df1164b4f1fdff69bf113c8f18274a4ce6a05dd4c1ccbacb8d2c3760210e312c3a344294b43b23d06b7ce7263d3178e4fd530ba5838dc0e517b7d6fff2a0d9c4d69105a8fdab3f0c51a219c1ec10337b7cf05f8f3b1fb0a09f600308e5c21ae6ae06d6f87a6766d29e3a34f331f520d80524d580bd54b25716b6b937534233b856e022d20e53779b3a4a3615a3d62d1824c2bfa906e7804d629cc6712a3aee8c3703e99ec807cdb2d381acf126d63b83a2ce1d8f5cb768270bf41ae5637976acbaad8a1fa52cfb7a2f012966f3d29867cf2c28e504043a09eeff91917f6e96dc35a7df124074da73a20b87c7c8e2196f344cc08bd4c2406daaf6064488b5f9983131d90141fba82b13b0b1ff60565be66d53c36df3a9b4c772bffd428b34f94060ad32c59c9c029eba5fabd7a01b4e7252406c0ce7bb93c831034b100cc71090b37a436f96ce902973e2dca9594886b602ed6142697413aa448652529fe688a2e62fa96f8031ade066bb2bdc682f0ae3a526c7ad3c5d01e243b999a58aa5f6816dcd7a0cdd49202e128b99436f71e7fb7033bf96d8e3930e39e024530ec4b7932d334e54a66bfc3630b472336b6719d5a38e6e9bed938f71fe49e0af0b20c5db5408cabb3227b1690e904ea3116ee568330f56a5a698b914570962da4d831f5f5acde9acb257d272d0cd14e3133c89307f2d1575e32b8cc1582d1e4a680d35a1a2cace6233dfb4b0a7fea26f41785e1ac6007dd20d8b6dc3bd6857fa487c52b39f86647a67931b33910b746331305199d20ecd2e4d3b454226a134240831ea5a35c1e2d603c48eea209868b839c79a9318b6fd1078bc0f2bb9b0e931b64d63fbbcbf22b41e3cf7bee5cecb3c0e7b3ae39cf736fce8645ab33becbc9586a9154e29dd88f42ec7deecb2a4c08ac020ce54607f8006d2aa05a689ea688419215f0a10043820d85965a0001f102915fa6b2edfc4d6db7011a725db79b3974e9c1fc1636781bc9609359cfb0c5c921b83fc1115f7ed2568e49991ef93f8b8ff93a0d778251f0bcaa00ad64de8438d40aa05adbd1d1d1d2bca05ea9471a2c1a3733e92bcdf896d47dbe41b9f0d8b8b75de1ccd7cd7b7802fc01c4536a1a7b52ce70736e2cdfc547b58401023e34a608c1b09d0f13ab83d7b3fcde0e050c8cb4635508ddc143a9e6edb1e5a489a48ae0f4d5b0cede7d1b0ed8177709edbd61d859f6d9bad93a4c640684b7b8d994d8f5c0c8773da2b7a5b57d28b58d3f00c53430671d4af1537a262e8ea44a1b943c9bfc5082ad86d6690de32bb6527c815da065061bf79562d292e3d4799aa0df968fb939f64203f541dd4d006e5bd0b34b39215a972c36b229fc2f8e7f10e154b369d7b8f85f89daaaba6ec9836ad748dd79be4a58210341a458202a16e152ca2b0338a116a8490a7fa52c02",
|
||||||
|
"height": 2767099,
|
||||||
|
"confirmations": 3582
|
||||||
|
},
|
||||||
|
"id": 123
|
||||||
|
}
|
Loading…
Reference in a new issue