-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Description
TL;DR: A malicious mining pool is disrupting the network and could cause deep blockchain re-organizations. DNS checkpoints, which have existed for emergency use in the code since 2014, could prevent deep blockchain re-organizations, but Monero's blockchain consensus protocol would temporarily be less decentralized.
Background
As of August 2025, a malicious mining pool has used its large (yet minority) share of Monero hashpower to pursue a "selfish mining" strategy. The actions of the mining pool have reduced the revenue of honest mining pools, caused delays in transaction confirmations, and produced short (up to 9 blocks) blockchain re-organizations. Large blockchain re-organizations (10 blocks and deeper) have a nonzero probability of invalidating transactions (funds return to the wallet of the user who sent the funds) and can enable deliberate double-spend attacks (a form of fraud) against merchants who consider funds "fully confirmed" at a depth less than the re-org depth achieved by the attacker. An attacker with minority hashpower can still occasionally cause deep blockchain re-organizations. For example, a determined attacker with 30% hashpower share could cause a 10-block re-org about three times per day. (See "Table: Duration of meta attack to achieve attack success probability of 50 percent" for details.)
Many medium- and long-term proposals are being considered. An emergency defense that could be deployed quickly is rolling DNS checkpoints.
In 2014, just a few months after the birth of Monero, a feature was added to the node software to enable an opt-in check of checkpointed blocks in the blockchain by checking block hashes placed in the Domain Name System (DNS) records of domains managed by the Monero Core Team. At the time, the purpose of the feature was to mitigate chain splits resulting from consensus bugs:
Users can also set auto-enforcing the checkpoints via
--enforce-dns-checkpointing
option tomonerod
. In case of mismatch,monerod
will rollback the local blockchain by a few blocks. Eventually, the alternative ("fixed") fork will get heavier and the node will follow it, leaving the "invalid" fork behind. This option is recommended for unattended full nodes.
Summing up, MoneroPulse is [an] emergency checkpointing mechanism. It is opt-in for the users.
The domains, checkpoints.moneropulse.se
, checkpoints.moneropulse.org
, checkpoints.moneropulse.net
, and checkpoints.moneropulse.co
, already host records for older, static checkpointed blocks. The records can be view by inputting this command on a Linux terminal:
dig -t txt checkpoints.moneropulse.net +dnssec
Procedure and what could be achieved
The original purpose of the DNS checkpoints was not to prevent deep chain re-organizations. However, in an emergency, they can be used for this purpose. The procedure and attack defense scenario could look like:
- Some nodes would be operated by the Monero Core Team. Let
k
be the depth of blocks that the nodes would consider to be "finalized" and not subject to being replaced by a blockchain re-org (perhapsk = 2
). - Once the nodes agree, a script would update the 4 DNS domain records to declare a particular block (identified by its height and block hash) as a "checkpointed" block.
- If an attacker tried to re-org the checkpointed block by secretly mining and broadcasting a longer chain, the nodes in the network that have enabled
--enforce-dns-checkpointing
would reject the re-org attempt and stay on the honest chain. - Even if the attacker attempted to mine a longer chain, eventually the honest chain would overtake the miner's chain and even nodes on the network that did not have
--enforce-dns-checkpointing
enabled would follow the honest chain.
To work properly, mining pools possessing the majority of active hashpower (not just the majority of honest hashpower) would need to start enforcing DNS checkpoints. Ideally, parties that handle a lot of XMR such as exchanges and large merchants would also enable --enforce-dns-checkpointing
to prevent double-spend attacks against them.
Given the simple specification above, rolling DNS checkpoints would:
- Prevent deep chain re-orgs
- Decrease the revenue loss of honest miners by shortening the attack window of selfish mining
The rolling DNS checkpoints would not:
- Prevent an adversary from mining empty blocks, delaying user transactions from being confirmed.
- Prevent the almost complete loss of revenue by honest miners if the attacker gained and sustained majority hashpower.
Advantages
Practical advantages
-
The code for this countermeasure has been in the Monero codebase for over ten years. The countermeasure is ready to deploy now, once final testing, "procedure smoothing" (see below) and successful outreach to honest mining pools is completed.
-
Humans (i.e. the Monero Core Team) could intervene by ceasing or modifying the checkpointing procedure if the checkpointing produces undesirable results.
Principled advantages
- Rolling DNS checkpoints could keep the Monero network working, with minimum disruption, for the thousands of users who depend on it every day for private peer-to-peer electronic cash.
Disadvantages
Practical disadvantages
-
Rolling DNS checkpointing has never been deployed on Monero's mainnet before. Something unforeseen could go wrong.
-
Successful checkpointing requires most of the major honest Monero mining pools to voluntarily enable DNS checkpointing enforcement. If the mining pools choose not to, rolling DNS checkpoints will likely be unsuccessful.
-
Maintaining control of the DNS records would require strict security procedures. Two previous security incidents in 2019 and 2023 involving malicious software hosted on getmonero.org servers and theft of community donated funds should be kept in mind.
Principled disadvantages
-
Without a doubt, Monero's consensus mechanism would be less decentralized if rolling DNS checkpoints were enabled.
-
Monero nodes would temporarily no longer follow the chain with the most proof of work.
-
Mining pools following the DNS checkpoints would be similar to a situation of the majority of mining hashpower coordinating and colluding against a minority hashpower miner. "Majority hashpower cannot collude" may be considered a security assumption of proof-of-work consensus.
Parameters and procedure details to decide
Some parameters that are open to discussion:
k
, how many blocks deep from the chain tip to checkpoint.- Time To Live (TTL) of the DNS records, which can affect updating frequency.
- How often nodes should check the DNS checkpoints.
- Should there be more than one rolling checkpoint at a time? If so, how many?
How should the checkpointing script decide which specific block hash to checkpoint? Should it poll an odd number of nodes and require majority agreement?
Should nodes help alternative blocks propagate more quickly and completely so that all nodes have the same information set? Normal fluffy block propagation is fast: in a data collection trial involving five nodes, 80 percent of new blocks arrived at all nodes within a one-second interval..
Note that 3 of the 4 DNS checkpoint records must agree or the DNS checkpoint is ignored by nodes.
Precedents
There are precedents for preventing network damage and disruption of user experience by coordinating with mining pools and issuing voluntary rules through DNS:
- In 2017, the Monero Research Lab discovered a flaw in Monero's original cryptography implementation that would allow a malicious actor to create counterfeit Monero coins. The flaw was patched and confirmed to never be exploited. (Exploitation would be leave clear evidence in the blockchain history.) From February 22, 2017 to April 14, 2017, mining pools implemented a soft fork (in effect) to prevent the counterfeiting flaw from being exploited:
2017-02-22: A point release of Monero is rushed out so that exchanges and mining pools can update, under the guise of it preventing a RingCT DoS attack (such attack did not exist, but it seemed a fair explanation)....
2017-04-14: The Monero network hard forks to increase the dynamic block size minimum median, but this has the added bonus of ensuring the entire network is protected.
-
In 2020, a malicious actor launched an attack on nodes. The attack attempted to reduce user privacy and cause nodes to crash. Monero developers implemented an optional ban list of malicious node IP addresses, supplied through DNS, to weaken the attack while long-term measures were studied and implemented.
-
In 2023, the Monero Research Lab discovered that mining pools were inadvertently delaying Monero transaction confirmations by about 60 seconds longer than necessary. Many mining pool operators, including Nanopool, MoneroOcean, SupportXMR, and Hashvault, changed their mining pool software configurations to speed up transaction confirmations. The configuration update was in the economic self-interest of the mining pools because it increased their transaction fee reward by mining transactions before other pools.
Paths to majority hashpower
DataHoarder provided most of the data required for this section's analysis and reviewed a draft.
Honest miners who enforce the DNS checkpoints must be able to (eventually) outrun the attacking chains mined by the selfish miner. These checkpoint-enforcing miners must have the majority of all hashpower (not just the majority of honest hashpower). Honest miners who do not enforce DNS checkpoints can inadvertently assist the selfish miner in the following plausible scenario:
For simplicity, let the selfish miner, honest checkpoint-enforcing miners, and honest non-checkpoint-enforcing miners each have one third of network hashpower.
- All honest miners (controlling 2/3rds of hashpower) are mining the public, honest chain. The selfish miner is attempting to mine a long attacking chain in secret.
- The selfish miner is lucky and successfully mines an attacking chain of some length
$l$ . At this length$l$ , the attacking chain would re-org (orphan) the honest chain to a depth that the DNS checkpoints would prohibit. - The selfish miner broadcasts the attacking chain. (Also of note is that the selfish miner releases a chain of
$N+1$ , so honest miners usually only needs to catch up to$+2$ , yet the selfish miner might behave differently on the face of checkpoints.) The two groups of honest miners react in two different ways:- The honest checkpoint-enforcing miners reject the new attacking chain and continue to mine on the honest chain.
- The honest non-checkpoint-enforcing miners accept the new attacking chain as the longest chain and begin mining on top of it.
- The selfish miner continues to mine on the attacking chain, which is now public. Its own hashpower is 1/3rd of network hashpower, but it has added another 1/3rd of network hashpower to its attacking chain by bringing the non-checkpoint-enforcing miners onto its attacking chain.
- The chain mined by checkpoint-enforcing miners only has 1/3rd of network hashpower. In this simple scenario, their chain falls behind the selfish miner's chain and never catches up, becoming a permanent minority-hashpower alternative chain.
The permanent chain split described in this scenario is a major risk of rolling DNS checkpoints. A majority of hashpower must enforce checkpoints to avoid the risk.
Estimates of mining pool hashpower
Normally, the share of hashpower contributed by each mining pool can be estimated by pulling historical blockchain data from nodes. However, blocks mined by mining pools can be removed from the main blockchain by a selfish miner who mines a successful attacking chain. The selfish miner's own blocks that fail an attack attempt are also valid blocks that are not included in the main chain. The orphaned blocks of honest miners and the selfish miners must be included in the estimate of hashpower.
The following is an analysis of the share of all blocks (main-chain and orphaned) mined by mining pools during "Epoch 176" of the Qubic protocol, from 2025-08-28 00:00:00 UTC to 2025-09-03 12:00:00 UTC. Qubic has revealed the view key of the Monero wallet that receives mining rewards for its Epoch 176.
Below is a plot of the share of blocks mined by Qubic, aggregated by 1-hour and 6-hour intervals. Note that random luck plays a major role in the share of blocks mined in 1-hour periods because each hour would contain only about 30 blocks on average. The maximum share of blocks mined by Qubic in any 6-hour period was about 40 percent.

