Support Neo N3 account auth #1368

Closed
opened 2025-12-28 17:22:43 +00:00 by sami · 18 comments
Owner

Originally created by @cthulhu-rider on GitHub (Mar 3, 2025).

Originally assigned to: @cthulhu-rider on GitHub.

introduced in https://github.com/nspcc-dev/neofs-api/issues/305

all system parts must serve authentication and authorization via new scheme

Originally created by @cthulhu-rider on GitHub (Mar 3, 2025). Originally assigned to: @cthulhu-rider on GitHub. introduced in https://github.com/nspcc-dev/neofs-api/issues/305 all system parts must serve authentication and authorization via new scheme
sami 2025-12-28 17:22:43 +00:00
Author
Owner

@cthulhu-rider commented on GitHub (Mar 3, 2025):

Neo Go code snippet demonstrating examplary authorization through new N3 scheme with contract-side key management (thx to @AnnaShaleva && @roman-khimov):

func TestGenerateUserMgmtWitness(t *testing.T) {
	chain, _, httpSrv := initServerWithInMemoryChain(t)

	c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{})
	require.NoError(t, err)
	t.Cleanup(c.Close)
	require.NoError(t, c.Init())

	// TODO: fill these variables:
	var (
		currHeight int64 = int64(chain.BlockHeight()) // may be current Epoch or whatever UserMgmt contract needs it to be.
		sig              = make([]byte, keys.SignatureLen)
		userName         = []byte("Bob")
		mgmtH      util.Uint160
		method     = "verifyUserSig"     // the name of UserMgmt's verification method.
		callFlags  = callflag.ReadStates // depends on the permissions UserMgmt contract needs in its verification method.
	)

	// This part of Invocation script should be filled by SN.
	snInv := io.NewBufBinWriter()
	emit.Int(snInv.BinWriter, currHeight)

	// This part of the Invocation script will be filled by witness issuer.
	inv := io.NewBufBinWriter()
	emit.Bytes(inv.BinWriter, sig)

	// Verification script will be filled by witness issuer as far.
	vrf := io.NewBufBinWriter()
	emit.Bytes(inv.BinWriter, userName)
	emit.Int(vrf.BinWriter, 3)                                  // 3 is the number of UserMgmt's verification method arguments. In this script it contains of: height, sig and userName.
	emit.Opcodes(vrf.BinWriter, opcode.PACK)                    // Pack arguments to Array.
	emit.AppCallNoArgs(vrf.BinWriter, mgmtH, method, callFlags) // Call UserMgmt's verification method (assuming that this method pushes bool on stack as a result).

	// Construct the verification script and execute it via RPC:
	s := append(snInv.Bytes(), append(inv.Bytes(), vrf.Bytes()...)...)
	res, err := c.InvokeScript(s, []transaction.Signer{
		// TODO: put your account as a signer if the UserManagement account uses CheckWitness.
	})
	ok, err := unwrap.Bool(res, err)
	require.NoError(t, err)

	// That's the verification result returned by UserMgmt contract.
	require.True(t, ok)
}
@cthulhu-rider commented on GitHub (Mar 3, 2025): Neo Go code snippet demonstrating examplary authorization through new N3 scheme with contract-side key management (thx to @AnnaShaleva && @roman-khimov): ```go func TestGenerateUserMgmtWitness(t *testing.T) { chain, _, httpSrv := initServerWithInMemoryChain(t) c, err := rpcclient.New(context.Background(), httpSrv.URL, rpcclient.Options{}) require.NoError(t, err) t.Cleanup(c.Close) require.NoError(t, c.Init()) // TODO: fill these variables: var ( currHeight int64 = int64(chain.BlockHeight()) // may be current Epoch or whatever UserMgmt contract needs it to be. sig = make([]byte, keys.SignatureLen) userName = []byte("Bob") mgmtH util.Uint160 method = "verifyUserSig" // the name of UserMgmt's verification method. callFlags = callflag.ReadStates // depends on the permissions UserMgmt contract needs in its verification method. ) // This part of Invocation script should be filled by SN. snInv := io.NewBufBinWriter() emit.Int(snInv.BinWriter, currHeight) // This part of the Invocation script will be filled by witness issuer. inv := io.NewBufBinWriter() emit.Bytes(inv.BinWriter, sig) // Verification script will be filled by witness issuer as far. vrf := io.NewBufBinWriter() emit.Bytes(inv.BinWriter, userName) emit.Int(vrf.BinWriter, 3) // 3 is the number of UserMgmt's verification method arguments. In this script it contains of: height, sig and userName. emit.Opcodes(vrf.BinWriter, opcode.PACK) // Pack arguments to Array. emit.AppCallNoArgs(vrf.BinWriter, mgmtH, method, callFlags) // Call UserMgmt's verification method (assuming that this method pushes bool on stack as a result). // Construct the verification script and execute it via RPC: s := append(snInv.Bytes(), append(inv.Bytes(), vrf.Bytes()...)...) res, err := c.InvokeScript(s, []transaction.Signer{ // TODO: put your account as a signer if the UserManagement account uses CheckWitness. }) ok, err := unwrap.Bool(res, err) require.NoError(t, err) // That's the verification result returned by UserMgmt contract. require.True(t, ok) } ```
Author
Owner

