Skip to content

Commit 3333075

Browse files
authored
feat(rpc): tag Zebra-mined blocks with a 🦓 coinbase marker (#10836)
1 parent fcf5e6a commit 3333075

16 files changed

Lines changed: 232 additions & 72 deletions

‎.github/workflows/test-docker.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ jobs:
111111
- id: custom-conf
112112
name: Custom config
113113
env_vars: -v $GITHUB_WORKSPACE/zebrad/tests/common/configs/custom-conf.toml:/tmp/custom-conf.toml:ro -e CONFIG_FILE_PATH=/tmp/custom-conf.toml
114-
grep_patterns: -e "extra_coinbase_data:\\sSome\\(\\\"Do you even shield\\?\\\"\\)"
114+
grep_patterns: -e "extra_coinbase_data:\\sSome\\(ExtraCoinbaseData\\(\\\"Do you even shield\\?\\\"\\)\\)"
115115

116116
# RPC configuration tests
117117
- id: rpc-conf

‎CHANGELOG.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org).
99

1010
### Added
1111

12+
- Zebra now tags the coinbase input of every block it mines with a `🦓`. The
13+
`mining.extra_coinbase_data` option is now limited to 86 bytes (was 94); Zebra
14+
refuses to start if it is exceeded.
1215
- Pre-built `zebrad` binaries are attached to each GitHub release for Linux on
1316
`x86_64` and `aarch64`, so operators can run a node without Docker or a source
1417
build, also installable with `cargo binstall zebrad`. Each `.tar.gz` carries a

‎book/src/user/mining.md‎

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,22 +61,21 @@ If `miner_address` is a Unified Address with more than one receiver, Zebra sends
6161

