qt: Fix text display when state of prune button is changed #17035

pull emilengler wants to merge 1 commits into bitcoin:master from emilengler:2019-10-qt-intro-prune-text-update-fix changing 2 files +44 −23
  1. emilengler commented at 0:32 am on October 3, 2019: contributor

    Currently there is this bug when enabling and disabling pruning: The intro screen isn’t updated when something is changing.

    This fixes the storage message in the top. Here is a video of this fix Unfortunately it doesn’t fix the required storage amount, I will be working on this next. Done! This needs to get into 0.19 as well

  2. fanquake added the label GUI on Oct 3, 2019
  3. MarcoFalke added this to the milestone 0.19.0 on Oct 3, 2019
  4. MarcoFalke added the label Needs backport on Oct 3, 2019
  5. MarcoFalke added the label Needs gitian build on Oct 3, 2019
  6. in src/qt/intro.cpp:360 in fa149cb03f outdated
    355+        QObject::tr("The wallet will also be stored in this directory.")
    356+    );
    357+    // Reset GUI width and update storage warning if neccesary
    358+    this->adjustSize();
    359+}
    360+void Intro::on_prune_stateChanged(int arg1)
    


    promag commented at 11:10 pm on October 3, 2019:

    IMO we should move away from these dynamic connections. My suggestion is to move to the constructor:

    0connect(ui->prune, &QCheckBox::stateChanged, [this](int state) {
    1    setSizeWarningLabel(/* isPruning = */ state == 2, m_blockchain_size, m_chain_state_size);
    2});
    

    emilengler commented at 1:28 am on October 4, 2019:
    I agree with you but this is a bigger change and should be a done in a separate PR because this one is intended to be a fix, not a replacement

    promag commented at 6:44 am on October 4, 2019:
    Huh? Bigger? Please unresolved this.
  7. in src/qt/intro.cpp:336 in fa149cb03f outdated
    331@@ -349,3 +332,39 @@ QString Intro::getPathToCheck()
    332     mutex.unlock();
    333     return retval;
    334 }
    335+
    336+void Intro::setSizeWarningLabel(bool isPruning, uint64_t blockchain_size, uint64_t chain_state_size)
    


    promag commented at 11:11 pm on October 3, 2019:
    Why have the 2 last arguments if they are always the same?

    emilengler commented at 1:29 am on October 4, 2019:
    What do you mean? IIRC the blockchain size and chainstate size are two different things

    promag commented at 6:45 am on October 4, 2019:
    Instead of arguments, you have access to m_blockchain_size and the other.

    emilengler commented at 8:20 pm on October 4, 2019:
    Are you sure? IIRC it was only available in the contructor but I could be wrong, thats why I used this method


    emilengler commented at 9:35 pm on October 4, 2019:
    Done, don’t know why it didn’t worked last time
  8. promag commented at 11:11 pm on October 3, 2019: member
    Concept ACK.
  9. DrahtBot removed the label Needs gitian build on Oct 4, 2019
  10. MarcoFalke deleted a comment on Oct 4, 2019
  11. in src/qt/intro.cpp:339 in 51abf462d7 outdated
    334@@ -349,3 +335,28 @@ QString Intro::getPathToCheck()
    335     mutex.unlock();
    336     return retval;
    337 }
    338+
    339+void Intro::setSizeWarningLabel(bool isPruning)
    


    hebasto commented at 8:19 am on October 5, 2019:
    Naming: s/isPruning/is_pruned/

    emilengler commented at 11:29 pm on October 5, 2019:
    d35cfde6429dafe576f7490dd3fd880e70994fde
  12. in src/qt/intro.cpp:318 in 51abf462d7 outdated
    304@@ -322,6 +305,9 @@ void Intro::startThread()
    305 
    306     connect(executor, &FreespaceChecker::reply, this, &Intro::setStatus);
    307     connect(this, &Intro::requestCheck, executor, &FreespaceChecker::check);
    308+    connect(ui->prune, &QCheckBox::stateChanged, [this](int state) {
    309+        setSizeWarningLabel(state == 2);
    310+    });
    


    hebasto commented at 8:22 am on October 5, 2019:

    This does not work if “-prune” is set:

    0src/qt/bitcoin-qt -prune=3000
    

    Screenshot from 2019-10-05 11-15-01


    emilengler commented at 11:44 pm on October 5, 2019:
    255ab91b31ca3848654a63fcc384b83913bd36e8
  13. hebasto commented at 8:23 am on October 5, 2019: member

    Unfortunately it doesn’t fix the required storage amount, I will be working on this next.

    I tend to Approach NACK. The full problem solution, with the required storage amount is fixed, is welcome.

  14. in src/qt/intro.cpp:309 in 51abf462d7 outdated
    304@@ -322,6 +305,9 @@ void Intro::startThread()
    305 
    306     connect(executor, &FreespaceChecker::reply, this, &Intro::setStatus);
    307     connect(this, &Intro::requestCheck, executor, &FreespaceChecker::check);
    308+    connect(ui->prune, &QCheckBox::stateChanged, [this](int state) {
    309+        setSizeWarningLabel(state == 2);
    


    hebasto commented at 8:52 am on October 5, 2019:

    emilengler commented at 9:29 pm on October 5, 2019:
    Sure, I will work on your suggested changes now

    emilengler commented at 11:44 pm on October 5, 2019:
    ea8ef9cde7b28fda72aa2313c92e6396e700e357
  15. emilengler commented at 4:50 am on October 6, 2019: contributor
    @hebasto Done
  16. emilengler requested review from promag on Oct 6, 2019
  17. in src/qt/intro.cpp:134 in f9b82406cb outdated
    129@@ -130,30 +130,16 @@ Intro::Intro(QWidget *parent, uint64_t blockchain_size, uint64_t chain_state_siz
    130     );
    131     ui->lblExplanation2->setText(ui->lblExplanation2->text().arg(PACKAGE_NAME));
    132 
    133+    // This is used to default set the prune box if not enough storage is available
    134+    pruneWasNotSet = true;
    


    hebasto commented at 1:49 pm on October 6, 2019:

    Developer Notes - C++ data structures:

    Initialize all non-static class members where they are defined.


    emilengler commented at 4:55 pm on October 6, 2019:
    430985b1ab5639f1a7440705bc857fd9f7b45aaf
  18. in src/qt/intro.h:72 in f9b82406cb outdated
    68@@ -69,10 +69,12 @@ private Q_SLOTS:
    69     QString pathToCheck;
    70     uint64_t m_blockchain_size;
    71     uint64_t m_chain_state_size;
    72+    bool pruneWasNotSet;
    


    hebasto commented at 1:52 pm on October 6, 2019:

    Developer Notes - Coding Style (C++):

    • Variable (including function arguments) and namespace names are all lowercase and may use _ to separate words (snake_case).
      • Class member variables have a m_ prefix.

    Also a negated name is confusing. Could it be positive?


    emilengler commented at 4:06 pm on October 6, 2019:
    Yes, do this now

    promag commented at 4:31 pm on October 6, 2019:
    You don’t have to add this, just use ui->prune->isChecked()?

    emilengler commented at 4:57 pm on October 6, 2019:
    @promag I do, it is there to check if it touched by the user. If the user has used the checkbox once, this bool will go to false
  19. hebasto changes_requested
  20. hebasto commented at 2:23 pm on October 6, 2019: member

    It seems commits need to be squashed in two ones:

    • introduce pruneWasNotSet member
    • all the rest
  21. in src/qt/intro.h:77 in f9b82406cb outdated
    68@@ -69,10 +69,12 @@ private Q_SLOTS:
    69     QString pathToCheck;
    70     uint64_t m_blockchain_size;
    71     uint64_t m_chain_state_size;
    72+    bool pruneWasNotSet;
    73 
    74     void startThread();
    75     void checkPath(const QString &dataDir);
    76     QString getPathToCheck();
    77+    void setSizeWarningLabel(bool is_pruned);
    


    hebasto commented at 2:30 pm on October 6, 2019:
    ui->prune->isChecked() is accessible here. bool is_pruned parameter seems redundant.

    emilengler commented at 4:08 pm on October 6, 2019:
    Oh yeah, it is just a remaining of the first version of this patch
  22. emilengler commented at 5:07 pm on October 6, 2019: contributor
    Squashing done
  23. in src/qt/intro.h:72 in 727f86f3a7 outdated
    68@@ -69,10 +69,12 @@ private Q_SLOTS:
    69     QString pathToCheck;
    70     uint64_t m_blockchain_size;
    71     uint64_t m_chain_state_size;
    72+    bool pruneSet = false;
    


    hebasto commented at 5:39 pm on October 6, 2019:
    0    bool m_prune_set{false};
    

    emilengler commented at 0:01 am on October 7, 2019:
    Done
  24. hebasto commented at 5:52 pm on October 6, 2019: member

    Approach ACK 727f86f3a75051daaeb81cb3984574c06eb62449, tested on Linux Mint 19.2.

    Nit: #17035 (review)

    Commit 727f86f3a75051daaeb81cb3984574c06eb62449 message “… It also removes a useless boolean added before” is a hint ;) Why to introduce a variable in a commit to remove it in the next one?

    It does not work if disk space is insufficient and -prune=0 or -noprune is passed as an command-line option (it could be leave for the following PRs, IMO).

    EDIT: The “qt: Update the required space message when the prune box is checked” commit (727f86f3a75051daaeb81cb3984574c06eb62449) should deal with both ui->prune->setChecked() and pruneTarget. The latter is ignored now ;)

  25. emilengler commented at 0:03 am on October 7, 2019: contributor

    @hebasto

    EDIT: The “qt: Update the required space message when the prune box is checked” commit (727f86f) should deal with both ui->prune->setChecked() and pruneTarget. The latter is ignored now ;)

    I removed this message completely because a variable rename wasn’t the real point of this commit

  26. hebasto commented at 5:08 pm on October 7, 2019: member

    As I wrote:

    … should deal with both ui->prune->setChecked() and pruneTarget. The latter is ignored now ;)

    More details are in the screenshot: Screenshot from 2019-10-07 20-02-26

    If pruneTarget is set to 2 GB, amount 3 GB of stored data is obviously incorrect.

  27. emilengler commented at 7:56 pm on October 7, 2019: contributor
    @hebasto Sure, what is with the chainstate?
  28. jonasschnelli commented at 9:52 am on October 8, 2019: contributor

    Tested a bit, something seems incorrect. If I start with -prune=2000 I’ll get a text “Approx 7GB” while when I start without -prune, I’ll get 4GB as size warning.

    I know this is not the place to discuss that, but why have we chosen 2GB as prune target instead of the minimum of 550MB?

  29. promag commented at 9:42 am on October 9, 2019: member

    If I start with -prune=2000 I’ll get a text “Approx 7GB” while when I start without -prune, I’ll get 4GB as size warning.

    I confirm this behavior, setting -prune on command line and setting in the UI should give the same approximation?

  30. emilengler commented at 3:28 pm on October 9, 2019: contributor
    @promag @jonasschnelli I might xe wrong but this is a feature and not a bug. It is because the chainstate size is also being incremented to the required amount of storage. When you prune with 550MB your bitcoin folder will still be around 4-5GB huge and not 550MB
  31. emilengler commented at 3:29 pm on October 9, 2019: contributor

    See Line 361:

    0requiredSpace += m_chain_state_size;
    
  32. emilengler commented at 3:29 pm on October 14, 2019: contributor
    Push
  33. hebasto commented at 8:09 am on October 15, 2019: member

    Tested this PR on top of the current master (dcc640811c8c1a0678a3c3726cb7df14f0a60250).


    0src/qt/bitcoin-qt -chain=test -prune=2000
    

    Screenshot from 2019-10-15 10-56-44

    That is correct, as 5 GB is a sum of pruned blockchain size (2 GB) and chainstate size (3 GB).


    0src/qt/bitcoin-qt -chain=test
    

    Screenshot from 2019-10-15 11-00-36

    That is wrong, as 2 GB is the default pruned blockchain size, and chainstate size is skipped. @promag

    I confirm this behavior, setting -prune on command line and setting in the UI should give the same approximation?

    Yes, they should.

  34. emilengler commented at 2:06 pm on October 15, 2019: contributor
    Ok, will work on specific chain selection now
  35. emilengler commented at 2:14 pm on October 16, 2019: contributor
    @hebasto Fixed, squashed and ready for merge
  36. hebasto commented at 11:08 am on October 17, 2019: member

    Tested 9ab3876b43d45e8d742adcad996537b2dcbacff3 on Linux Mint 19.2:

    0$ git log -1
    1commit 7fabf14f9556c6d56219f9c835d78a458d862ad5 (HEAD -> pr17035-20191017)
    2Merge: 46d6930f8 9ab3876b4
    3Author: Emil Engler <me@emilengler.com>
    4Date:   Wed Oct 16 21:36:43 2019 +0000
    5
    6    Merge 9ab3876b43d45e8d742adcad996537b2dcbacff3 into 46d6930f8c7ba7cbcd7d86dd5d0117642fcbc819
    7
    8$ src/qt/bitcoin-qt -prune=42000
    

    Screenshot from 2019-10-17 13-58-44-1

  37. emilengler commented at 11:38 am on October 17, 2019: contributor
    @hebasto Fixed and tested heavily
  38. in src/qt/intro.cpp:139 in 3665d9f6e3 outdated
    154-        tr("%1 will download and store a copy of the Bitcoin block chain.").arg(PACKAGE_NAME) + " " +
    155-        storageRequiresMsg.arg(requiredSpace) + " " +
    156-        tr("The wallet will also be stored in this directory.")
    157-    );
    158+    setSizeWarningLabel();
    159+    pruneTarget = pruneTarget ? pruneTarget / 1000 : 2;
    


    hebasto commented at 12:10 pm on October 17, 2019:

    pruneTarget variable holds semantically different values at different points: (1) pruned target in mebibytes (MiB), and (2) pruned target in gigabytes (10^6 bytes). IMO, such semantic overloading should be avoided.

    Please note, that the binary coefficient is used in master branch: https://github.com/bitcoin/bitcoin/blob/46d6930f8c7ba7cbcd7d86dd5d0117642fcbc819/src/qt/intro.cpp#L142

    -prune option also uses a binary prefix for its value – MiB.


    emilengler commented at 3:11 pm on October 17, 2019:
    IIRC it still uses megabyte internally but only gigabyte for the display in the GUI because otherwise it could get strange looking

    emilengler commented at 3:13 pm on October 17, 2019:
    ACK on the overloading problem. I will multiply with 1000 instead

    hebasto commented at 4:27 pm on October 17, 2019:
    Eh? Why is this comment marked as resolved?
  39. in src/qt/intro.h:73 in 3665d9f6e3 outdated
    68@@ -69,10 +69,13 @@ private Q_SLOTS:
    69     QString pathToCheck;
    70     uint64_t m_blockchain_size;
    71     uint64_t m_chain_state_size;
    72+    bool m_prune_set{false};
    73+    uint64_t pruneTarget;
    


    hebasto commented at 12:14 pm on October 17, 2019:
    Could be renamed: s/pruneTarget/m_prune_target/ and initialized to 0 by default?

    emilengler commented at 2:57 pm on October 17, 2019:
    ACK
  40. hebasto changes_requested
  41. emilengler commented at 4:13 pm on October 17, 2019: contributor
    @hebasto Updated
  42. in src/qt/intro.cpp:352 in 6ed7f35883 outdated
    348@@ -349,7 +349,7 @@ void Intro::setSizeWarningLabel()
    349     requiredSpace = m_blockchain_size;
    350     QString storageRequiresMsg = QObject::tr("At least %1 GB of data will be stored in this directory, and it will grow over time.");
    351     if (ui->prune->isChecked()) {
    352-        uint64_t prunedGBs = std::ceil(pruneTarget * 1024 * 1024.0 / GB_BYTES);
    353+        uint64_t prunedGBs = std::ceil(m_prune_target * 1000 * 1000 / GB_BYTES);
    



    emilengler commented at 5:51 pm on October 17, 2019:
    Ok, then I will change everything to Mebibyte

    Sjors commented at 2:39 pm on October 22, 2019:
    MiB is indeed consistent with other places, but you should also change uint64_t GB_BYTES{1000000000} to uint64_t GiB_BYTES{1073741824}. But that impacts the settings screen. Probably better to leave this change for another PR.

    ryanofsky commented at 3:37 pm on October 22, 2019:

    Instead of doing ad-hoc calculations everywhere, I’d strongly encourage using the following well-defined PruneGBtoMiB and PruneMiBtoGB functions (from #15936) for this:

    https://github.com/bitcoin/bitcoin/blob/0ab41dd770c1f13983df528b111cfc8a51fe016a/src/qt/optionsmodel.h#L24-L25

    This would increase readability and prevent bugs and let us be sure when we migrate this setting out of qsettings that round trips between units work will correctly (e.g. 3GB -> 2861 MiB -> 3GB not 2GB)


    ryanofsky commented at 3:51 pm on October 22, 2019:

    MiB is indeed consistent with other places, but you should also change uint64_t GB_BYTES{1000000000} to uint64_t GiB_BYTES{1073741824}. But that impacts the settings screen. Probably better to leave this change for another PR.

    I’m not sure what the goal of this change would be, and would definitely think it should be done as separately from this PR. The existing conversions in this PR as of f3108a01a03a937fc3d31f007caa69e1f95f182f look correct to me, and I’d expect normal users to be more familiar with GB than GiB. Also if a user sees a GiB value and mistakenly interprets it as a GB value their node will use more storage than expected and maybe lead to problems.


    EDIT: f3108a01a03a937fc3d31f007caa69e1f95f182f does actually have some buggy conversions I missed the first pass, see #17035#pullrequestreview-305331256, but this tangential to the point about wanting to stick to GB instead of switching to GiB.

  43. emilengler commented at 5:55 pm on October 17, 2019: contributor
    @hebasto Fixed, however it will now round down if you prune, this should be correct behavior because it’s now a slightly different unit
  44. in src/qt/intro.cpp:361 in 66050ef7f9 outdated
    356+        }
    357+        Intro::ui->lblExplanation3->setVisible(true);
    358+    } else {
    359+        Intro::ui->lblExplanation3->setVisible(false);
    360+    }
    361+    requiredSpace += m_chain_state_size;
    


    Sjors commented at 2:25 pm on October 22, 2019:
    Move to start of function, otherwise storageRequiresMsg will have the wrong amount.

    emilengler commented at 2:57 pm on October 22, 2019:
    Nope, see L352-354

    Sjors commented at 4:24 pm on October 22, 2019:
    Not sure what’s to see there? Everywhere it says 3 GB it should say 5 GB; 2 GB pruning + 3 GB chainstate (m_assumed_chain_state_size).

    Sjors commented at 4:29 pm on October 22, 2019:
    This has never been a very precise number because 3 GB more or less didn’t matter, but with pruning we should show the correct numbers.

    emilengler commented at 7:44 pm on October 22, 2019:
    Oh now I understand what you mean. Will work on that tomorrow

    emilengler commented at 2:40 pm on October 23, 2019:
    @ryanofsky These functions are not existent in my branch. Should I copy them?

    ryanofsky commented at 3:01 pm on October 23, 2019:

    @ryanofsky These functions are not existent in my branch. Should I copy them?

    Yes, that’s why I linked to my branch.

  45. Sjors changes_requested
  46. Sjors commented at 2:27 pm on October 22, 2019: member

    Concept ACK. I don’t think the original behavior is a bug, but this PR does make things more clear. The intro dialog explained how much space is needed without pruning, and then offers to reduce that if you do prune. It’s nice that it now updates the amounts and colors.

    “GB needed for full chain” needs to be " GB needed for pruned chain" when prune is checked.

    I suggest quashing these commits. Perhaps start with a commit that introduces setSizeWarningLabel and adds member variables, but doesn’t change behavior. That way it’s more clear what behavior you’re changing.

  47. qt: Fix text display when state of prune button is changed
    This fixes the bug of the required amount of storage in the intro screen
    f3108a01a0
  48. emilengler commented at 3:02 pm on October 22, 2019: contributor
    Squashed
  49. in src/qt/intro.cpp:139 in f3108a01a0
    155-        tr("%1 will download and store a copy of the Bitcoin block chain.").arg(PACKAGE_NAME) + " " +
    156-        storageRequiresMsg.arg(requiredSpace) + " " +
    157-        tr("The wallet will also be stored in this directory.")
    158-    );
    159+    setSizeWarningLabel();
    160+    m_prune_target = m_prune_target ? m_prune_target / 1024 : 2;
    


    ryanofsky commented at 4:09 pm on October 22, 2019:

    This is doing a conversion from MiB to GiB not GB. Also it is rounding down instead of up, which means round trips between two units are not stable. (Maybe not a problem for this PR since it is a one-time conversion, but definitely a problem for #12833 and #15936 which need consistent repeatable conversions.)

    Would suggest:

    • Renaming this variable from m_prune_target to m_prune_target_gb to be clear about units it is supposed to contain.
    • Switching a temporary int64_t prune_target_mib variable above to hold the -prune value.
    • Changing this line to m_prune_target_gb = prune_target_mib ? PruneMiBtoGB(prune_target_mib) : 2 using the PruneGBtoMiB function below (orignally from #15936)

    https://github.com/bitcoin/bitcoin/blob/0ab41dd770c1f13983df528b111cfc8a51fe016a/src/qt/optionsmodel.h#L24-L25

  50. in src/qt/intro.cpp:353 in f3108a01a0
    348+{
    349+    requiredSpace = m_blockchain_size;
    350+    QString storageRequiresMsg = QObject::tr("At least %1 GB of data will be stored in this directory, and it will grow over time.");
    351+    if (ui->prune->isChecked()) {
    352+        uint64_t prunedGBs = std::ceil(m_prune_target * 1024 * 1024 / GB_BYTES);
    353+        if (prunedGBs <= requiredSpace) {
    


    Sjors commented at 4:26 pm on October 22, 2019:
    This should check prunedGBs + m_chain_state_size <= requiredSpace (assuming you add m_chain_state_size to requiredSpace earlier).
  51. in src/qt/intro.cpp:352 in f3108a01a0
    347+void Intro::setSizeWarningLabel()
    348+{
    349+    requiredSpace = m_blockchain_size;
    350+    QString storageRequiresMsg = QObject::tr("At least %1 GB of data will be stored in this directory, and it will grow over time.");
    351+    if (ui->prune->isChecked()) {
    352+        uint64_t prunedGBs = std::ceil(m_prune_target * 1024 * 1024 / GB_BYTES);
    


    ryanofsky commented at 4:31 pm on October 22, 2019:
    Maybe I’m misunderstanding the flow, but it seems like there a bug here. This code is trying to convert m_prune_target from mib to gb when m_prune_target would already contain gib after running the code in the constructor.
  52. emilengler commented at 7:45 pm on October 22, 2019: contributor
    @Sjors @ryanofsky Will patch your changes into the code tomorrow. So this can finally get merged… Thanks for your reviews!
  53. ryanofsky commented at 8:17 pm on October 22, 2019: member

    So this can finally get merged

    Most of the reviews on the high-priority list are older than this, for some perspective: https://github.com/bitcoin/bitcoin/projects/8. More reviewers -> faster merges, of course.

  54. Sjors commented at 8:25 am on October 23, 2019: member
    I don’t think it’s necessary to hold back the next release candidate for this. It can go into v0.19.1 if it doesn’t make the cut.
  55. emilengler commented at 1:09 pm on October 23, 2019: contributor
    @Sjors There is still one and a half week, thats probably enough time
  56. Sjors commented at 1:26 pm on October 23, 2019: member
    @emilengler there’s no official timetable. Usually we decide during the IRC meeting to ship a new release candidate when it looks like the most important bugs are squashed. Then we wait another week or so, and do another release candidate if new bugs were found. Otherwise the last candidate becomes the final version.
  57. laanwj commented at 11:40 am on October 24, 2019: member
    I don’t regard this as a blocker for the release either. IMO we’ll cut rc2 without this. It would be nice to have, but it seems to be slightly controversial whether this is the right solution, has unaddressed comments, and it has garnered no ACKs yet.
  58. laanwj removed this from the milestone 0.19.0 on Oct 25, 2019
  59. laanwj added this to the milestone 0.19.1 on Oct 25, 2019
  60. emilengler closed this on Nov 11, 2019

  61. emilengler deleted the branch on Nov 11, 2019
  62. fanquake removed the label Needs backport (0.19) on Nov 14, 2019
  63. fanquake removed this from the milestone 0.19.1 on Nov 14, 2019
  64. jonasschnelli referenced this in commit b89f2d0599 on Jan 27, 2020
  65. sidhujag referenced this in commit c928853f90 on Feb 1, 2020
  66. sidhujag referenced this in commit 30f59ff225 on Nov 10, 2020
  67. Fabcien referenced this in commit fbd564ae6a on Jan 14, 2021
  68. MarcoFalke locked this on Dec 16, 2021

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-12-18 18:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me