Year 2038 time bug #21356

issue IamLupo opened this issue on March 4, 2021
  1. IamLupo commented at 4:03 AM on March 4, 2021: none

    In 2038 the epoch time run out of bits and that makes it need more bits to represent the correct datetime.

    https://en.wikipedia.org/wiki/Year_2038_problem

  2. IamLupo added the label Bug on Mar 4, 2021
  3. 7hacker commented at 4:20 AM on March 4, 2021: none

    I believe bitcoin uses unsigned 32-bit time types so the problem if any is delayed to 2106

  4. IamLupo commented at 4:29 AM on March 4, 2021: none

    I believe bitcoin uses unsigned 32-bit time types so the problem if any is delayed to 2106

    Well the hole project uses int64_t GetTime(): https://github.com/bitcoin/bitcoin/blob/master/src/util/time.cpp#L26

    If you read this function you see he uses the time() function through the OS to get the epoch time.

    And if i read the specifications from linux about this call: https://man7.org/linux/man-pages/man2/time.2.html

    On Linux, a call to time() with tloc specified as NULL cannot fail with the error EOVERFLOW, even on ABIs where time_t is a signed 32-bit integer and the clock ticks past the time 2**31 (2038-01-19 03:14:08 UTC, ignoring leap seconds). (POSIX.1 permits, but does not require, the EOVERFLOW error in the case where the seconds since the Epoch will not fit in time_t.) Instead, the behavior on Linux is undefined when the system time is out of the time_t range. Applications intended to run after 2038 should use ABIs with time_t wider than 32 bits. Basicly i dont see in the project that it uses ABI's with time_t.

    Also if there is a unsigned 32-bit declaired then that doesnt mean the time() function will also generate this. In this case its a time_t value. That is treated different.

  5. 7hacker commented at 4:42 AM on March 4, 2021: none

    That call appears deprecated. See util/time.h

    /**
     * DEPRECATED
     * Use either GetSystemTimeInSeconds (not mockable) or GetTime<T> (mockable)
     */
    int64_t GetTime();
    
  6. IamLupo commented at 5:39 AM on March 4, 2021: none

    I also read that this apperntly will get fixed by itself over time. Like basicly its not a big issue anymore. Just a recompile over time.

    If you write C++ code using chrono::system_clock or chrono::steady_clock now and update ESP-IDF once the 2038 problem is solved, the code should keep working after 2038 with just a recompile against the newer libc & libstdc++.

    https://github.com/espressif/esp-idf/issues/3860

  7. laanwj added the label Brainstorming on Mar 4, 2021
  8. laanwj commented at 9:39 AM on March 4, 2021: member

    It would be good to have a test for this. (e.g. what happens to a running bitcoind at the epoch, starting one after it, etc)

  9. laanwj added the label Tests on Mar 4, 2021
  10. maflcko commented at 4:02 PM on March 5, 2021: member

    Side-note: It is also possible to run the tests under a specific time. E.g. faketime -f '+3y' ./test/functional/wallet_disable.py.

  11. IamLupo commented at 3:21 AM on March 6, 2021: none

    I made a small script:

    #include <iostream>
    #include <chrono>
    #include <cstdint>
    #include <cassert>
    
    using namespace std;
    
    int total_bits(uint64_t epoch)
    {
    	int i = 0;
    	
    	while(epoch != 0)
    	{
    		epoch /= 2;
    		i++;
    	}
    	
    	return i;
    }
    
    template <typename T>
    static T GetSystemTime()
    {
        const auto now = std::chrono::duration_cast<T>(std::chrono::system_clock::now().time_since_epoch());
        assert(now.count() > 0);
        return now;
    }
    
    int main()
    {
    	int64_t epoch;
    	
    	cout << "set date '2038-01-01'" << endl;
    	system("date +%Y%m%d -s '20380101'");
    	epoch = int64_t{GetSystemTime<std::chrono::seconds>().count()};
    	cout << "epoch  = " << epoch << " (bits: " << total_bits(epoch) << ")" << endl;
    	
    	system("date +%Y%m%d -s '20380201'");
    	cout << "set date '2038-02-01'" << endl;
    	epoch = int64_t{GetSystemTime<std::chrono::seconds>().count()};
    	cout << "epoch  = " << epoch << " (bits: " << total_bits(epoch) << ")" << endl;
    	
    	system("date +%Y%m%d -s '21060201'");
    	cout << "set date '2106-02-01'" << endl;
    	epoch = int64_t{GetSystemTime<std::chrono::seconds>().count()};
    	cout << "epoch  = " << epoch << " (bits: " << total_bits(epoch) << ")" << endl;
    	
    	system("date +%Y%m%d -s '21060301'");
    	cout << "set date '2106-03-01'" << endl;
    	epoch = int64_t{GetSystemTime<std::chrono::seconds>().count()};
    	cout << "epoch  = " << epoch << " (bits: " << total_bits(epoch) << ")" << endl;
    	
    	return 0;
    }
    

    Result:

    iamlupo@iamlupo-VM2:/var/www/EpochTime2038$ sudo ./main
    set date '2038-01-01'
    20380101
    epoch  = 2145913200 (bits: 31)
    20380201
    set date '2038-02-01'
    epoch  = 2148591600 (bits: 32)
    21060201
    set date '2106-02-01'
    epoch  = 4294422000 (bits: 32)
    21060301
    set date '2106-03-01'
    epoch  = 4296841200 (bits: 33)
    

    Basicly this tells me the std:chrono works correctly on my linux vm.

    iamlupo@iamlupo-VM2:/var/www/EpochTime2038$ uname -a
    Linux iamlupo-VM2 5.8.0-43-generic [#49](/bitcoin-bitcoin/49/)-Ubuntu SMP Fri Feb 5 03:01:28 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
    iamlupo@iamlupo-VM2:/var/www/EpochTime2038$ g++ --version
    g++ (Ubuntu 10.2.0-13ubuntu1) 10.2.0
    Copyright (C) 2020 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions.  There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    

    This should be tested between different compilers and os's to figure out if all of them has patched std::chrono correctly and since what version its been patched. That you can avoid old compilers.

    Second will be that all parts where a time is been used needs to be patched fron unsigned int/uint32_t to int64_t.

    As example here: https://github.com/bitcoin/bitcoin/blob/4a540683ec40393d6369da1a9e02e45614db936d/src/primitives/block.h#L27

  12. garlonicon commented at 3:35 AM on October 14, 2021: none

    Patching block.h will affect mining, because if you replace uint32_t nTime with something wider than 32-bit value, then patched block headers will be backward-incompatible. That would mean a hard-fork.

  13. carnhofdaki commented at 7:53 AM on May 15, 2022: contributor

    If this nTime was used merely for computing the difficulty, it can stay uint32_t and the only thing requiring change would be something like adding a condition if block_time_2016_back > block_time_now { do_simple_magic; } which would normalize (offset) both of the two values (the overflown lower and before-overflow bigger, rendering them like they are supposed to be) and carry on with the difficulty computation like normal. Yes, it would require some minimum Bitcoin Core version, but the sooner it gets implemented, the sooner are compatible nodes running. The fix can be also easily back-ported to older stable branches.

    Is my thinking right that the the related value of uint32_t type stored in each block can be (or already is?) used merely in difficulty computation so that everything can stay as it is and no hard-fork is needed?

  14. maflcko removed the label Bug on Oct 5, 2022
  15. fanquake closed this on Oct 21, 2022

  16. sidhujag referenced this in commit ac491b0e47 on Oct 23, 2022
  17. bitcoin locked this on Oct 21, 2023

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: 2026-04-24 09:15 UTC

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