@cthulhu-rider commented on GitHub (Mar 31, 2025):

it starts with https://github.com/nspcc-dev/neofs-node/pull/3216. Other signed entities are gonna be changed the same way

after discussion with @roman-khimov, it was decided to not add account field to the requests' verification headers

@cthulhu-rider commented on GitHub (Mar 31, 2025): it starts with https://github.com/nspcc-dev/neofs-node/pull/3216. Other signed entities are gonna be changed the same way after discussion with @roman-khimov, it was decided to not add account field to the requests' verification headers
Author
Owner

@cthulhu-rider commented on GitHub (Apr 8, 2025):

played in DevEnv with contract verifications

contract

package usermgt

import (
	"github.com/nspcc-dev/neo-go/pkg/interop"
	"github.com/nspcc-dev/neo-go/pkg/interop/convert"
	"github.com/nspcc-dev/neo-go/pkg/interop/native/crypto"
	"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
	"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
)

var (
	pub1 = []byte{2, 96, 75, 136, 21, 168, 7, 84, 186, 116, 188, 162, 16, 211, 118, 214, 188, 5, 137, 225, 225, 92, 160, 227, 252,
		172, 212, 243, 117, 248, 218, 100, 204}
	pub2 = []byte{2, 197, 103, 133, 220, 111, 202, 127, 226, 182, 187, 3, 65, 10, 87, 185, 99, 138, 239, 150, 226, 148, 74, 125,
		223, 141, 110, 88, 227, 175, 186, 50, 255}
)

func VerifySignature(user string, sig interop.Signature) bool {
	var pub []byte
	if ledger.CurrentIndex() == 321 {
		pub = pub1
	} else {
		pub = pub2
	}
	ntb := make([]byte, 4)
	copy(ntb, convert.ToBytes(runtime.GetNetwork()))
	signedPayload := append(ntb, runtime.GetScriptContainer().Hash...)
	return crypto.VerifyWithECDsa(signedPayload, pub, sig, crypto.Secp256r1Sha256)
}

signer:

func TestContractVerify(t *testing.T) {
	const contract = "0e65bcee6f717724be6889a6c3b15e99dc080462"
	const method = "verifySignature"
	const username = "Bob"
	payload := []byte("Hello, world!")
	usrPriv := &keys.PrivateKey{PrivateKey: ecdsa.PrivateKey{
		PublicKey: ecdsa.PublicKey{
			Curve: elliptic.P256(),
			X: new(big.Int).SetBytes([]byte{96, 75, 136, 21, 168, 7, 84, 186, 116, 188, 162, 16, 211, 118, 214, 188, 5, 137, 225,
				225, 92, 160, 227, 252, 172, 212, 243, 117, 248, 218, 100, 204}),
			Y: new(big.Int).SetBytes([]byte{110, 159, 173, 150, 197, 107, 245, 192, 43, 31, 248, 127, 94, 105, 74, 8, 160, 155, 102, 106,
				229, 80, 146, 252, 217, 179, 252, 193, 82, 184, 189, 250}),
		},
		D: new(big.Int).SetBytes([]byte{228, 153, 124, 27, 19, 179, 93, 125, 150, 103, 68, 213, 135, 142, 137, 146, 218, 221, 196, 37,
			72, 127, 76, 33, 172, 178, 215, 83, 150, 7, 50, 195}),
	}}

	c, err := rpcclient.NewWS(context.Background(), "ws://ir01.neofs.devenv:30333/ws", rpcclient.WSOptions{})
	require.NoError(t, err)
	require.NoError(t, c.Init())
	t.Cleanup(c.Close)

	// user-side
	ver, err := c.GetVersion()
	require.NoError(t, err)
	contractH, err := util.Uint160DecodeStringLE(contract)
	require.NoError(t, err)

	signed := hash.NetSha256(uint32(ver.Protocol.Network), hashedPayload(payload))
	sig := usrPriv.SignHash(signed)

	invoc := io.NewBufBinWriter()
	emit.Bytes(invoc.BinWriter, sig)
	emit.String(invoc.BinWriter, username)

	verif := io.NewBufBinWriter()
	emit.Int(verif.BinWriter, 2)
	emit.Opcodes(verif.BinWriter, opcode.PACK)
	emit.AppCallNoArgs(verif.BinWriter, contractH, method, callflag.All)

	// sys-side
	fullScript := slices.Concat(invoc.Bytes(), verif.Bytes())
	txHash := hashedPayload(payload).Hash() // payload is container, eACL, object, etc.
	txSigner := transaction.Signer{}        // needed?
	bh := &block.Header{
		Index: 322, // contract ledger.CurrentIndex returns 321
	}

	tx := transaction.NewFakeTX(fullScript, txSigner, txHash, 0)
	ok, err := unwrap.Bool(c.InvokeContainedScript(tx, bh, nil, nil))
	require.NoError(t, err)
	require.True(t, ok)
}
@cthulhu-rider commented on GitHub (Apr 8, 2025): played in DevEnv with contract verifications contract ```go package usermgt import ( "github.com/nspcc-dev/neo-go/pkg/interop" "github.com/nspcc-dev/neo-go/pkg/interop/convert" "github.com/nspcc-dev/neo-go/pkg/interop/native/crypto" "github.com/nspcc-dev/neo-go/pkg/interop/native/ledger" "github.com/nspcc-dev/neo-go/pkg/interop/runtime" ) var ( pub1 = []byte{2, 96, 75, 136, 21, 168, 7, 84, 186, 116, 188, 162, 16, 211, 118, 214, 188, 5, 137, 225, 225, 92, 160, 227, 252, 172, 212, 243, 117, 248, 218, 100, 204} pub2 = []byte{2, 197, 103, 133, 220, 111, 202, 127, 226, 182, 187, 3, 65, 10, 87, 185, 99, 138, 239, 150, 226, 148, 74, 125, 223, 141, 110, 88, 227, 175, 186, 50, 255} ) func VerifySignature(user string, sig interop.Signature) bool { var pub []byte if ledger.CurrentIndex() == 321 { pub = pub1 } else { pub = pub2 } ntb := make([]byte, 4) copy(ntb, convert.ToBytes(runtime.GetNetwork())) signedPayload := append(ntb, runtime.GetScriptContainer().Hash...) return crypto.VerifyWithECDsa(signedPayload, pub, sig, crypto.Secp256r1Sha256) } ``` signer: ```go func TestContractVerify(t *testing.T) { const contract = "0e65bcee6f717724be6889a6c3b15e99dc080462" const method = "verifySignature" const username = "Bob" payload := []byte("Hello, world!") usrPriv := &keys.PrivateKey{PrivateKey: ecdsa.PrivateKey{ PublicKey: ecdsa.PublicKey{ Curve: elliptic.P256(), X: new(big.Int).SetBytes([]byte{96, 75, 136, 21, 168, 7, 84, 186, 116, 188, 162, 16, 211, 118, 214, 188, 5, 137, 225, 225, 92, 160, 227, 252, 172, 212, 243, 117, 248, 218, 100, 204}), Y: new(big.Int).SetBytes([]byte{110, 159, 173, 150, 197, 107, 245, 192, 43, 31, 248, 127, 94, 105, 74, 8, 160, 155, 102, 106, 229, 80, 146, 252, 217, 179, 252, 193, 82, 184, 189, 250}), }, D: new(big.Int).SetBytes([]byte{228, 153, 124, 27, 19, 179, 93, 125, 150, 103, 68, 213, 135, 142, 137, 146, 218, 221, 196, 37, 72, 127, 76, 33, 172, 178, 215, 83, 150, 7, 50, 195}), }} c, err := rpcclient.NewWS(context.Background(), "ws://ir01.neofs.devenv:30333/ws", rpcclient.WSOptions{}) require.NoError(t, err) require.NoError(t, c.Init()) t.Cleanup(c.Close) // user-side ver, err := c.GetVersion() require.NoError(t, err) contractH, err := util.Uint160DecodeStringLE(contract) require.NoError(t, err) signed := hash.NetSha256(uint32(ver.Protocol.Network), hashedPayload(payload)) sig := usrPriv.SignHash(signed) invoc := io.NewBufBinWriter() emit.Bytes(invoc.BinWriter, sig) emit.String(invoc.BinWriter, username) verif := io.NewBufBinWriter() emit.Int(verif.BinWriter, 2) emit.Opcodes(verif.BinWriter, opcode.PACK) emit.AppCallNoArgs(verif.BinWriter, contractH, method, callflag.All) // sys-side fullScript := slices.Concat(invoc.Bytes(), verif.Bytes()) txHash := hashedPayload(payload).Hash() // payload is container, eACL, object, etc. txSigner := transaction.Signer{} // needed? bh := &block.Header{ Index: 322, // contract ledger.CurrentIndex returns 321 } tx := transaction.NewFakeTX(fullScript, txSigner, txHash, 0) ok, err := unwrap.Bool(c.InvokeContainedScript(tx, bh, nil, nil)) require.NoError(t, err) require.True(t, ok) } ```
Author
Owner

