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/Make NewEpoch callback mechanism more generic #368

Merged
merged 6 commits into from
Nov 27, 2023

Conversation

carpawell
Copy link
Member

Do not hardcode container and balance contract as a NewEpoch events receivers in the netmap contract. Use the netmap's registration method on a deploy stage instead.

Copy link
Member

@roman-khimov roman-khimov left a comment

Choose a reason for hiding this comment

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

Tests fail.

netmap/netmap_contract.go Outdated Show resolved Hide resolved
netmap/netmap_contract.go Outdated Show resolved Hide resolved
netmap/config.yml Outdated Show resolved Hide resolved
balance/balance_contract.go Outdated Show resolved Hide resolved
common/netmap.go Outdated Show resolved Hide resolved
common/netmap.go Outdated Show resolved Hide resolved
container/container_contract.go Outdated Show resolved Hide resolved
netmap/netmap_contract.go Show resolved Hide resolved
netmap/netmap_contract.go Show resolved Hide resolved
common/netmap.go Outdated Show resolved Hide resolved
common/netmap.go Outdated Show resolved Hide resolved
container/config.yml Outdated Show resolved Hide resolved
netmap/netmap_contract.go Outdated Show resolved Hide resolved
@carpawell
Copy link
Member Author

Ready one more time. Look carefully, please.

@carpawell carpawell marked this pull request as ready for review November 23, 2023 17:08
netmap/netmap_contract.go Show resolved Hide resolved
netmap/netmap_contract.go Outdated Show resolved Hide resolved
netmap/netmap_contract.go Show resolved Hide resolved
netmap/netmap_contract.go Show resolved Hide resolved
netmap/netmap_contract.go Show resolved Hide resolved
// callback requester. Netmap contract's address is taken from the
// NNS contract, therefore, it must be presented and filled with
// netmap information for a correct SubscribeForNewEpoch call; otherwise
// a successive call is not guaranteed.
Copy link
Contributor

Choose a reason for hiding this comment

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

what call is this line talking about? not guaranteed seems like some kind of random

Copy link
Member Author

Choose a reason for hiding this comment

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

what call is this line talking about?

method call

not guaranteed seems like some kind of random

i meant it depends on an NNS implementation. it may and may not panic

Copy link
Contributor

Choose a reason for hiding this comment

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

i've read this doc several times and not sure i completely understand it. We impose some preconditions (Netmap contract registered in NNS). If complied with - success, otherwise panic, right? Where is not guaranteed come from?

filled with netmap information - who must be filled and what information?

Copy link
Member Author

Choose a reason for hiding this comment

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

If complied with - success, otherwise panic, right?

well, to me that is not for sure. you need this to work it OK, but if it is not true, it depends on the NNS contract implementation. this call maybe a part of transaction, and panic (exception) may fail the whole TX

Where is not guaranteed come from?

that is how i said "undefined behavior" but that time i wanted to say it this way

filled with netmap information - who must be filled and what information?

NNS contract

// NNS contract, therefore, it must be presented and filled with
// netmap information for a correct SubscribeForNewEpoch call; otherwise
// a successive call is not guaranteed.
// Caller must have `NewEpoch` method with a single numeric argument.
Copy link
Contributor

Choose a reason for hiding this comment

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

possible to check?

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

@roman-khimov, no args' types check?

Copy link
Member

Choose a reason for hiding this comment

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

It doesn't care even for actual calls (hi, neo-project/neo#1891) and you want it to check a complete signature in hasMethod...

Copy link
Member Author

Choose a reason for hiding this comment

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

done

"github.com/nspcc-dev/neo-go/pkg/interop/native/std"
)

// ResolveContractHash resolves contract hash by its well-known NeoFS name.
Copy link
Contributor

Choose a reason for hiding this comment

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

well known by whom?

Suggested change
// ResolveContractHash resolves contract hash by its well-known NeoFS name.
// ResolveContractHash hash of the NeoFS contract by its NNS domain name.

btw is there any place where we doc these names?

Copy link
Member Author

Choose a reason for hiding this comment

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

ResolveContractHash hash of the NeoFS contract

what do you want to say by it?

by its NNS domain name.

not sure we can say it. that is not the full NNS name, a caller should pass just container, not container.neofs

)

// ResolveContractHash resolves contract hash by its well-known NeoFS name.
// Contract name should be lowercased, should not include `.neofs` TLD. Example
Copy link
Contributor

Choose a reason for hiding this comment

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

its better to either require name from the names list (rel thread above) or just should be a valid NNS domain