Below is a table of the number of blocks mined by each pool during Qubic Epoch 176. Columns for both the whole period and only "marathons" are shown. Qubic mines in 24 hour bursts called "marathons". During Epoch 176, 24-hour marathons started on August 28, 30, and September 1 at 12:00 UTC.
Table: Blocks mined by pools during Qubic Epoch 176
all time blocks |
all time share (%) |
marathon blocks |
marathon share (%) |
|
---|---|---|---|---|
qubic | 1352 | 25.60 | 852 | 32.69 |
supportxmr.com | 1256 | 23.78 | 586 | 22.49 |
xmr.nanopool.org | 1048 | 19.84 | 455 | 17.46 |
monero.hashvault.pro | 708 | 13.40 | 280 | 10.74 |
p2pool.observer | 254 | 4.81 | 112 | 4.30 |
c3pool.org | 185 | 3.50 | 79 | 3.03 |
moneroocean.stream | 166 | 3.14 | 80 | 3.07 |
Unknown | 90 | 1.70 | 56 | 2.15 |
kryptex.com | 86 | 1.63 | 39 | 1.50 |
xmrpool.eu | 48 | 0.91 | 22 | 0.84 |
monero.herominers.com | 32 | 0.61 | 21 | 0.81 |
skypool.org | 28 | 0.53 | 11 | 0.42 |
mini.p2pool.observer | 15 | 0.28 | 6 | 0.23 |
monerohash.com | 6 | 0.11 | 3 | 0.12 |
pool.xmr.pt | 3 | 0.06 | 0 | 0.00 |
nano.p2pool.observer | 2 | 0.04 | 2 | 0.08 |
monerod.org | 1 | 0.02 | 1 | 0.04 |
old.p2pool.observer | 1 | 0.02 | 0 | 0.00 |
xmr.solopool.org | 1 | 0.02 | 1 | 0.04 |
Sum | 5282 | 100.00 | 2606 | 100.01 |
Mining pools Nanopool, MoneroOcean, SupportXMR, and Hashvault (NOSH) could provide the requisite majority hashpower. In 2023, the four NOSH pools voluntarily changed their mining pool software configuration to speed up transaction confirmations when Monero community members requested the change. NOSH may be willing to enforce rolling DNS checkpoints because they have changed their mining operations in the past upon community request.
Below is a plot of all blocks (main chain and orphaned) mined by the NOSH mining pools during Qubic epoch 176. In nearly all 6-hour periods, NOSH mined the majority of blocks.

