Skip to content

Conversation

@AnnaShaleva
Copy link
Member

@AnnaShaleva AnnaShaleva commented Mar 19, 2024

Description

Implement native Notary contract.

Close #2897.

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

  • Native Gas contract unit tests;
  • Native Notary contract unit tests.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules:

AnnaShaleva and others added 8 commits March 6, 2024 12:00
Close #2896. Use a stub for native Notary contract hash since this
contract is not implemented yet. Thus, technically, NotaryAssisted
attribute verification will always fail on real network until native
Notary is implemented.

Signed-off-by: Anna Shaleva <[email protected]>
Transactions network fee should be split between Primary node and Notary
nodes.

Signed-off-by: Anna Shaleva <[email protected]>
Once Notary contract is implemented, this hash will be replaced by a
proper Notary contract hash. The exact value won't be changed since
Notary contract has constant hash as any other native contract.

Signed-off-by: Anna Shaleva <[email protected]>
No functional changes, just a refactoring for better code readability.

Signed-off-by: Anna Shaleva <[email protected]>
Close #2897. Depends on #3175.

Signed-off-by: Anna Shaleva <[email protected]>
@vncoelho
Copy link
Member

Is this PR to be merged now or after next release?
I was planning to review current release and then merge these next feature.

@roman-khimov
Copy link
Contributor

Is this PR to be merged now or after next release?

https://github.com/neo-project/neo/milestone/2

@dusmart
Copy link

dusmart commented Mar 21, 2024

This contract has a Verify method which is very dangerous.

private bool Verify(ApplicationEngine engine, byte[] sig)

In this method, we verify that tx.Signers[1] has enough balance stored in the contract. Plus, we verify that a signature from any notary is provided.

Here is an attack vector. A malicious notary can sign as many transactions as it wants and then publish at most 500 txs on chain while tx.Signers[1] has a little balance only sufficient for one tx.

If it's not a malicious notary, someone could still get a chance to make the notary sign something multiple times and cache them then publish them at once.

By the way, do you know how many core modules will be affected by this notary feature? Is this the last PR?

Copy link
Member

@vncoelho vncoelho left a comment

Choose a reason for hiding this comment

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

I am not seeing the part of the code we discussed on Discord, @AnnaShaleva, you mentioned about a delegated powered notary signing.
"The contract itself don't send the transactions. It's a designated Notary node who is powered to send transactions on behalf of notary service users. "

I want to check that because it should be, at least, similar to the oracles design.

No functional changes, just a refactoring.

Signed-off-by: Anna Shaleva <[email protected]>
@AnnaShaleva
Copy link
Member Author

By the way, do you know how many core modules will be affected by this notary feature? Is this the last PR?

