@@ -1304,6 +1304,8 @@ where
13041304 transactions_request,
13051305 // Orchard trees
13061306 zebra_state:: ReadRequest :: OrchardTree ( hash_or_height) ,
1307+ // Ironwood trees
1308+ zebra_state:: ReadRequest :: IronwoodTree ( hash_or_height) ,
13071309 // Block info
13081310 zebra_state:: ReadRequest :: BlockInfo ( previous_block_hash. into( ) ) ,
13091311 zebra_state:: ReadRequest :: BlockInfo ( hash_or_height) ,
@@ -1382,7 +1384,25 @@ where
13821384 size : orchard_tree_size,
13831385 } ;
13841386
1385- let trees = GetBlockTrees { sapling, orchard } ;
1387+ let ironwood_tree_response = futs. next ( ) . await . expect ( "`futs` should not be empty" ) ;
1388+ let zebra_state:: ReadResponse :: IronwoodTree ( ironwood_tree) =
1389+ ironwood_tree_response. map_misc_error ( ) ?
1390+ else {
1391+ unreachable ! ( "unmatched response to an IronwoodTree request" ) ;
1392+ } ;
1393+
1394+ // This could be `None` if there's a chain reorg between state queries. Before NU6.3 the
1395+ // Ironwood tree is empty (size 0).
1396+ let ironwood_tree = ironwood_tree. ok_or_misc_error ( "missing Ironwood tree" ) ?;
1397+ let ironwood = IronwoodTrees {
1398+ size : ironwood_tree. count ( ) ,
1399+ } ;
1400+
1401+ let trees = GetBlockTrees {
1402+ sapling,
1403+ orchard,
1404+ ironwood,
1405+ } ;
13861406
13871407 let block_info_response = futs. next ( ) . await . expect ( "`futs` should not be empty" ) ;
13881408 let zebra_state:: ReadResponse :: BlockInfo ( prev_block_info) =
@@ -1959,6 +1979,27 @@ where
19591979 let ( orchard_tree, orchard_root) =
19601980 orchard. map_or ( ( None , None ) , |( tree, root) | ( Some ( tree) , Some ( root) ) ) ;
19611981
1982+ let ironwood = if network. is_nu_active ( consensus:: NetworkUpgrade :: Nu6_3 , height. into ( ) ) {
1983+ match read_state
1984+ . ready ( )
1985+ . and_then ( |service| {
1986+ service. call ( zebra_state:: ReadRequest :: IronwoodTree ( hash. into ( ) ) )
1987+ } )
1988+ . await
1989+ . map_misc_error ( ) ?
1990+ {
1991+ zebra_state:: ReadResponse :: IronwoodTree ( tree) => {
1992+ tree. map ( |t| ( t. to_rpc_bytes ( ) , t. root ( ) . bytes_in_display_order ( ) . to_vec ( ) ) )
1993+ }
1994+ _ => unreachable ! ( "unmatched response to an Ironwood tree request" ) ,
1995+ }
1996+ } else {
1997+ None
1998+ } ;
1999+ // Only present from NU6.3, so pre-NU6.3 responses keep the sprout/sapling/orchard shape.
2000+ let ironwood = ironwood
2001+ . map ( |( tree, root) | Treestate :: new ( trees:: Commitments :: new ( Some ( root) , Some ( tree) ) ) ) ;
2002+
19622003 Ok ( GetTreestateResponse :: new (
19632004 hash,
19642005 height,
@@ -1968,6 +2009,7 @@ where
19682009 None ,
19692010 Treestate :: new ( trees:: Commitments :: new ( sapling_root, sapling_tree) ) ,
19702011 Treestate :: new ( trees:: Commitments :: new ( orchard_root, orchard_tree) ) ,
2012+ ironwood,
19712013 ) )
19722014 }
19732015
@@ -1979,7 +2021,7 @@ where
19792021 ) -> Result < GetSubtreesByIndexResponse > {
19802022 let mut read_state = self . read_state . clone ( ) ;
19812023
1982- const POOL_LIST : & [ & str ] = & [ "sapling" , "orchard" ] ;
2024+ const POOL_LIST : & [ & str ] = & [ "sapling" , "orchard" , "ironwood" ] ;
19832025
19842026 if pool == "sapling" {
19852027 let request = zebra_state:: ReadRequest :: SaplingSubtrees { start_index, limit } ;
@@ -2020,6 +2062,33 @@ where
20202062 _ => unreachable ! ( "unmatched response to a subtrees request" ) ,
20212063 } ;
20222064
2065+ let subtrees = subtrees
2066+ . values ( )
2067+ . map ( |subtree| SubtreeRpcData {
2068+ root : subtree. root . encode_hex ( ) ,
2069+ end_height : subtree. end_height ,
2070+ } )
2071+ . collect ( ) ;
2072+
2073+ Ok ( GetSubtreesByIndexResponse {
2074+ pool,
2075+ start_index,
2076+ subtrees,
2077+ } )
2078+ } else if pool == "ironwood" {
2079+ let request = zebra_state:: ReadRequest :: IronwoodSubtrees { start_index, limit } ;
2080+ let response = read_state
2081+ . ready ( )
2082+ . and_then ( |service| service. call ( request) )
2083+ . await
2084+ . map_misc_error ( ) ?;
2085+
2086+ let subtrees = match response {
2087+ zebra_state:: ReadResponse :: IronwoodSubtrees ( subtrees) => subtrees,
2088+ _ => unreachable ! ( "unmatched response to a subtrees request" ) ,
2089+ } ;
2090+
2091+ // Ironwood reuses the Orchard note type, so the subtree root is encoded like Orchard's.
20232092 let subtrees = subtrees
20242093 . values ( )
20252094 . map ( |subtree| SubtreeRpcData {
@@ -4372,23 +4441,29 @@ pub struct GetBlockTrees {
43724441 sapling : SaplingTrees ,
43734442 #[ serde( skip_serializing_if = "OrchardTrees::is_empty" ) ]
43744443 orchard : OrchardTrees ,
4444+ // `default` so responses and fixtures from before Ironwood (which have no `ironwood` field)
4445+ // still deserialize, as the empty tree.
4446+ #[ serde( default , skip_serializing_if = "IronwoodTrees::is_empty" ) ]
4447+ ironwood : IronwoodTrees ,
43754448}
43764449
43774450impl Default for GetBlockTrees {
43784451 fn default ( ) -> Self {
43794452 GetBlockTrees {
43804453 sapling : SaplingTrees { size : 0 } ,
43814454 orchard : OrchardTrees { size : 0 } ,
4455+ ironwood : IronwoodTrees { size : 0 } ,
43824456 }
43834457 }
43844458}
43854459
43864460impl GetBlockTrees {
43874461 /// Constructs a new instance of ['GetBlockTrees'].
4388- pub fn new ( sapling : u64 , orchard : u64 ) -> Self {
4462+ pub fn new ( sapling : u64 , orchard : u64 , ironwood : u64 ) -> Self {
43894463 GetBlockTrees {
43904464 sapling : SaplingTrees { size : sapling } ,
43914465 orchard : OrchardTrees { size : orchard } ,
4466+ ironwood : IronwoodTrees { size : ironwood } ,
43924467 }
43934468 }
43944469
@@ -4401,6 +4476,11 @@ impl GetBlockTrees {
44014476 pub fn orchard ( self ) -> u64 {
44024477 self . orchard . size
44034478 }
4479+
4480+ /// Returns ironwood data held by ['GetBlockTrees'].
4481+ pub fn ironwood ( self ) -> u64 {
4482+ self . ironwood . size
4483+ }
44044484}
44054485
44064486/// Sapling note commitment tree information.
@@ -4427,6 +4507,18 @@ impl OrchardTrees {
44274507 }
44284508}
44294509
4510+ /// Ironwood note commitment tree information. Ironwood reuses the Orchard tree type.
4511+ #[ derive( Copy , Clone , Default , Debug , Eq , PartialEq , serde:: Deserialize , serde:: Serialize ) ]
4512+ pub struct IronwoodTrees {
4513+ size : u64 ,
4514+ }
4515+
4516+ impl IronwoodTrees {
4517+ fn is_empty ( & self ) -> bool {
4518+ self . size == 0
4519+ }
4520+ }
4521+
44304522/// Build a valid height range from the given optional start and end numbers.
44314523///
44324524/// # Parameters
0 commit comments