Implements low-level transparent components #45

Merged
pitmutt merged 14 commits from rav001 into dev040 2024-03-26 20:39:32 +00:00
8 changed files with 253 additions and 25 deletions
Showing only changes of commit b0df0480c5 - Show all commits

View file

@ -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,

View file

@ -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'& } -> `()'
#} #}

View file

@ -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 =

View file

@ -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

View file

@ -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
View 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

File diff suppressed because one or more lines are too long

8
zebratx.json Normal file
View file

@ -0,0 +1,8 @@
{
"result": {
"hex": "0400008085202f8900010829d200000000001976a91484ae5002305847e7176362d7c12c19c5bdbbaf8088ac0000000023392a00f02cd200000000000192331caef004cc758fb666bed1908e61daa82d5c9835c0544afd8369589d350b04a7488a9870983860779ca2e0079a286fe71f60d5c583c3427d24ff968bad3246c1c838b90f465becc1ddfea5839b730ec219d577ed182f6da8f493350b422c86943b7c8ff42de8aee0fe01f4b91c8bb204008f06f85c3dffdb622632d2d4e8b8f0c7457cfa0f4238c7ef4c8903a89559e9307c26e844747ccb9b8dd5e7e83637983746b2fec3de051312306eb8b15db4766b3ef5fe3086d53d388cf2b3b209389ff3644e47d6bfdbe2fafef1bc2311093ad0b49f4600925f55328da337e73f01f83097acd8f2aca7a85f28e75fb4efec6551e026a1ebb35c25efde455cc44002bb8cc79288ed738423432558ebb583874aa5c356abe5be794e1bfaeaf6a7eccf67e5d938751a3a351bc21d4422d2ff0f36f5b30759d79b1ef2d83618d9c1769694454002d2f2be74de3ac10d39829369c87a70e1e9769e7d5ae7c865282a04487a8ae4cf5beeecaea6a3be1c864bdd8d61df88f08a76ac49d28a3a069d2c0d02068a10e88674b39c9d03da49256d914319d267c0d1db08ee7777668e90a94c50a065977222ee620f2291f6ca3fa464fafe8fc3fedf64a836eef5a2ca16aaae5573ee082a77f046d388750fa4ce3853c846ae3f338741c7976f72db4ade4abd4211e8d335ec8c83309bc7d7140a99dfb64a29839b9acc74de4ac0949bcbec4e76be9096a45ab6ca19b165f4097e24ab92d7b58694b0897789c3cdcca2b3d4b0a9da153fafe68f940031b6548d3c37c1301faa9adcfc41c417e613c0838340e28801f72610289d7435910fd276ca243d119541e0a121d263fdda149ac40f293e6fee6d5ddc32532ad947548eb5d20a5bfea97543965fe09313f1a5a78ce51ecac9c36b54cb573780da15d197f5ffacf1fa0d2b5495057a29104d610936c1898d1058f6f7b90e614bc2e3ff56b1e75aa4708128e3782f602dbdd29ece268311965592ddd536ea63841ea953b20677e0dd911852d23b85a3382420d22cd276b216e81638540b04966210a9308e8f9fb46958c967e3c2e36ae081a95cec8865a87d85d5689f660fe6c616ebfc2dab0f6e41d3e8c2906405fb98a506d90a8e8c6201d520a0deaa65e92e91f965288128101427d58e0b1e3ad8a49526feed27f3bcc6d505591483e2e4cc4a9b678d63f3abc905f26f91083bc595b89ff0b6cc3caa9d93013127ab7b30fbe18fad6f7f380fd6d5668fb6c3fdea3771fdd3004994e5752275ff7b186f9ad95f9d7ff01263f1165de34c1ae867e8954d66186880a90d73eace4dc1b8b17c76815242342821b4fab93755c3dc24e60aafd1cd3e283a7414de3af18c61328d92e9141916b8bb816de024a5a047a66508340a3287f698a41804e297916ff04f2921a0eeb8fcc5690c7fc024f57ab1fb6c6bc9a0caf9bf9e0e9aad64ceb2634bedbda6716235e4b93b67cd07ae06fde6abd2893143b55628be83fd4b347ce407dabf28e288f99d23b031376bfc1b1552cac1557e4730b03be581a92feae7d39fa2cf1c565a6cbe59a83b64b90ef8fc73ff6f8b9562d77fae1221df8f5ddb029f12ae80c3f128b87e56f78224b875af54a2fa1434749bb2e1c7ad9331497a71015ae0fc63903f36023e7f34b97c6ec5976ba3740845e5870c85f1b2042cdca86620881e08595215332de7d5828844e9e44124e42e1c60f6821cb71640c6643b01681553c932d310632a8b21154445176eb1a9a3c87dff22508bdbe4f1500e19131a072c42ff1d106ade135722a9e37e95e7e93917378e7907aae4be92dab78b1cd5a771d6064f6e3afc26ff84943a84de7f6ca6b0ab5993d1013b061da4053d77398cbeb329a6ae16f76493f85df1164b4f1fdff69bf113c8f18274a4ce6a05dd4c1ccbacb8d2c3760210e312c3a344294b43b23d06b7ce7263d3178e4fd530ba5838dc0e517b7d6fff2a0d9c4d69105a8fdab3f0c51a219c1ec10337b7cf05f8f3b1fb0a09f600308e5c21ae6ae06d6f87a6766d29e3a34f331f520d80524d580bd54b25716b6b937534233b856e022d20e53779b3a4a3615a3d62d1824c2bfa906e7804d629cc6712a3aee8c3703e99ec807cdb2d381acf126d63b83a2ce1d8f5cb768270bf41ae5637976acbaad8a1fa52cfb7a2f012966f3d29867cf2c28e504043a09eeff91917f6e96dc35a7df124074da73a20b87c7c8e2196f344cc08bd4c2406daaf6064488b5f9983131d90141fba82b13b0b1ff60565be66d53c36df3a9b4c772bffd428b34f94060ad32c59c9c029eba5fabd7a01b4e7252406c0ce7bb93c831034b100cc71090b37a436f96ce902973e2dca9594886b602ed6142697413aa448652529fe688a2e62fa96f8031ade066bb2bdc682f0ae3a526c7ad3c5d01e243b999a58aa5f6816dcd7a0cdd49202e128b99436f71e7fb7033bf96d8e3930e39e024530ec4b7932d334e54a66bfc3630b472336b6719d5a38e6e9bed938f71fe49e0af0b20c5db5408cabb3227b1690e904ea3116ee568330f56a5a698b914570962da4d831f5f5acde9acb257d272d0cd14e3133c89307f2d1575e32b8cc1582d1e4a680d35a1a2cace6233dfb4b0a7fea26f41785e1ac6007dd20d8b6dc3bd6857fa487c52b39f86647a67931b33910b746331305199d20ecd2e4d3b454226a134240831ea5a35c1e2d603c48eea209868b839c79a9318b6fd1078bc0f2bb9b0e931b64d63fbbcbf22b41e3cf7bee5cecb3c0e7b3ae39cf736fce8645ab33becbc9586a9154e29dd88f42ec7deecb2a4c08ac020ce54607f8006d2aa05a689ea688419215f0a10043820d85965a0001f102915fa6b2edfc4d6db7011a725db79b3974e9c1fc1636781bc9609359cfb0c5c921b83fc1115f7ed2568e49991ef93f8b8ff93a0d778251f0bcaa00ad64de8438d40aa05adbd1d1d1d2bca05ea9471a2c1a3733e92bcdf896d47dbe41b9f0d8b8b75de1ccd7cd7b7802fc01c4536a1a7b52ce70736e2cdfc547b58401023e34a608c1b09d0f13ab83d7b3fcde0e050c8cb4635508ddc143a9e6edb1e5a489a48ae0f4d5b0cede7d1b0ed8177709edbd61d859f6d9bad93a4c640684b7b8d994d8f5c0c8773da2b7a5b57d28b58d3f00c53430671d4af1537a262e8ea44a1b943c9bfc5082ad86d6690de32bb6527c815da065061bf79562d292e3d4799aa0df968fb939f64203f541dd4d006e5bd0b34b39215a972c36b229fc2f8e7f10e154b369d7b8f85f89daaaba6ec9836ad748dd79be4a58210341a458202a16e152ca2b0338a116a8490a7fa52c02",
"height": 2767099,
"confirmations": 3582
},
"id": 123
}