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

feat: transactions in the same block have different timestamps #1777

Closed
wants to merge 8 commits into from

Conversation

deelawn
Copy link
Contributor

@deelawn deelawn commented Mar 14, 2024

Implements #1709. This is a BREAKING CHANGE that will break code that assumes transactions in the same block will have the same timestamp. The strategy here is to add 100 nanoseconds on to the timestamp after each transaction in a block. The counter is reset to zero at the end of each block.

Contributors' checklist...
  • Added new tests, or not needed, or not feasible
  • Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory
  • Updated the official documentation or not needed
  • No breaking changes were made, or a BREAKING CHANGE: xxx message was included in the description
  • [ x Added references to related issues and PRs
  • Provided any useful hints for running manual tests
  • Added new benchmarks to generated graphs, if any. More info here.

@github-actions github-actions bot added 🧾 package/realm Tag used for new Realms or Packages. 📦 🤖 gnovm Issues or PRs gnovm related 📦 🌐 tendermint v2 Issues or PRs tm2 related 📦 ⛰️ gno.land Issues or PRs gno.land package related labels Mar 14, 2024
@deelawn deelawn changed the title tx time offset feat: transactions in the same block have different timestamps Mar 15, 2024
@deelawn deelawn marked this pull request as ready for review March 15, 2024 22:35

// This is an arbitrary value that is increased after each transaction so that each transaction in the same block
// retrieve a unique time when calling `time.Now()`.
app.txCurrentTimeOffset += 100
Copy link
Contributor

@piux2 piux2 Apr 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we change this arbitrary value of 100 in production, it will cause backward compatibility issues for contracts that depend on the timestamp value. The offset should come from the block itself, otherwise it could leave potential breaking logics in the contract when the timestamp value is stored in realm states and compared with stored value in the same block, and later we change arbitrary value != 100. Also, what about multiple messages per transaction?

Copy link
Contributor

@jefft0 jefft0 Apr 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about multiple messages per transaction?

Our use case in the social app is calling a realm function like PostMessage which adds the message with its timestamp from time.Now(). I think it's reasonable that time.Now() returns the same value for each call in the same transaction. It think it is OK to explain to devs that a single call to a realm function runs "instantaneously". The only thing we're asking for in our use case is that a transaction which is submitted at a different time has a different timestamp. (Maybe the transactions are collected into one block but this is a processing detail that the devs shouldn't have to worry about.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the intention here is to keep the same timestamp per transaction so each message has the same and each transaction timestamp in the block is different. We could store the offset in the block; I need to think about this more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @piux2 -- I'm heavily leaning towards having this be a block-level thing, rather than something that's injected through the app. We can most probably keep this kind of logic in the execution context (we as the callers of the VM control the execution context).

This way we eliminate the need for bloating the app with an offset, and propagating it out to all transactions (even ones that aren't GnoVM transactions)

@thehowl
Copy link
Member

thehowl commented Apr 11, 2024

From the review meeting: I kind of dislike this approach, as it merges in what is actually the "transaction index" (within the block) together with the block time, creating a time.Now() value that while it does solve Berty's problem with AVL keys immediately, it does seem to be more counterintuitive in the long run.

The proposed/agreed solution is that of exposing instead std.TransactionIndex() int, which instead returns the position. @jefft0, to have the same behaviour as in this PR, you could have time.Now().Add(time.Duration(std.TransactionIndex()) * 100)

@deelawn
Copy link
Contributor Author

deelawn commented Apr 25, 2024

Will pick this up again today to make the suggested changes.

@zivkovicmilos
Copy link
Member

@deelawn

can you please merge in the latest master changes? 🙏

@zivkovicmilos zivkovicmilos added the breaking change Functionality that contains breaking changes label Jun 12, 2024
Copy link
Member

@zivkovicmilos zivkovicmilos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're going in a good direction, but it requires a bit more thought, and possibly changing the scope 🙏

@@ -9,8 +9,8 @@ import (
type ExecContext struct {
ChainID string
Height int64
Timestamp int64 // seconds
TimestampNano int64 // nanoseconds, only used for testing.
Timestamp int64
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to keep Timestamp if we have the nano version?
Can't we just make the Timestamp be the nano version?

@@ -27,6 +27,21 @@ type InMemoryNodeConfig struct {
GenesisMaxVMCycles int64
}

func (cfg *InMemoryNodeConfig) AddGenesisBalances(balances ...Balance) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why this was added?

"github.com/gnolang/gno/tm2/pkg/crypto"
"github.com/gnolang/gno/tm2/pkg/crypto/keys"
"github.com/gnolang/gno/tm2/pkg/log"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

const testChainID string = "tendermint_test"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should move this into the test, even if we redeclare it

Not a huge fan of having package-level test globals

@@ -65,7 +72,7 @@ func TestCallMultiple_Integration(t *testing.T) {
defer node.Stop()

// Init Signer & RPCClient
signer := newInMemorySigner(t, "tendermint_test")
signer := defaultInMemorySigner(t, "tendermint_test")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm laughing that we defined a var but never used it here :)

t.Helper()

mnemonic := integration.DefaultAccount_Seed
name := integration.DefaultAccount_Name
keybase := keys.NewInMemory()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: this can be inlined

Comment on lines +186 to +188
if res.err != nil {
t.Errorf("unexpected error %v", res.err)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick:

Suggested change
if res.err != nil {
t.Errorf("unexpected error %v", res.err)
}
require.NoError(t, res.err)

Comment on lines +192 to +194
if len(results) != 2 {
t.Errorf("expected 2 results, got %d", len(results))
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick:

Suggested change
if len(results) != 2 {
t.Errorf("expected 2 results, got %d", len(results))
}
require.Len(t, results, 2)

Comment on lines +197 to +200
if i < maxTries {
continue
}
t.Errorf("expected same height, got %d and %d", results[0].Height, results[1].Height)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick:

Suggested change
if i < maxTries {
continue
}
t.Errorf("expected same height, got %d and %d", results[0].Height, results[1].Height)
require.Less(t, i, maxTries)

Comment on lines +203 to +212
extractInt := func(data []byte) int64 {
parts := strings.Split(string(data), " ")
numStr := parts[0][1:]
num, err := strconv.ParseInt(numStr, 10, 64)
if err != nil {
t.Errorf("unable to parse number from string %s", string(data))
}

return num
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not declare this outside the loop?


// This is an arbitrary value that is increased after each transaction so that each transaction in the same block
// retrieve a unique time when calling `time.Now()`.
app.txCurrentTimeOffset += 100
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @piux2 -- I'm heavily leaning towards having this be a block-level thing, rather than something that's injected through the app. We can most probably keep this kind of logic in the execution context (we as the callers of the VM control the execution context).

This way we eliminate the need for bloating the app with an offset, and propagating it out to all transactions (even ones that aren't GnoVM transactions)

@deelawn
Copy link
Contributor Author

deelawn commented Sep 6, 2024

I don't think we want this any more

@deelawn deelawn closed this Sep 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking change Functionality that contains breaking changes 📦 🌐 tendermint v2 Issues or PRs tm2 related 📦 ⛰️ gno.land Issues or PRs gno.land package related 📦 🤖 gnovm Issues or PRs gnovm related 🧾 package/realm Tag used for new Realms or Packages.
Projects
Status: Done
Status: No status
Development

Successfully merging this pull request may close these issues.

Feature request: Unique time.Now() per transaction
5 participants