Copy link
Member Author

Choose a reason for hiding this comment

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

its better to either require name from the names list (rel thread above)

the list can grow and docs arent updated usually

should be a valid NNS domain

but container is not a valid NNS name

// values: "netmap", "container", etc.
// Relies on some NeoFS specifics:
// 1. NNS contract should be deployed first (and should have `1` contract ID)
// 2. It should be prefilled with contract hashes by their names (no
Copy link
Contributor

Choose a reason for hiding this comment

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

isnt this obvious for ResolveContractHash semantics? If it's not prefilled -> resolving fails

Copy link
Member Author

Choose a reason for hiding this comment

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

NNS is kinda general thing and we are not the only ones who have it. so i wanted to highlight what exactly should be done: NNS is deployed and it should map string -> uint160 (although i understand that the only callers of this method is are our contracts)

@@ -73,7 +73,8 @@ const (
containerContractKey = "containerScriptHash"
balanceContractKey = "balanceScriptHash"

cleanupEpochMethod = "newEpoch"
newEpochSubscribersKey = "newEpochSubscribers"
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
newEpochSubscribersKey = "newEpochSubscribers"
newEpochSubscribersKeyPrefix = "newEpochSubscribers"

Copy link
Member

Choose a reason for hiding this comment

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

Please use one byte prefixes, they don't have to be this long and we can only have 64-byte keys.

Copy link
Member Author

Choose a reason for hiding this comment

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

newEpochSubscribersKeyPrefix

ok but without key then

Please use one byte prefixes

n won my democratic election

// contracts are migrated in alphabetical order at least for now

contract.SeekStorage(append(newEpochSubsNewPrefix, 0), func(k, v []byte) bool {
balanceGot, err := util.Uint160DecodeBytesLE(k)
Copy link
Contributor

Choose a reason for hiding this comment

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

u dont check func entrance. I'd gather map index->address and check after SeekStorage call

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed by bool flags

@@ -601,6 +605,32 @@ func ListConfig() []record {
return config
}

// SubscribeForNewEpoch registers passed contract as a NewEpoch event
// subscriber. Such a contract must have a `NewEpoch` method with a single
// numeric argument. Transactions that call SubscribeForNewEpoch must be
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// numeric argument. Transactions that call SubscribeForNewEpoch must be
// numeric parameter. Transactions that call SubscribeForNewEpoch must be

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed

// subscriber. Such a contract must have a `NewEpoch` method with a single
// numeric argument. Transactions that call SubscribeForNewEpoch must be
// witnessed by the Alphabet.
// Produces `NewEpochHookRegistration` notification event with a just
Copy link
Contributor

Choose a reason for hiding this comment

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

according to code it is

Suggested change
// Produces `NewEpochHookRegistration` notification event with a just
// Produces `NewEpochSubscription` notification event with a just

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed. it did not live renamings well

@@ -252,6 +252,13 @@ func (x *Contract) GetStorageItem(key []byte) []byte {
return x.exec.Chain.GetStorageItem(x.id, key)
}

// SeekStorage calls a provided handler against every stored item that starts
// with a provided prefix. On handler's `false` return stops iteration. prefix
// is removed from the resulting pair.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// is removed from the resulting pair.
// is removed from the resulting pair key.

?

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed

@cthulhu-rider
Copy link
Contributor

1st commit message has strange quotes

// NNS contract, therefore, it must be presented and filled with
// netmap information for a correct SubscribeForNewEpoch call; otherwise
// a successive call is not guaranteed.
// Caller must have `NewEpoch` method with a single numeric argument.
Copy link
Member

Choose a reason for hiding this comment

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

common/netmap.go Outdated Show resolved Hide resolved
@@ -73,7 +73,8 @@ const (
containerContractKey = "containerScriptHash"
balanceContractKey = "balanceScriptHash"

cleanupEpochMethod = "newEpoch"
newEpochSubscribersKey = "newEpochSubscribers"
Copy link
Member

Choose a reason for hiding this comment

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

Please use one byte prefixes, they don't have to be this long and we can only have 64-byte keys.

netmap/netmap_contract.go Outdated Show resolved Hide resolved
@carpawell
Copy link
Member Author

1st commit message has strange quotes

fixed

common/netmap.go Outdated Show resolved Hide resolved
common/netmap.go Outdated
@@ -12,6 +14,11 @@ import (
// a successive call is not guaranteed.
// Caller must have `NewEpoch` method with a single numeric argument.
func SubscribeForNewEpoch() {
callingHash := runtime.GetExecutingScriptHash()
Copy link
Member

Choose a reason for hiding this comment

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

It's executing script hash and you're calling it calling.

Copy link
Member Author

Choose a reason for hiding this comment

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

i meant that is a contract that called that method. renamed to callingContract, it is ok?

common/netmap.go Outdated
@@ -12,6 +14,11 @@ import (
// a successive call is not guaranteed.
// Caller must have `NewEpoch` method with a single numeric argument.
func SubscribeForNewEpoch() {
callingHash := runtime.GetExecutingScriptHash()
if !management.HasMethod(callingHash, "newEpoch", 1) {
Copy link
Member

Choose a reason for hiding this comment

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

Why this check is here and not in the subscribeForNewEpoch itself? It makes much more sense there since netmap controls its state and shouldn't allow bad subscriptions (forget contract upgrades for a moment).

Copy link
Member Author

Choose a reason for hiding this comment

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

moved to the netmap contract

roman-khimov
roman-khimov previously approved these changes Nov 27, 2023
AnnaShaleva
AnnaShaleva previously approved these changes Nov 27, 2023
Copy link
Member

@AnnaShaleva AnnaShaleva left a comment

Choose a reason for hiding this comment

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

LGTM otherwise, but these are minor comments and may be left unresolved.

it := storage.Find(ctx, newEpochSubscribersPrefix, storage.RemovePrefix|storage.KeysOnly)
for iterator.Next(it) {
contractHash := interop.Hash160(iterator.Value(it).([]byte)[1:]) // one byte is for number prefix
contract.Call(contractHash, cleanupEpochMethod, contract.All, epoch)
Copy link
Member

Choose a reason for hiding this comment

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

And one more important moment that just came to my mind. If something (panic, I mean) happens in the calling callback, then the rest of callbacks won't be executed. It remains PostPersist and technically it's OK to fail immediately, so just a question: do we need to think about some exception handlers here?

Copy link
Member Author

Choose a reason for hiding this comment

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

do we need to think about some exception handlers here?

only our own contracts are expected to be new-epoch-notified so any panic is a global panic to me and i can not say that some contract is more or less important to us, we need all of them to be notified. failed notifications are a failed NeoFS network to me. also, i do know what such a handler should do. notify, panic but later, log?

@roman-khimov

Copy link
Member

Choose a reason for hiding this comment

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

Partial failure in many ways is better than complete failure, so handling exceptions makes some sense here, but I'd leave it to another issue.

Comment on lines +628 to +633
raw := iterator.Value(it).([]byte)[1:] // 1 byte is an index
if contract.Equals(raw) {
return
}
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 a paranoiac about VM's EQUAL and compiler, could we have a test for the contract that subscribes for notifications and after that subscribes one more time? In the end we should check that there's only one subscriber record stored in the netmap.

Copy link
Member Author

Choose a reason for hiding this comment

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

done

Comment on lines 102 to 103
addrBalance interop.Hash160 // Balance contract not used legacy
addrContainer interop.Hash160 // Container contract not used legacy
Copy link
Member

Choose a reason for hiding this comment

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

BTW, if these two fields are unused, then you can explicitly mark them as _.

Copy link
Member Author

Choose a reason for hiding this comment

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

fixed

roman-khimov
roman-khimov previously approved these changes Nov 27, 2023
Remove hardcoded "container" as a nice name TLD. It looks like a bug and
has nothing useful. Also, fix tests that register "neofs" as a nice name
TLD, that does not allow any contract test changes with the common TLD
("neofs") registration.

Signed-off-by: Pavel Karpy <[email protected]>
Do not hardcode container and balance contract as a `NewEpoch` events
receivers in the netmap contract. Use the new netmap's registration method
on subscribers' deployment stages instead. Closes #327.

Signed-off-by: Pavel Karpy <[email protected]>
Subscribe only contracts that can handle it with a corresponding method.
Method's arg type is currently not possible to check but that is a Neo story
not that repo's or the commit's one.

Signed-off-by: Pavel Karpy <[email protected]>
With `nolint` of course. Seems like `unused` ignores funcs that are marked with
`nolint` and does not see the old keys usage.

Signed-off-by: Pavel Karpy <[email protected]>
@carpawell
Copy link
Member Author

Some var renamings, commit body fixes, sry.

@roman-khimov roman-khimov merged commit a72a205 into master Nov 27, 2023
8 checks passed
@roman-khimov roman-khimov deleted the feat/new-epoch-hook branch November 27, 2023 19:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants