Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Year 2038 time bug #21356

Closed
IamLupo opened this issue Mar 4, 2021 · 9 comments · Fixed by #26259
Closed

Year 2038 time bug #21356

IamLupo opened this issue Mar 4, 2021 · 9 comments · Fixed by #26259

Comments

@IamLupo
Copy link

IamLupo commented Mar 4, 2021

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

@IamLupo IamLupo added the Bug label Mar 4, 2021
@7hacker
Copy link

7hacker commented Mar 4, 2021

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

@IamLupo
Copy link
Author

IamLupo commented Mar 4, 2021

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.

@7hacker
Copy link

7hacker commented Mar 4, 2021

That call appears deprecated. See util/time.h

/**
 * DEPRECATED
 * Use either GetSystemTimeInSeconds (not mockable) or GetTime<T> (mockable)
 */
int64_t GetTime();

@IamLupo
Copy link
Author

IamLupo commented Mar 4, 2021

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++.

espressif/esp-idf#3860

@laanwj
Copy link
Member

laanwj commented Mar 4, 2021

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)

@maflcko
Copy link
Member

maflcko commented Mar 5, 2021

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

@IamLupo
Copy link
Author

IamLupo commented Mar 6, 2021

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-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:

uint32_t nTime;

@garlonicon
Copy link

garlonicon commented Oct 14, 2021

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.

@carnhofdaki
Copy link
Contributor

carnhofdaki commented May 15, 2022

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?

@maflcko maflcko removed the Bug label Oct 5, 2022
sidhujag pushed a commit to syscoin/syscoin that referenced this issue Oct 23, 2022
fafc96a test: Test year 2106 block timestamps (MacroFake)

Pull request description:

  Alternative to bitcoin#21362 that closes bitcoin#21356

ACKs for top commit:
  Sjors:
    utACK fafc96a

Tree-SHA512: 196d98f42d6f7f0222312b7bd1c68b3bd30cb6f0cbaccb900cfc5fcc689494adb2a7d7d6023c1ff1e8cf871047ec37eeca41386e31029d99cabf9343b4fd2a03
@bitcoin bitcoin locked and limited conversation to collaborators Oct 21, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants
@laanwj @7hacker @maflcko @IamLupo @garlonicon @carnhofdaki and others