@cthulhu-rider commented on GitHub (Apr 8, 2025):

played in DevEnv with multi-sig

func TestMultiSigVerify(t *testing.T) {
	payload := []byte("Hello, world!")
	var privs []*keys.PrivateKey

	const n = 7
	for range n {
		k, err := keys.NewPrivateKey()
		require.NoError(t, err)
		privs = append(privs, k)
	}
	// keys should be sorted
	slices.SortFunc(privs, func(a, b *keys.PrivateKey) int { return a.PublicKey().Cmp(b.PublicKey()) })
	var pubs keys.PublicKeys
	for i := range privs {
		pubs = append(pubs, privs[i].PublicKey())
	}

	c, err := rpcclient.NewWS(context.Background(), "ws://ir01.neofs.devenv:30333/ws", rpcclient.WSOptions{})
	require.NoError(t, err)
	require.NoError(t, c.Init())
	t.Cleanup(c.Close)

	ver, err := c.GetVersion()
	require.NoError(t, err)

	// user-side
	signed := hash.NetSha256(uint32(ver.Protocol.Network), hashedPayload(payload))
	m := smartcontract.GetDefaultHonestNodeCount(n)
	var invoc []byte
	for i := range m {
		sig := privs[i].SignHash(signed)
		invocI := slices.Concat([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, sig)
		invoc = slices.Concat(invoc, invocI)
	}

	verif, err := smartcontract.CreateDefaultMultiSigRedeemScript(pubs)
	require.NoError(t, err)

	// sys-side
	fullScript := slices.Concat(invoc, verif)
	txHash := hashedPayload(payload).Hash() // payload is container, eACL, object, etc.
	txSigner := transaction.Signer{}        // needed?
	var bh *block.Header // seems like not needed for multisig

	tx := transaction.NewFakeTX(fullScript, txSigner, txHash, 0)
	ok, err := unwrap.Bool(c.InvokeContainedScript(tx, bh, nil, nil))
	require.NoError(t, err)
	require.True(t, ok)
}
@cthulhu-rider commented on GitHub (Apr 8, 2025): played in DevEnv with multi-sig ```go func TestMultiSigVerify(t *testing.T) { payload := []byte("Hello, world!") var privs []*keys.PrivateKey const n = 7 for range n { k, err := keys.NewPrivateKey() require.NoError(t, err) privs = append(privs, k) } // keys should be sorted slices.SortFunc(privs, func(a, b *keys.PrivateKey) int { return a.PublicKey().Cmp(b.PublicKey()) }) var pubs keys.PublicKeys for i := range privs { pubs = append(pubs, privs[i].PublicKey()) } c, err := rpcclient.NewWS(context.Background(), "ws://ir01.neofs.devenv:30333/ws", rpcclient.WSOptions{}) require.NoError(t, err) require.NoError(t, c.Init()) t.Cleanup(c.Close) ver, err := c.GetVersion() require.NoError(t, err) // user-side signed := hash.NetSha256(uint32(ver.Protocol.Network), hashedPayload(payload)) m := smartcontract.GetDefaultHonestNodeCount(n) var invoc []byte for i := range m { sig := privs[i].SignHash(signed) invocI := slices.Concat([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, sig) invoc = slices.Concat(invoc, invocI) } verif, err := smartcontract.CreateDefaultMultiSigRedeemScript(pubs) require.NoError(t, err) // sys-side fullScript := slices.Concat(invoc, verif) txHash := hashedPayload(payload).Hash() // payload is container, eACL, object, etc. txSigner := transaction.Signer{} // needed? var bh *block.Header // seems like not needed for multisig tx := transaction.NewFakeTX(fullScript, txSigner, txHash, 0) ok, err := unwrap.Bool(c.InvokeContainedScript(tx, bh, nil, nil)) require.NoError(t, err) require.True(t, ok) } ```
Author
Owner

@cthulhu-rider commented on GitHub (Apr 8, 2025):

@roman-khimov @AnnaShaleva pls review my examples

questions are:

  1. seems like transaction signer is only needed for contract witnesses, but we work with fake unsigned tx, so witnesses seem useless to me. I left it empty and everything works. But pay attention anyway, mb im missing some security issue
  2. i followed approach of salting payload with network magic as done for Neo txs. Works good. Is this required/useful?
  3. which FS chain height to use for check?

the most obvious values for 3:

  • creation epoch tick height for objects
  • IAT epoch height for tokens
  • current (IR) height for container payloads
  • current (SN) height for requests
@cthulhu-rider commented on GitHub (Apr 8, 2025): @roman-khimov @AnnaShaleva pls review my examples questions are: 1. seems like transaction signer is only needed for contract witnesses, but we work with fake unsigned tx, so witnesses seem useless to me. I left it empty and everything works. But pay attention anyway, mb im missing some security issue 2. i followed approach of salting payload with network magic as done for Neo txs. Works good. Is this required/useful? 3. which FS chain height to use for check? the most obvious values for 3: - creation epoch tick height for objects - IAT epoch height for tokens - current (IR) height for container payloads - current (SN) height for requests
Author
Owner

@cthulhu-rider commented on GitHub (Apr 8, 2025):

i also think system should allow the call of only one approved contract. Store its address in the network configuration (Netmap contract)?

@cthulhu-rider commented on GitHub (Apr 8, 2025): i also think system should allow the call of only one approved contract. Store its address in the network configuration (Netmap contract)?
Author
Owner

@roman-khimov commented on GitHub (Apr 8, 2025):

(haven't looked at code yet)

transaction signer is only needed for contract witnesses

No, technically verification script has access to this data as well, so they need to filled in anyway.

i followed approach of salting payload with network magic as done for Neo txs. Works good. Is this required/useful?

This prevents cross-network replay attacks, so it's definitely good.

the most obvious values for 3:

All good to me.

i also think system should allow the call of only one approved contract.

Why?

@roman-khimov commented on GitHub (Apr 8, 2025): (haven't looked at code yet) > transaction signer is only needed for contract witnesses No, technically verification script has access to this data as well, so they need to filled in anyway. > i followed approach of salting payload with network magic as done for Neo txs. Works good. Is this required/useful? This prevents cross-network replay attacks, so it's definitely good. > the most obvious values for 3: All good to me. > i also think system should allow the call of only one approved contract. Why?
Author
Owner

@roman-khimov commented on GitHub (Apr 8, 2025):

emit.String(invoc.BinWriter, username)

This should be a part of the verification script (binding the script hash to user name), but the result is going to be the same in this example.

Otherwise looks OK.

@roman-khimov commented on GitHub (Apr 8, 2025): > emit.String(invoc.BinWriter, username) This should be a part of the verification script (binding the script hash to user name), but the result is going to be the same in this example. Otherwise looks OK.
Author
Owner

@cthulhu-rider commented on GitHub (Apr 9, 2025):

This should be a part of the verification script

oh yeah, account is essentially a verif script, so we should build it from the username

@cthulhu-rider commented on GitHub (Apr 9, 2025): > This should be a part of the verification script oh yeah, account is essentially a verif script, so we should build it from the username
Author
Owner

@cthulhu-rider commented on GitHub (Apr 9, 2025):

No, technically verification script has access to this data as well, so they need to filled in anyway.

lookin at https://pkg.go.dev/github.com/nspcc-dev/neo-go@v0.108.1/pkg/core/transaction#Signer, we can definitely specify an account (verif script essentially). Others are inobvious to me speaking about general purpose scenarios. I tend to think that input can depend on particular contract call. What we can do?

i also think system should allow the call of only one approved contract.
Why?

initially i thought that malicious user could make fake call always returning true. But i forgot that this will entail an account change. So yeah, seems like not needed

@cthulhu-rider commented on GitHub (Apr 9, 2025): > No, technically verification script has access to this data as well, so they need to filled in anyway. lookin at https://pkg.go.dev/github.com/nspcc-dev/neo-go@v0.108.1/pkg/core/transaction#Signer, we can definitely specify an account (verif script essentially). Others are inobvious to me speaking about general purpose scenarios. I tend to think that input can depend on particular contract call. What we can do? > i also think system should allow the call of only one approved contract. > Why? initially i thought that malicious user could make fake call always returning `true`. But i forgot that this will entail an account change. So yeah, seems like not needed
Author
Owner

@roman-khimov commented on GitHub (Apr 9, 2025):

I tend to think that input can depend on particular contract call

It shouldn't. In this case we always have a single signer. Let it be in transaction and that's it (with "none" scope).

@roman-khimov commented on GitHub (Apr 9, 2025): > I tend to think that input can depend on particular contract call It shouldn't. In this case we always have a single signer. Let it be in transaction and that's it (with "none" scope).
Author
Owner

@cthulhu-rider commented on GitHub (Apr 10, 2025):

https://github.com/nspcc-dev/neofs-testcases comminicate with the system via CLI. Currently, there is no way to pass prepared signature to container create and so on. I think this opportunity will be useful in general. In a separate issue

@cthulhu-rider commented on GitHub (Apr 10, 2025): https://github.com/nspcc-dev/neofs-testcases comminicate with the system via CLI. Currently, there is no way to pass prepared signature to `container create` and so on. I think this opportunity will be useful in general. In a separate issue * refs #1323
Author
Owner

@cthulhu-rider commented on GitHub (Apr 16, 2025):

TODO: pass context to verification funcs for RPC verifications

@cthulhu-rider commented on GitHub (Apr 16, 2025): TODO: pass context to verification funcs for RPC verifications
Author
Owner

@cthulhu-rider commented on GitHub (Apr 16, 2025):

FS chain height resolution for objects (from creation epoch) and tokens (from IAT claim) is a bit challenging for now. Netmap contract provides https://pkg.go.dev/github.com/nspcc-dev/neofs-contract/contracts/netmap#LastEpochBlock only, which does not fit in general

i see next options:

  1. add new GetEpochTickBlock(epoch uint64) (block int) method to the contract. Pros: easy to use. Cons: 1) contract changes, its storage grows 2) doubt that there will be enough storage for a huge number of epochs
  2. restore height mapping using notifaction scroll

i like 2 more since we have all tools for it

@roman-khimov what do u think?

@cthulhu-rider commented on GitHub (Apr 16, 2025): FS chain height resolution for objects (from creation epoch) and tokens (from IAT claim) is a bit challenging for now. Netmap contract provides https://pkg.go.dev/github.com/nspcc-dev/neofs-contract/contracts/netmap#LastEpochBlock only, which does not fit in general i see next options: 1. add new `GetEpochTickBlock(epoch uint64) (block int)` method to the contract. Pros: easy to use. Cons: 1) contract changes, its storage grows 2) doubt that there will be enough storage for a huge number of epochs 2. restore height mapping using notifaction scroll i like 2 more since we have all tools for it @roman-khimov what do u think?
Author
Owner

@roman-khimov commented on GitHub (Apr 16, 2025):

2 doesn't work in case nodes cut the tail. 1 is actually not that expensive, one record per hour is just 8760 records per year. I'd try that for now, maybe we'll create some better scheme in future.

@roman-khimov commented on GitHub (Apr 16, 2025): 2 doesn't work in case nodes cut the tail. 1 is actually not that expensive, one record per hour is just 8760 records per year. I'd try that for now, maybe we'll create some better scheme in future.
Author
Owner

@cthulhu-rider commented on GitHub (Apr 16, 2025):

nodes cut the tail

oh, rly a problem. Then new method is the only option for now

@cthulhu-rider commented on GitHub (Apr 16, 2025): > nodes cut the tail oh, rly a problem. Then new method is the only option for now
Author
Owner

@cthulhu-rider commented on GitHub (Apr 23, 2025):

TODO: check simple- and multi-sig scripts locally, do not make RPC

@cthulhu-rider commented on GitHub (Apr 23, 2025): TODO: check simple- and multi-sig scripts locally, do not make RPC
Author
Owner

@roman-khimov commented on GitHub (Apr 23, 2025):

Can be a separate issue, it's an optimization. Just like #3243.

@roman-khimov commented on GitHub (Apr 23, 2025): Can be a separate issue, it's an optimization. Just like #3243.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
nspcc-dev/neofs-node#1368
No description provided.