To be able to process existing NeoFS chains that use Notary - yes, it's the last PR. But ideally in future we'd like to implement P2PNotaryRequest payload with pool (ref. nspcc-dev/neo-go#1546) and Notary service plugin (ref. nspcc-dev/neo-go#1547). We'll add related issues to neo-project later.

"The contract itself don't send the transactions. It's a designated Notary node who is powered to send transactions on behalf of notary service users. "
I want to check that because it should be, at least, similar to the oracles design.

It's a part of Notary service plugin. See the example implementation in https://github.com/nspcc-dev/neo-go/blob/master/pkg/services/notary/notary.go.

Copy link
Contributor

@vang1ong7ang vang1ong7ang left a comment

Choose a reason for hiding this comment

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

as @dusmart said , verify method should be used with extreme caution. and is there a fork introduced?

@roman-khimov
Copy link
Contributor

verify method should be used with extreme caution

That's a valid concern, but there are multiple ways to handle it, we'll get to it after #3175 merge.

Notary node account is always a standard single signature script.

This commit fixes state difference between Go and C# nodes at block 9762 of
NeoFS Testnet:
```
go run scripts/compare-dumps/compare-dumps.go ./godump-echidna-neofs-testnet/ ../../neo-project/neo/neo-cli-notary-testnet/Storage_2bdb2b5f/
Processing directory BlockStorage_0
Processing directory BlockStorage_100000
file BlockStorage_100000/dump-block-10000.json: block 9762, changes length mismatch: 13 vs 20
compare-dumps dumpDirA dumpDirB
exit status 1
```

The reason is in improper construction of Notary node address which
leads to the fact that notary GAS reward goes to wrong addresses. Here's
a part of OnPersist application log for this block generated by C# node
that contains wrong GAS receiver addresses:
```
                    {
                        "contract": "0xd2a4cff31913016155e38e474a2c06d08be276cf",
                        "eventname": "Transfer",
                        "state": {
                            "type": "Array",
                            "value": [
                                {
                                    "type": "Any"
                                },
                                {
                                    "type": "ByteString",
                                    "value": "Z0B9UyyyUsDlW4OgICWJ+nwLyZc="
                                },
                                {
                                    "type": "Integer",
                                    "value": "11428571"
                                }
                            ]
                        }
                    },
                    {
                        "contract": "0xd2a4cff31913016155e38e474a2c06d08be276cf",
                        "eventname": "Transfer",
                        "state": {
                            "type": "Array",
                            "value": [
                                {
                                    "type": "Any"
                                },
                                {
                                    "type": "ByteString",
                                    "value": "5Sk5i/oPh7IBb7VvlHfiUjX2LHU="
                                },
                                {
                                    "type": "Integer",
                                    "value": "11428571"
                                }
                            ]
                        }
                    },
                    {
                        "contract": "0xd2a4cff31913016155e38e474a2c06d08be276cf",
                        "eventname": "Transfer",
                        "state": {
                            "type": "Array",
                            "value": [
                                {
                                    "type": "Any"
                                },
                                {
                                    "type": "ByteString",
                                    "value": "HcOwwNv9mwY1i3SF4d5qX8jDdJs="
                                },
                                {
                                    "type": "Integer",
                                    "value": "11428571"
                                }
                            ]
                        }
                    },
                    {
                        "contract": "0xd2a4cff31913016155e38e474a2c06d08be276cf",
                        "eventname": "Transfer",
                        "state": {
                            "type": "Array",
                            "value": [
                                {
                                    "type": "Any"
                                },
                                {
                                    "type": "ByteString",
                                    "value": "8a2TEhIZFJ/lZDklkMs+d6i7OWI="
                                },
                                {
                                    "type": "Integer",
                                    "value": "11428571"
                                }
                            ]
                        }
                    },
                    {
                        "contract": "0xd2a4cff31913016155e38e474a2c06d08be276cf",
                        "eventname": "Transfer",
                        "state": {
                            "type": "Array",
                            "value": [
                                {
                                    "type": "Any"
                                },
                                {
                                    "type": "ByteString",
                                    "value": "Oke4DvOe+XZ+3mQnZ5nE1Ilm3jY="
                                },
                                {
                                    "type": "Integer",
                                    "value": "11428571"
                                }
                            ]
                        }
                    },
                    {
                        "contract": "0xd2a4cff31913016155e38e474a2c06d08be276cf",
                        "eventname": "Transfer",
                        "state": {
                            "type": "Array",
                            "value": [
                                {
                                    "type": "Any"
                                },
                                {
                                    "type": "ByteString",
                                    "value": "7frkdtIbPRN2ijaHnw+tfUxMzA8="
                                },
                                {
                                    "type": "Integer",
                                    "value": "11428571"
                                }
                            ]
                        }
                    },
                    {
                        "contract": "0xd2a4cff31913016155e38e474a2c06d08be276cf",
                        "eventname": "Transfer",
                        "state": {
                            "type": "Array",
                            "value": [
                                {
                                    "type": "Any"
                                },
                                {
                                    "type": "ByteString",
                                    "value": "YjI6lI1pOy/OxH1PjcG/yIXwD/4="
                                },
                                {
                                    "type": "Integer",
                                    "value": "11428571"
                                }
                            ]
                        }
                    }
                ],
                "stack": [
                ],
                "trigger": "OnPersist",
                "vmstate": "HALT"
            }
```

Signed-off-by: Anna Shaleva <[email protected]>
if (deposit is null)
{
var feePerKey = Policy.GetAttributeFeeV1(engine.SnapshotCache, (byte)TransactionAttributeType.NotaryAssisted);
if ((long)amount < 2 * feePerKey) throw new ArgumentOutOfRangeException(string.Format("first deposit can not be less than {0}, got {1}", 2 * feePerKey, amount));
Copy link
Member

Choose a reason for hiding this comment

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

I think that here we missed some standard fee instead of just the dynamic.

Copy link
Member Author

Choose a reason for hiding this comment

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

It's just a basic check that ensures that deposited value will be enough to pay for a fallback transaction.

@vncoelho vncoelho requested a review from vang1ong7ang April 22, 2025 12:06
/// <returns>result</returns>
private static long CalculateNotaryReward(IReadOnlyStore snapshot, long nFees, int notariesCount)
{
return nFees * Policy.GetAttributeFeeV1(snapshot, (byte)TransactionAttributeType.NotaryAssisted) / notariesCount;
Copy link
Member

Choose a reason for hiding this comment

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

Are we sure that this matches:

if (notaryAssisted is not null)
                {
                    totalNetworkFee -= (notaryAssisted.NKeys + 1) * Policy.GetAttributeFeeV1(engine.SnapshotCache, (byte)notaryAssisted.Type);
                }

There we multiple. Here we divide then mint, are the rounding correct?
Maybe it would be safer to do the same calculation and then divide the sum correctly and precisely.

ping @shargon

Copy link
Member Author

Choose a reason for hiding this comment

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

The rounding is correct. We agree that there might be a situation when total notary reward is not divisible by notariesCount, then the reward per node will be rounded to lower integer value. The reminder will not be transferred to anyone, and we just live with it. So to us, it's not a problem since reminder is negligible.

If it's a problem, then we can always set NotaryAssisted attribute fee to be a multuple of notariesCount.

Copy link
Member

Choose a reason for hiding this comment

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

If it's a problem, then we can always set NotaryAssisted attribute fee to be a multuple of notariesCount.

It will be nice, every protection is good

Copy link
Member Author

@AnnaShaleva AnnaShaleva Apr 28, 2025

Choose a reason for hiding this comment

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

It will be nice, every protection is good

It will be done by Committee via invocation of setAttributeFee. So I don't think we need to update the code.

Copy link
Member

Choose a reason for hiding this comment

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

It will be nice, every protection is good

It will be done by Committee via invocation of setAttributeFee. So I don't think we need to update the code.

but they can do the opposite

Copy link
Member Author

Choose a reason for hiding this comment

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

We still consider that this reminder is negligible and it doesn't worth to introduce dependency from RoleManagement into Policy contract to protect from possible committee misbehavior.

Copy link
Contributor

Choose a reason for hiding this comment

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

Committee can do a lot of wrong things, protecting from bad committee is impossible in general. Realistically this is never a problem even if there are some datoshis burned.

@NGDAdmin NGDAdmin merged commit a8b1f5a into master Apr 29, 2025
7 checks passed
@NGDAdmin NGDAdmin deleted the notary-contract branch April 29, 2025 02:26
AnnaShaleva added a commit to nspcc-dev/neo-go that referenced this pull request Apr 30, 2025
AnnaShaleva added a commit to nspcc-dev/neo-go that referenced this pull request Apr 30, 2025
AnnaShaleva added a commit to nspcc-dev/neo-go that referenced this pull request Apr 30, 2025
AnnaShaleva added a commit to nspcc-dev/neo-go that referenced this pull request Apr 30, 2025
AnnaShaleva added a commit to nspcc-dev/neo-go that referenced this pull request Apr 30, 2025
AnnaShaleva added a commit to nspcc-dev/neo-go that referenced this pull request Apr 30, 2025
AnnaShaleva added a commit to nspcc-dev/neo-go that referenced this pull request Apr 30, 2025
cschuchardt88 added a commit to cschuchardt88/neo that referenced this pull request Jun 8, 2025
* Implement NotaryAssisted transaction attribute

Close neo-project#2896. Use a stub for native Notary contract hash since this
contract is not implemented yet. Thus, technically, NotaryAssisted
attribute verification will always fail on real network until native
Notary is implemented.

Signed-off-by: Anna Shaleva <[email protected]>

* Payloads: add doc to CalculateNetworkFee method of NotaryAssisted attribute

Signed-off-by: Anna Shaleva <[email protected]>

* Native: add NotaryAssisted attributes handler to Gas OnPersist

Transactions network fee should be split between Primary node and Notary
nodes.

Signed-off-by: Anna Shaleva <[email protected]>

* Payloads: adjust comment to NotaryAssisted attribute

Signed-off-by: Anna Shaleva <[email protected]>

* Payloads: temporary use hard-coded Notary contract hash

Once Notary contract is implemented, this hash will be replaced by a
proper Notary contract hash. The exact value won't be changed since
Notary contract has constant hash as any other native contract.

Signed-off-by: Anna Shaleva <[email protected]>

* Payloads: replace hard-coded Notary hash value with calculated one

No functional changes, just a refactoring for better code readability.

Signed-off-by: Anna Shaleva <[email protected]>

* Native: implement native Notary contract

Close neo-project#2897. Depends on neo-project#3175.

Signed-off-by: Anna Shaleva <[email protected]>

* Native: fix typo in the exception message

Signed-off-by: Anna Shaleva <[email protected]>

* Native: use more syntactic sugar

No functional changes, just a refactoring.

Signed-off-by: Anna Shaleva <[email protected]>

* Notary: add unit tests for OnNEP17Payment and ExpirationOf methods

Also, mark improper code of Withdraw method with TODO, it should be
reworked.

Signed-off-by: Anna Shaleva <[email protected]>

* Fix transfer

* Update tests/Neo.UnitTests/SmartContract/Native/UT_Notary.cs

* Update UT_Notary.cs

* fix

* Fix notary

* Fix notary

* Notary: add unit tests for LockDepositUntil and BalanceOf methods

Also, format code.

Signed-off-by: Anna Shaleva <[email protected]>

* Notary: test GAS distribution with FeePerKey update

Test the situation described in
https://github.com/neo-project/neo/pull/3175/files/00b54ff6d20cc84b435beaa790fe72a9d8f78bec#r1530493475.

Signed-off-by: Anna Shaleva <[email protected]>

* Notary: add test for Withdraw

Signed-off-by: Anna Shaleva <[email protected]>

* Notary: remove unused code

No functional changes, I just finally made my code analizer work
properly.

Signed-off-by: Anna Shaleva <[email protected]>

* Attributtes: fix NotaryAssisted attribute documentation format

Co-authored-by: Shargon <[email protected]>

* Native: update to the fresh master

Fetch changes from the fresh master and fix build errors.

Signed-off-by: Anna Shaleva <[email protected]>

* Notary: sync up with master changes

No functional changes, just adopt latest changes in syntax from master
to make the code buildable and make unit tests pass.

Signed-off-by: Anna Shaleva <[email protected]>

* Notary: make the contract active starting from Echidna.

Signed-off-by: Anna Shaleva <[email protected]>

* Notary: add NEP-27 to the list of supported standards

Port nspcc-dev/neo-go#3792.

Signed-off-by: Anna Shaleva <[email protected]>

* Notary: adjust deposit expiration rule

Follow nspcc-dev/neo-go#3211.

Signed-off-by: Anna Shaleva <[email protected]>

* clean usings

* Update tests/Neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs

Co-authored-by: Will <[email protected]>

* Notary: improve long line format

No functional changes.

Signed-off-by: Anna Shaleva <[email protected]>

* Notary: prettify exception thrown on unexpected OnNEP17Payment data

Throw FormatException instead of InvalidCastException on invalid data
passed to OnNEP17Payment.

Signed-off-by: Anna Shaleva <[email protected]>

* Notary: don't FAULT execution in case of non-tx verification container

Return `false` in case if execution container for `verify` is not a
transaction.

Signed-off-by: Anna Shaleva <[email protected]>

* Notary: unify deposit lock validity check

No functional changes, just a refactoring.

Signed-off-by: Anna Shaleva <[email protected]>

* Notary: extend `verify` with signature argument validation

Ref.
https://github.com/neo-project/neo/pull/3178/files/56acd97e4b2e93601b7e3d4b15b5ccafc9455fcd#r2010316964.

Signed-off-by: Anna Shaleva <[email protected]>

* Notary: add a note about Deposit serialization

No functional changes.

Signed-off-by: Anna Shaleva <[email protected]>

* UnitTests: remove NotaryAssisted fees unit test

This test requires Notary nodes to be properly designated via native
RoleManagement contract, it's hard to mock this behaviour during test,
I wasn't able to properly do it.

This test may be restored once we have better testing system.

Signed-off-by: Anna Shaleva <[email protected]>

* Notary: replace exceptions with false return for `withdraw`

Ref.
neo-project#3178 (comment).

Signed-off-by: Anna Shaleva <[email protected]>

* Notary: refactor variable definition

No functional changes, ref.
neo-project#3178 (comment).

Signed-off-by: Anna Shaleva <[email protected]>

* Notary: migrate to Policy.GetAttributeFeeV1 usage

No functional changes, just adopt the neo-project#3859.

Signed-off-by: Anna Shaleva <[email protected]>

* Review Notary (neo-project#3870)

* Update Notary.cs

* Update Notary.cs

* Update Notary.cs

* fix

* clean

* Clean

* Update src/Neo/SmartContract/Native/Notary.cs

* Update tests/Neo.UnitTests/TestUtils.Transaction.cs

Co-authored-by: Christopher Schuchardt <[email protected]>

* Update tests/Neo.UnitTests/TestUtils.Transaction.cs

Co-authored-by: Christopher Schuchardt <[email protected]>

* Update tests/Neo.UnitTests/TestUtils.Transaction.cs

* Update tests/Neo.UnitTests/TestUtils.Transaction.cs

* Notary: adapt Policy-based MaxValidUntilBlockIncrement

Fetch recent update from neo-project#3861.

Signed-off-by: Anna Shaleva <[email protected]>

* Notary: rename manifest parameters to follow the native style

Unify parameters naming to follow the style of other native contracts.
No functional changes.

Signed-off-by: Anna Shaleva <[email protected]>

* Notary: adjust required callflags for some methods

Signed-off-by: Anna Shaleva <[email protected]>

* Notary: fix Notary node scripthash construction

Notary node account is always a standard single signature script.

This commit fixes state difference between Go and C# nodes at block 9762 of
NeoFS Testnet:
```
go run scripts/compare-dumps/compare-dumps.go ./godump-echidna-neofs-testnet/ ../../neo-project/neo/neo-cli-notary-testnet/Storage_2bdb2b5f/
Processing directory BlockStorage_0
Processing directory BlockStorage_100000
file BlockStorage_100000/dump-block-10000.json: block 9762, changes length mismatch: 13 vs 20
compare-dumps dumpDirA dumpDirB
exit status 1
```

The reason is in improper construction of Notary node address which
leads to the fact that notary GAS reward goes to wrong addresses. Here's
a part of OnPersist application log for this block generated by C# node
that contains wrong GAS receiver addresses:
```
                    {
                        "contract": "0xd2a4cff31913016155e38e474a2c06d08be276cf",
                        "eventname": "Transfer",
                        "state": {
                            "type": "Array",
                            "value": [
                                {
                                    "type": "Any"
                                },
                                {
                                    "type": "ByteString",
                                    "value": "Z0B9UyyyUsDlW4OgICWJ+nwLyZc="
                                },
                                {
                                    "type": "Integer",
                                    "value": "11428571"
                                }
                            ]
                        }
                    },
                    {
                        "contract": "0xd2a4cff31913016155e38e474a2c06d08be276cf",
                        "eventname": "Transfer",
                        "state": {
                            "type": "Array",
                            "value": [
                                {
                                    "type": "Any"
                                },
                                {
                                    "type": "ByteString",
                                    "value": "5Sk5i/oPh7IBb7VvlHfiUjX2LHU="
                                },
                                {
                                    "type": "Integer",
                                    "value": "11428571"
                                }
                            ]
                        }
                    },
                    {
                        "contract": "0xd2a4cff31913016155e38e474a2c06d08be276cf",
                        "eventname": "Transfer",
                        "state": {
                            "type": "Array",
                            "value": [
                                {
                                    "type": "Any"
                                },
                                {
                                    "type": "ByteString",
                                    "value": "HcOwwNv9mwY1i3SF4d5qX8jDdJs="
                                },
                                {
                                    "type": "Integer",
                                    "value": "11428571"
                                }
                            ]
                        }
                    },
                    {
                        "contract": "0xd2a4cff31913016155e38e474a2c06d08be276cf",
                        "eventname": "Transfer",
                        "state": {
                            "type": "Array",
                            "value": [
                                {
                                    "type": "Any"
                                },
                                {
                                    "type": "ByteString",
                                    "value": "8a2TEhIZFJ/lZDklkMs+d6i7OWI="
                                },
                                {
                                    "type": "Integer",
                                    "value": "11428571"
                                }
                            ]
                        }
                    },
                    {
                        "contract": "0xd2a4cff31913016155e38e474a2c06d08be276cf",
                        "eventname": "Transfer",
                        "state": {
                            "type": "Array",
                            "value": [
                                {
                                    "type": "Any"
                                },
                                {
                                    "type": "ByteString",
                                    "value": "Oke4DvOe+XZ+3mQnZ5nE1Ilm3jY="
                                },
                                {
                                    "type": "Integer",
                                    "value": "11428571"
                                }
                            ]
                        }
                    },
                    {
                        "contract": "0xd2a4cff31913016155e38e474a2c06d08be276cf",
                        "eventname": "Transfer",
                        "state": {
                            "type": "Array",
                            "value": [
                                {
                                    "type": "Any"
                                },
                                {
                                    "type": "ByteString",
                                    "value": "7frkdtIbPRN2ijaHnw+tfUxMzA8="
                                },
                                {
                                    "type": "Integer",
                                    "value": "11428571"
                                }
                            ]
                        }
                    },
                    {
                        "contract": "0xd2a4cff31913016155e38e474a2c06d08be276cf",
                        "eventname": "Transfer",
                        "state": {
                            "type": "Array",
                            "value": [
                                {
                                    "type": "Any"
                                },
                                {
                                    "type": "ByteString",
                                    "value": "YjI6lI1pOy/OxH1PjcG/yIXwD/4="
                                },
                                {
                                    "type": "Integer",
                                    "value": "11428571"
                                }
                            ]
                        }
                    }
                ],
                "stack": [
                ],
                "trigger": "OnPersist",
                "vmstate": "HALT"
            }
```

Signed-off-by: Anna Shaleva <[email protected]>

---------

Signed-off-by: Anna Shaleva <[email protected]>
Co-authored-by: Jimmy <[email protected]>
Co-authored-by: Fernando Diaz Toledano <[email protected]>
Co-authored-by: Christopher Schuchardt <[email protected]>
Co-authored-by: Will <[email protected]>
Co-authored-by: NGD Admin <[email protected]>
Co-authored-by: Vitor Nazário Coelho <[email protected]>
Jim8y pushed a commit to neo-project/neo-devpack-dotnet that referenced this pull request Aug 3, 2025
* Neo.SmartContract.Framework: add native Notary contract API
Ref. neo-project/neo#3178.
Ported from #556
with all necessary updates.
Signed-off-by: Anna Shaleva <[email protected]>
* Update src/Neo.SmartContract.Framework/Native/Notary.cs
Co-authored-by: Will <[email protected]>
---------
Signed-off-by: Anna Shaleva <[email protected]>
Co-authored-by: Jimmy <[email protected]>
Co-authored-by: Will <[email protected]>
Co-authored-by: Shargon <[email protected]>
Jim8y added a commit to neo-project/neo-devpack-dotnet that referenced this pull request Aug 18, 2025
* Neo.SmartContract.Framework: add native Notary contract API

Ref. neo-project/neo#3178.

Ported from #556
with all necessary updates.

Signed-off-by: Anna Shaleva <[email protected]>

* Update src/Neo.SmartContract.Framework/Native/Notary.cs

Co-authored-by: Will <[email protected]>

---------

Signed-off-by: Anna Shaleva <[email protected]>
Co-authored-by: Jimmy <[email protected]>
Co-authored-by: Will <[email protected]>
Co-authored-by: Shargon <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature Type: Large changes or new features Hardfork Ready to Merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Notary native contract