6262
[#extra-coinbase-data]: #extra-coinbase-data
6363

64-
Zebra does not tag its blocks by default. If you don't set `extra_coinbase_data`, the blocks you mine carry no identifying data. Setting this option fixes that:
64+
Zebra prepends a `🦓` marker to the coinbase input of every block it builds. Setting `extra_coinbase_data` adds your own tag (such as a pool name) after it, separated by `": "`:
6565

6666
```toml
6767
[mining]
6868
miner_address = 't3dvVE3SQEi7kqNzwrfNePxZ1d4hUyztBA1'
6969
extra_coinbase_data = "/MyPoolName/"
7070
```
7171

72-
A few important details about how this value is used:
72+
How it's used:
7373

74-
- The string is inserted into the transparent input script of the coinbase transaction, immediately after the encoded block height.
75-
- The string is always encoded as raw UTF-8 bytes. It is **not** hex-decoded, even if it looks like a valid hex string — `extra_coinbase_data = "deadbeef"` puts the eight ASCII characters `deadbeef` in the coinbase script, not the four bytes `0xde 0xad 0xbe 0xef`. This option only carries UTF-8 text, so arbitrary non-UTF-8 byte sequences can't be embedded. In practice most mining tags are short, human-readable pool names or identifiers, so this is usually what you want anyway.
76-
- The encoded value, including Zebra's script push overhead, is limited to 94 bytes. Because that limit includes 1-2 bytes of push-opcode overhead, keep your tag at 92 bytes or less to be safe. If the limit is exceeded, Zebra refuses to build a block template until the value is shortened.
77-
- This field is optional. Leaving it unset is valid — Zebra just won't tag the block.
74+
- Inserted into the coinbase input script, after the block height, `🦓` marker, and `": "` separator.
75+
- Limited to 86 bytes. If exceeded, Zebra refuses to start.
76+
- Optional. If unset, the block still carries the `🦓` marker, just no extra data.
7877

79-
You can confirm the tag is being applied by calling `getblocktemplate` and checking the `coinbasetxn.data` field (see [Testing the setup](#testing-the-setup)): the hex string after the height bytes should decode back to your configured text.
78+
You can confirm the marker is applied by calling `getblocktemplate` and checking the `coinbasetxn.data` field (see [Testing the setup](#testing-the-setup)): after the height bytes you'll see the `🦓` marker (`f0 9f a6 93`), then — if `extra_coinbase_data` is set — the `": "` separator (`3a 20`) and your text.
8079

8180
### Miner memo
8281

‎zebra-rpc/CHANGELOG.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414
- The indexer `NonFinalizedStateChange` subscription accepts the caller's known chain
1515
tip hashes and streams only the blocks above them, so a re-subscribing consumer
1616
resumes instead of being sent the entire non-finalized state again.
17+
- `config::mining::ExtraCoinbaseData` and `config::mining::ExtraCoinbaseDataTooLong`.
1718

1819
### Changed
1920

21+
- Zebra now prepends a `🦓` marker to the coinbase input of every block it builds.
22+
- `config::mining::Config::extra_coinbase_data` is now `Option<ExtraCoinbaseData>` (was
23+
`Option<String>`), limited to 86 bytes (was 94) and validated on construction.
2024
- The read-state syncer (`TrustedChainSync`) applies backpressure to the non-finalized
2125
block stream instead of dropping blocks for a slow consumer, bridges the gap between
2226
a lagging finalized tip and the streamed non-finalized chain by fetching the missing

‎zebra-rpc/src/config/mining.rs‎

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,34 @@
11
//! Mining config
22
3-
use std::collections::HashMap;
3+
use std::{collections::HashMap, ops::Deref};
44

5-
use serde::{Deserialize, Serialize};
5+
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
66
use serde_with::{serde_as, DisplayFromStr};
77

88
use strum_macros::EnumIter;
99
use zcash_address::ZcashAddress;
10+
use zcash_transparent::coinbase::{MAX_COINBASE_HEIGHT_LEN, MAX_COINBASE_SCRIPT_LEN};
1011
use zebra_chain::parameters::NetworkKind;
1112

13+
/// The maximum length of the optional, arbitrary data in the script sig field of a coinbase tx.
14+
pub(crate) const MAX_MINER_DATA_LEN: usize = MAX_COINBASE_SCRIPT_LEN - MAX_COINBASE_HEIGHT_LEN;
15+
16+
/// The marker Zebra prepends to the coinbase input of every block it builds.
17+
///
18+
/// The zebra emoji (`U+1F993`), 4 UTF-8 bytes.
19+
pub(crate) const ZEBRA_COINBASE_MARKER: &str = "🦓";
20+
21+
/// Separates [`ZEBRA_COINBASE_MARKER`] from `extra_coinbase_data`. Present only when that is set.
22+
pub(crate) const ZEBRA_COINBASE_SEPARATOR: &str = ": ";
23+
24+
/// The maximum length of the user-configurable `extra_coinbase_data`.
25+
///
26+
/// The coinbase data is the marker, separator, and user data in a single push, so the user
27+
/// portion is [`MAX_MINER_DATA_LEN`] minus the marker, separator, and the 2-byte `OP_PUSHDATA1`
28+
/// opcode (for pushes over 75 bytes).
29+
pub(crate) const MAX_USER_COINBASE_DATA_LEN: usize =
30+
MAX_MINER_DATA_LEN - ZEBRA_COINBASE_MARKER.len() - ZEBRA_COINBASE_SEPARATOR.len() - 2;
31+
1232
/// Mining configuration section.
1333
#[serde_as]
1434
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
@@ -20,12 +40,11 @@ pub struct Config {
2040
#[serde_as(as = "Option<DisplayFromStr>")]
2141
pub miner_address: Option<ZcashAddress>,
2242

23-
/// Optional data that Zebra will include in the transparent input of a coinbase transaction.
43+
/// Optional tag that Zebra appends to the coinbase input of every block it builds, after the
44+
/// Zebra `🦓` marker and a `: ` separator.
2445
///
25-
/// The string is always encoded as raw UTF-8 bytes; it is not hex-decoded, even if it looks
26-
/// like a valid hex string. The encoded value, including Zebra's script push overhead, is
27-
/// limited to 94 bytes.
28-
pub extra_coinbase_data: Option<String>,
46+
/// Limited to `MAX_USER_COINBASE_DATA_LEN` bytes.
47+
pub extra_coinbase_data: Option<ExtraCoinbaseData>,
2948

3049
/// Optional shielded memo that Zebra will include in the output of a shielded coinbase
3150
/// transaction. Limited to 512 bytes.
@@ -54,6 +73,58 @@ impl Config {
5473
}
5574
}
5675

76+
/// Operator-configured data appended to the coinbase input of every block Zebra builds, after
77+
/// Zebra's `🦓` marker and `: ` separator.
78+
///
79+
/// Validated on construction to fit within the coinbase data budget, so an oversized value can't
80+
/// be represented — and an oversized `mining.extra_coinbase_data` in the config makes Zebra fail
81+
/// to start.
82+
#[derive(Clone, Debug, Eq, PartialEq)]
83+
pub struct ExtraCoinbaseData(String);
84+
85+
impl Deref for ExtraCoinbaseData {
86+
type Target = str;
87+
88+
fn deref(&self) -> &Self::Target {
89+
&self.0
90+
}
91+
}
92+
93+
/// The error returned when [`ExtraCoinbaseData`] is constructed from too many bytes.
94+
#[derive(Clone, Debug, thiserror::Error)]
95+
#[error("extra_coinbase_data is {0} bytes, but the maximum is {MAX_USER_COINBASE_DATA_LEN}")]
96+
pub struct ExtraCoinbaseDataTooLong(usize);
97+
98+
impl TryFrom<String> for ExtraCoinbaseData {
99+
type Error = ExtraCoinbaseDataTooLong;
100+
101+
fn try_from(data: String) -> Result<Self, Self::Error> {
102+
if data.len() > MAX_USER_COINBASE_DATA_LEN {
103+
Err(ExtraCoinbaseDataTooLong(data.len()))
104+
} else {
105+
Ok(Self(data))
106+
}
107+
}
108+
}
109+
110+
impl<'de> Deserialize<'de> for ExtraCoinbaseData {
111+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
112+
where
113+
D: Deserializer<'de>,
114+
{
115+
Self::try_from(String::deserialize(deserializer)?).map_err(de::Error::custom)
116+
}
117+
}
118+
119+
impl Serialize for ExtraCoinbaseData {
120+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
121+
where
122+
S: Serializer,
123+
{
124+
self.0.serialize(serializer)
125+
}
126+
}
127+
57128
/// The desired address type for the `mining.miner_address` field in the config.
58129
#[derive(EnumIter, Eq, PartialEq, Default, Hash)]
59130
pub enum MinerAddressType {

‎zebra-rpc/src/methods/tests/snapshots/get_block_template_basic.coinbase_tx@mainnet_10.snap‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ V5(
99
inputs: [
1010
Coinbase(
1111
height: Height(1687105),
12-
data: [],
12+
data: [
13+
4,
14+
240,
15+
159,
16+
166,
17+
147,
18+
],
1319
sequence: 4294967295,
1420
),
1521
],

‎zebra-rpc/src/methods/tests/snapshots/get_block_template_basic.coinbase_tx@testnet_10.snap‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ V5(
99
inputs: [
1010
Coinbase(
1111
height: Height(1842421),
12-
data: [],
12+
data: [
13+
4,
14+
240,
15+
159,
16+
166,
17+
147,
18+
],
1319
sequence: 4294967295,
1420
),
1521
],

‎zebra-rpc/src/methods/tests/snapshots/get_block_template_basic@mainnet_10.snap‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,20 @@ expression: block_template
88
],
99
"version": 4,
1010
"previousblockhash": "0000000000d723156d9b65ffcf4984da7a19675ed7e2f06d9e5d5188af087bf8",
11-
"blockcommitmentshash": "fe03d8236b0835c758f59d279230ebaee2128754413103b9edb17c07451c2c82",
12-
"lightclientroothash": "fe03d8236b0835c758f59d279230ebaee2128754413103b9edb17c07451c2c82",
13-
"finalsaplingroothash": "fe03d8236b0835c758f59d279230ebaee2128754413103b9edb17c07451c2c82",
11+
"blockcommitmentshash": "9fe0f2f50842e3d0d5c4b78554ead8bfaa3314241f6f899d2373b96a3aaecccd",
12+
"lightclientroothash": "9fe0f2f50842e3d0d5c4b78554ead8bfaa3314241f6f899d2373b96a3aaecccd",
13+
"finalsaplingroothash": "9fe0f2f50842e3d0d5c4b78554ead8bfaa3314241f6f899d2373b96a3aaecccd",
1414
"defaultroots": {
1515
"merkleroot": "0dd4c87d6aba52431fef01079578826b547a22e444af986368c507918f893e8a",
1616
"chainhistoryroot": "94470fa66ebd1a5fdb109a5aa3f3204f14de3a42135e71aa7f4c44055847e0b5",
17-
"authdataroot": "0dbb78de9fdcd494307971e36dd049fc82d0ee9ee53aec8fd2a54dc0e426289b",
18-
"blockcommitmentshash": "fe03d8236b0835c758f59d279230ebaee2128754413103b9edb17c07451c2c82"
17+
"authdataroot": "c1b58477fa048262b10f726bf451dc8476e55fc56d82a5792a0ba63d0e2f645d",
18+
"blockcommitmentshash": "9fe0f2f50842e3d0d5c4b78554ead8bfaa3314241f6f899d2373b96a3aaecccd"
1919
},
2020
"transactions": [],
2121
"coinbasetxn": {
22-
"data": "050000800a27a726b4d0d6c20000000041be1900010000000000000000000000000000000000000000000000000000000000000000ffffffff040341be19ffffffff0480b2e60e0000000017a9147e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e87286bee000000000017a914d45cb1adffb5215a42720532a076f02c7c778c908738c94d010000000017a91469a9f95a98fe581b6eb52841ef4806dc4402eb908740787d010000000017a914931fec54c1fea86e574462cc32013f5400b8912987000000",
22+
"data": "050000800a27a726b4d0d6c20000000041be1900010000000000000000000000000000000000000000000000000000000000000000ffffffff090341be1904f09fa693ffffffff0480b2e60e0000000017a9147e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e87286bee000000000017a914d45cb1adffb5215a42720532a076f02c7c778c908738c94d010000000017a91469a9f95a98fe581b6eb52841ef4806dc4402eb908740787d010000000017a914931fec54c1fea86e574462cc32013f5400b8912987000000",
2323
"hash": "0dd4c87d6aba52431fef01079578826b547a22e444af986368c507918f893e8a",
24-
"authdigest": "0dbb78de9fdcd494307971e36dd049fc82d0ee9ee53aec8fd2a54dc0e426289b",
24+
"authdigest": "c1b58477fa048262b10f726bf451dc8476e55fc56d82a5792a0ba63d0e2f645d",
2525
"depends": [],
2626
"fee": 0,
2727
"sigops": 0,

‎zebra-rpc/src/methods/tests/snapshots/get_block_template_basic@testnet_10.snap‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,20 @@ expression: block_template
88
],
99
"version": 4,
1010
"previousblockhash": "0000000000d723156d9b65ffcf4984da7a19675ed7e2f06d9e5d5188af087bf8",
11-
"blockcommitmentshash": "cb1f1c6a5ad5ff9c4a170e3b747a24f3aec79817adba9a9451f19914481bb422",
12-
"lightclientroothash": "cb1f1c6a5ad5ff9c4a170e3b747a24f3aec79817adba9a9451f19914481bb422",
13-
"finalsaplingroothash": "cb1f1c6a5ad5ff9c4a170e3b747a24f3aec79817adba9a9451f19914481bb422",
11+
"blockcommitmentshash": "09b60daeec8adbd96a08459e35f142fac3ac7ea5c5fb218f521c9da8b3428f91",
12+
"lightclientroothash": "09b60daeec8adbd96a08459e35f142fac3ac7ea5c5fb218f521c9da8b3428f91",
13+
"finalsaplingroothash": "09b60daeec8adbd96a08459e35f142fac3ac7ea5c5fb218f521c9da8b3428f91",
1414
"defaultroots": {
1515
"merkleroot": "453e5aa76d390b623e714cad2658c219393b3019e639e8d477f91eca7697b62b",
1616
"chainhistoryroot": "03bc75f00c307a05aed2023819e18c2672cbe15fbd3200944997def141967387",
17-
"authdataroot": "a44375f0c0dd5ba612bd7b0efd77683cde8edf5055aff9fbfda443cc8d46bd3e",
18-
"blockcommitmentshash": "cb1f1c6a5ad5ff9c4a170e3b747a24f3aec79817adba9a9451f19914481bb422"
17+
"authdataroot": "9e7e7313dee920062d78f2f738c6d127b303c5f4649cd9a1b92a955c06c02fe4",
18+
"blockcommitmentshash": "09b60daeec8adbd96a08459e35f142fac3ac7ea5c5fb218f521c9da8b3428f91"
1919
},
2020
"transactions": [],
2121
"coinbasetxn": {
22-
"data": "050000800a27a726b4d0d6c200000000f51c1c00010000000000000000000000000000000000000000000000000000000000000000ffffffff0403f51c1cffffffff0480b2e60e0000000017a9147e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e87286bee000000000017a9140c0bcca02f3cba01a5d7423ac3903d40586399eb8738c94d010000000017a9144e3f0d9a33a2721604cbae2de8d9171e21f8fbe48740787d010000000017a91471e1df05024288a00802de81e08c437859586c8787000000",
22+
"data": "050000800a27a726b4d0d6c200000000f51c1c00010000000000000000000000000000000000000000000000000000000000000000ffffffff0903f51c1c04f09fa693ffffffff0480b2e60e0000000017a9147e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e7e87286bee000000000017a9140c0bcca02f3cba01a5d7423ac3903d40586399eb8738c94d010000000017a9144e3f0d9a33a2721604cbae2de8d9171e21f8fbe48740787d010000000017a91471e1df05024288a00802de81e08c437859586c8787000000",
2323
"hash": "453e5aa76d390b623e714cad2658c219393b3019e639e8d477f91eca7697b62b",
24-
"authdigest": "a44375f0c0dd5ba612bd7b0efd77683cde8edf5055aff9fbfda443cc8d46bd3e",
24+
"authdigest": "9e7e7313dee920062d78f2f738c6d127b303c5f4649cd9a1b92a955c06c02fe4",
2525
"depends": [],
2626
"fee": 0,
2727
"sigops": 0,

‎zebra-rpc/src/methods/tests/snapshots/get_block_template_long_poll.coinbase_tx@mainnet_10.snap‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ V5(
99
inputs: [
1010
Coinbase(
1111
height: Height(1687105),
12-
data: [],
12+
data: [
13+
4,
14+
240,
15+
159,
16+
166,
17+
147,
18+
],
1319
sequence: 4294967295,
1420
),
1521
],

0 commit comments

Comments
 (0)