C3Pool did not change their mining pool software configuration to speed up transaction confirmations in 2023, but it may respond positively to rolling DNS checkpointing. Below, C3Pool is added to the NOSH miners. The share of mined blocks improves.

Plots of data including only main chain blocks are viewable by clicking on the expandable section below.
Methodology, information on data sources, and analysis code are viewable by clicking on the expandable section below.
Methodology, data sources, and code
We need each combination of main chain & orphan and Qubic & honest pool. The main chain blocks mined by Qubic and honest pools can be collected from a Monero node. Orphan blocks mined by honest pools can be collected by a Monero node that was running at the time. (The node would consider the blocks as main-chain blocks when they are first seen. After Qubic broadcasts a longer chain that orphans the blocks, they are considered orphaned blocks, but still saved by the node.)
Orphaned Qubic blocks are more difficult to collect because they are "late" and do not propagate throughout the network well. We (DataHoarder and Rucknium) believe that nearly all of them have been collected. The fact that the blocks are valid blocks meeting the PoW hash difficulty and pay the coinbase output to the Qubic wallet is proof that these are orphaned Qubic blocks.
Next, we need to identify which pools mined each block. monero-blocks
was used to collect mined block claims from the APIs of all major honest mining pools. The claims of honest mining pools are accepted at face value. With small probability, a mining pool might not claim a block it has mined because it is orphaned very soon after it is mined. In that case, the block's mining pool would be considered "Unknown". Blocks mined by solo miners or very small pools are also marked "Unknown". TODO: Post data from monero-blocks
.
Qubic has provided the view key of the wallet that receives its mining rewards for its epoch 176:
Primary address: 49heVqhSznN9eoatkooNJLHHsRV6XiitDeW4J92cgney8BFfuacZGmzSA3fRKEHooC7X9xzCP9VXN6uK7XrpoXF35FXfQCP
View Key: d761a707408f9693f9a453501dc04df6c92b82309a90f23884564dabfed70106
The view key is used to check which blocks were mined by Qubic, including main chain blocks and orphan block. TODO: explain cryptographic proofs.
Below is R code to reproduce the plots and table
# First install these packages:
# install.packages(c("data.table", "RCurl", "RJSONIO", "knitr"))
library(data.table)
restricted.rpc.url <- "http://127.0.0.1:18081"
# Assumes local node, but can be remote node. Just needs
# restricted or unrestricted RPC port.
qubic_proofs <- read.csv("qubic-block-proofs.csv", stringsAsFactors = FALSE)
data.table::setDT(qubic_proofs)
pool_blocks <- read.csv("pool-blocks.csv", stringsAsFactors = FALSE)
data.table::setDT(pool_blocks)
start_epoch <- 3487247 # as.integer(as.POSIXct("2025-08-28 00:00:00"))
end_epoch <- 3491983 # as.integer(as.POSIXct("2025-09-03 12:00:00"))
# By height
start_epoch.time <- as.integer(as.POSIXct("2025-08-28 00:00:00"))
end_epoch.time <- as.integer(as.POSIXct("2025-09-03 12:00:00"))
qubic_proofs <- qubic_proofs[Timestamp %between% c(start_epoch.time, end_epoch.time), ]
json.rpc <- function(restricted.rpc.url, method, params, handle = RCurl::getCurlHandle()) {
json.post <- RJSONIO::toJSON(
list(
jsonrpc = "2.0",
id = "0",
method = method,
params = params
)
)
RJSONIO::fromJSON(
RCurl::postForm(paste0(restricted.rpc.url, "/json_rpc"),
.opts = list(
userpwd = "",
postfields = json.post,
httpheader = c('Content-Type' = 'application/json', Accept = 'application/json')
),
curl = handle
), asText = TRUE
)
}
alt_blocks_hashes <- RJSONIO::fromJSON(
RCurl::postForm(paste0(restricted.rpc.url, "/get_alt_blocks_hashes"),
.opts = list(
httpheader = c('Content-Type' = 'application/json', Accept = 'application/json')
)
), asText = TRUE
)$blks_hashes
range.days <- unique(c(seq(start_epoch, end_epoch, by = 720), end_epoch))
# Must break up or get this error:
# "Too many block headers requested."
main_chain.headers <- list()
for (i in seq_len(length(range.days) - 1)) {
cat(range.days[i], "\n")
main_chain <- json.rpc(restricted.rpc.url, method = "get_block_headers_range",
params = list(start_height = range.days[i], end_height = range.days[i + 1]))$result$headers
main_chain <- lapply(main_chain, FUN = function(result) {
if ( as.integer(result$height) %between% c(start_epoch, end_epoch) ) {
return(data.table(height = as.integer(result$height), hash = result$hash,
timestamp = as.integer(result$timestamp)))
} else {
return(data.table(height = integer(0), hash = character(0), timestamp = integer(0)))
}
})
main_chain.headers[[i]] <- data.table::rbindlist(main_chain)
}
main_chain.headers <- data.table::rbindlist(main_chain.headers)
main_chain.headers <- unique(main_chain.headers)
main_chain.headers[, on_main_chain := TRUE]
handle <- RCurl::getCurlHandle()
# Need this to get the height and time stamp
alt_blocks.headers <- lapply(alt_blocks_hashes, FUN = function(hash) {
result <- json.rpc(restricted.rpc.url, method = "get_block_header_by_hash",
params = list(hash = hash), handle = handle)$result$block_header
stopifnot(length(result$height) > 0)
cat(result$height, " ")
if ( as.integer(result$height) %between% c(start_epoch, end_epoch) ) {
return(data.table(height = as.integer(result$height), hash = result$hash,
timestamp = as.integer(result$timestamp)))
} else {
return(data.table(height = integer(0), hash = character(0), timestamp = integer(0)))
}
})
alt_blocks.headers <- data.table::rbindlist(alt_blocks.headers)
alt_blocks.headers[, on_main_chain := FALSE]
all_blocks <- rbind(main_chain.headers, alt_blocks.headers)
data.table::setorder(all_blocks, height, timestamp)
stopifnot(all(all_blocks$height %between% c(start_epoch, end_epoch)))
all_blocks[, is_qubic := hash %in% qubic_proofs$Id]
all_blocks <- merge(all_blocks, pool_blocks[, .(Id, Pool)], all.x = TRUE, by.x = "hash", by.y = "Id")
data.table::setorder(all_blocks, height, timestamp)
all_blocks[(is_qubic), Pool := "qubic"]
all_blocks[is.na(Pool), Pool := "Unknown"]
all_blocks[, is_NOSH := Pool %in%
c("xmr.nanopool.org", "moneroocean.stream", "supportxmr.com", "monero.hashvault.pro")]
all_blocks[, is_NOSH_plus_C3Pool := Pool %in%
c("xmr.nanopool.org", "moneroocean.stream", "supportxmr.com", "monero.hashvault.pro",
"c3pool.org")]
plot.hashpower <- function(data, share.col.name, plot.title, ylim,
red.circle.criterion = expression(share > 0.5), legend.pos = "topleft",
hours.fine = 1, hours.coarse = 6) {
data <- copy(data)
data$share.bool <- data[, share.col.name, with = FALSE]
data[, timestamp := as.POSIXct(timestamp)]
data[, binned.fine := timestamp - as.numeric(timestamp) %% (60 * 60 * hours.fine) ]
data[, binned.coarse := timestamp - as.numeric(timestamp) %% (60 * 60 * hours.coarse) ]
data.fine <- data[, .(share = sum(share.bool / .N) ), by = "binned.fine"]
data.coarse <- data[, .(share = sum(share.bool / .N) ), by = "binned.coarse"]
plot(data.fine[, .(binned.fine, share)],
main = plot.title, ylim = ylim,
type = "l", col = "darkgray",
ylab = "", xlab = "", yaxt = "n", xaxt = "n")
lines(data.coarse[, .(binned.coarse, share)], lwd = 1.25)
points(data.fine[eval(red.circle.criterion), .(binned.fine, share)], col = "red")
abline(h = 0.5, lty = 2)
legend(legend.pos,
legend = c(paste(hours.fine, "hour"), paste(hours.coarse, "hours")),
col = c("darkgray", "black"),
lwd = c(1, 1.25),
title = "Aggregation interval")
axis(2, at = axTicks(side = 2), labels = paste0(100 * axTicks(side = 2), "%"))
axis(4, at = axTicks(side = 2), labels = paste0(100 * axTicks(side = 2), "%"))
axis.POSIXct(1, at = seq(min(data$timestamp), max(data$timestamp),
by = "days"), format = "%b %d", las = 3)
text(x = max(data$timestamp), y = ylim[1], pos = 2, cex = 0.8,
label = "github.com/Rucknium")
return(invisible(NULL))
}
png("qubic-share-all-blocks-2025-08-29.png", width = 720, height = 720)
par(cex = 1.5)
plot.hashpower(all_blocks, share.col.name = "is_qubic",
plot.title = "Qubic mined blocks share (main chain and orphans)", ylim = c(0, 1))
dev.off()
png("qubic-share-main-chain-2025-08-29.png", width = 720, height = 720)
par(cex = 1.5)
plot.hashpower(all_blocks[(on_main_chain), ], share.col.name = "is_qubic",
plot.title = "Qubic mined blocks share (main chain only)", ylim = c(0, 1))
dev.off()
png("NOSH-share-all-blocks-2025-08-29.png", width = 720, height = 720)
par(cex = 1.5)
plot.hashpower(all_blocks, share.col.name = "is_NOSH",
plot.title = "Mined blocks share (main chain and orphans) of\nNanopool, MoneroOcean, SupportXMR, & Hashvault",
ylim = c(0, 1),
red.circle.criterion = expression(share < 0.5), legend.pos = "bottomleft")
dev.off()
png("NOSH-share-main-chain-2025-08-29.png", width = 720, height = 720)
par(cex = 1.5)
plot.hashpower(all_blocks[(on_main_chain), ], share.col.name = "is_NOSH",
plot.title = "Mined blocks share (main chain only) of\nNanopool, MoneroOcean, SupportXMR, & Hashvault",
ylim = c(0, 1),
red.circle.criterion = expression(share < 0.5), legend.pos = "bottomleft")
dev.off()
png("NOSH-plus-C3Pool-share-all-blocks-2025-08-29.png", width = 720, height = 720)
par(cex = 1.5)
plot.hashpower(all_blocks, share.col.name = "is_NOSH_plus_C3Pool",
plot.title = "Mined blocks share (main chain and orphans) of\nNanopool, MoneroOcean, SupportXMR, & Hashvault + C3Pool",
ylim = c(0, 1),
red.circle.criterion = expression(share < 0.5), legend.pos = "bottomleft")
dev.off()
png("NOSH-plus-C3Pool-share-main-chain-2025-08-29.png", width = 720, height = 720)
par(cex = 1.5)
plot.hashpower(all_blocks[(on_main_chain), ], share.col.name = "is_NOSH_plus_C3Pool",
plot.title = "Mined blocks share (main chain only) of\nNanopool, MoneroOcean, SupportXMR, & Hashvault + C3Pool",
ylim = c(0, 1),
red.circle.criterion = expression(share < 0.5), legend.pos = "bottomleft")
dev.off()
marathons.start <- as.integer(as.POSIXct(c(
"2025-08-28 12:00:00",
"2025-08-30 12:00:00",
"2025-09-01 12:00:00"
)))
marathons.end <- as.integer(as.POSIXct(c(
"2025-08-29 12:00:00",
"2025-08-31 12:00:00",
"2025-09-02 12:00:00"
)))
all_blocks[, Pool := factor(Pool)]
# Set as factor so all pools appear even if absent during marathons
pools.table.all <- all_blocks[, sort(table(Pool), decreasing = TRUE)]
pools.table.marathon <- all_blocks[
data.table::inrange(timestamp, marathons.start, marathons.end),
table(Pool)]
pools.table.marathon <- pools.table.marathon[
match(names(pools.table.all), names(pools.table.marathon))]
# Put in correct order
pools.table <- cbind(
`all time <br/> blocks` = pools.table.all,
`all time <br/> share (%)` = round(100 * prop.table(pools.table.all), 2),
`marathon <br/> blocks` = pools.table.marathon,
`marathon <br/> share (%)` = round(100 * prop.table(pools.table.marathon), 2))
pools.table <- addmargins(pools.table, margin = 1)
pools.table <- knitr::kable(pools.table, format = "pipe")
cat(pools.table, sep = "\n")
To-do list
If temporary rolling DNS checkpoints are to be deployed, here are some suggested tasks to be completed:
-
Final testing on testnet and mainnet. (Live experiments of testnet can be viewed at
https://testnetnode1.moneroconsensus.info/
,https://testnetnode2.moneroconsensus.info/
,https://testnetnode3.moneroconsensus.info/
, andhttps://testnetnode4.moneroconsensus.info/
.) -
Write production-ready code to poll nodes and update the DNS checkpoint records.
-
"Smooth" the handling of DNS checkpoints by nodes. For example, the frequency of fetching the checkpoints should be reduced from one hour to a few minutes.
-
Create a new point release of the Monero software. Decide if the checkpoints should be enabled or disabled by default.
-
Directly contact mining pools and other entities that handle a large amount of XMR. Entities that handle a small amount of XMR can be contacted, too.
-
Create a blog post on getmonero.org
-
Use the Monero Core Team's email broadcast list to reach other parties.
This proposal is subject to change.