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/push object meta to contracts #3044

Merged
merged 13 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 0 additions & 37 deletions pkg/innerring/processors/container.go

This file was deleted.

3 changes: 1 addition & 2 deletions pkg/innerring/processors/container/process_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"fmt"

"github.com/nspcc-dev/neo-go/pkg/network/payload"
"github.com/nspcc-dev/neofs-node/pkg/innerring/processors"
cntClient "github.com/nspcc-dev/neofs-node/pkg/morph/client/container"
"github.com/nspcc-dev/neofs-node/pkg/morph/event"
containerEvent "github.com/nspcc-dev/neofs-node/pkg/morph/event/container"
Expand Down Expand Up @@ -135,7 +134,7 @@ func (cp *Processor) approvePutContainer(ctx *putContainerContext) {
replicas = append(replicas, policy.ReplicaNumberByIndex(i))
}

err = processors.UpdatePlacementVectors(ctx.cID, cp.cnrClient, vectors, replicas)
err = cp.cnrClient.UpdateContainerPlacement(ctx.cID, vectors, replicas)
if err != nil {
cp.log.Error("could not update Container contract", zap.Stringer("cid", ctx.cID), zap.Error(err))
return
Expand Down
3 changes: 1 addition & 2 deletions pkg/innerring/processors/netmap/process_epoch.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package netmap
import (
"fmt"

"github.com/nspcc-dev/neofs-node/pkg/innerring/processors"
"github.com/nspcc-dev/neofs-node/pkg/innerring/processors/audit"
"github.com/nspcc-dev/neofs-node/pkg/innerring/processors/governance"
"github.com/nspcc-dev/neofs-node/pkg/innerring/processors/settlement"
Expand Down Expand Up @@ -115,7 +114,7 @@ func (np *Processor) updatePlacementInContract(nm netmap.NetMap, l *zap.Logger)
replicas = append(replicas, policy.ReplicaNumberByIndex(i))
}

err = processors.UpdatePlacementVectors(cID, np.containerWrp, vectors, replicas)
err = np.containerWrp.UpdateContainerPlacement(cID, vectors, replicas)
if err != nil {
l.Error("can't put placement vectors to Container contract", zap.Error(err))
continue
Expand Down
73 changes: 51 additions & 22 deletions pkg/morph/client/container/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,49 @@ package container

import (
"fmt"
"slices"
"strings"

"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neofs-node/pkg/morph/client"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
)

// AddNextEpochNodes registers public keys as a container's placement vector
// with specified index. Registration must be finished with final
// [Client.CommitContainerListUpdate] call. Always sends a notary request with
// Alphabet multi-signature.
func (c *Client) AddNextEpochNodes(cid cid.ID, placementIndex int, nodesKeys [][]byte) error {
if len(nodesKeys) == 0 {
return errNilArgument
// UpdateContainerPlacement registers public keys as a container's placement
// vectors. Always sends a notary request with Alphabet multi-signature.
// Number of vectors must equal number of replicas. Empty vectors removes
// container placement from the contract.
func (c *Client) UpdateContainerPlacement(cid cid.ID, vectors [][]netmap.NodeInfo, replicas []uint32) error {
if len(vectors) == 0 {
return c.dropPlacement(cid[:])
}

prm := client.InvokePrm{}
prm.SetMethod(addNextEpochNodes)
prm.SetArgs(cid, placementIndex, nodesKeys)
prm.RequireAlphabetSignature()
cnrHash := c.client.ContractAddress()
b := smartcontract.NewBuilder()

err := c.client.Invoke(prm)
for i, vector := range vectors {
b.InvokeMethod(cnrHash, addNextEpochNodes, cid[:], i, toAnySlice(pubKeys(vector)))
}
b.InvokeMethod(cnrHash, commitContainerListUpdate, cid[:], toAnySlice(replicas))

script, err := b.Script()
if err != nil {
return fmt.Errorf("could not invoke method (%s): %w", addNextEpochNodes, err)
return fmt.Errorf("building TX script: %w", err)
}

err = c.client.RunAlphabetNotaryScript(script)
if err != nil {
return fmt.Errorf("could not invoke alphabet script: %w", err)
}

return nil
}

// CommitContainerListUpdate finishes container placement updates for the current
// epoch made by former [Client.AddNextEpochNodes] calls. Always sends a notary
// request with Alphabet multi-signature.
func (c *Client) CommitContainerListUpdate(cid cid.ID, replicas []uint32) error {
if len(replicas) == 0 {
return errNilArgument
}

func (c *Client) dropPlacement(cid []byte) error {
prm := client.InvokePrm{}
prm.SetMethod(commitContainerListUpdate)
prm.SetArgs(cid, replicas)
prm.SetArgs(cid, nil)
prm.RequireAlphabetSignature()

err := c.client.Invoke(prm)
Expand All @@ -49,3 +54,27 @@ func (c *Client) CommitContainerListUpdate(cid cid.ID, replicas []uint32) error

return nil
}

func pubKeys(nodes []netmap.NodeInfo) [][]byte {
res := make([][]byte, 0, len(nodes))
for _, node := range nodes {
res = append(res, node.PublicKey())
}

// arrays take parts in transaction that should be multi-singed, so order
// is important to be the same
slices.SortFunc(res, func(a, b []byte) int {
Copy link
Member

Choose a reason for hiding this comment

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

slices.SortFunc(res, bytes.Compare)

return strings.Compare(string(a), string(b))
})

return res
}

func toAnySlice[T any](vv []T) []any {
res := make([]any, 0, len(vv))
for _, v := range vv {
res = append(res, v)
}

return res
}
52 changes: 52 additions & 0 deletions pkg/morph/client/notary.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,58 @@ func (c *Client) notaryInvoke(committee, invokedByAlpha bool, contract util.Uint
return nil
}

func (c *Client) runAlphabetNotaryScript(script []byte, nonce uint32) error {
if c.notary == nil {
panic("notary support is not enabled")
}

var conn = c.conn.Load()
if conn == nil {
return ErrConnectionLost
}

alphabetList, err := c.notary.alphabetSource() // prepare arguments for test invocation
if err != nil {
return err
}

until, err := c.notaryTxValidationLimit(conn)
if err != nil {
return err
}

cosigners, err := c.notaryCosigners(false, alphabetList, false)
if err != nil {
return err
}

nAct, err := notary.NewActor(conn.client, cosigners, c.acc)
if err != nil {
return err
}

mainH, fbH, untilActual, err := nAct.Notarize(nAct.MakeTunedRun(script, nil, func(r *result.Invoke, t *transaction.Transaction) error {
if r.State != vmstate.Halt.String() {
return &notHaltStateError{state: r.State, exception: r.FaultException}
}

t.ValidUntilBlock = until
t.Nonce = nonce

return nil
}))
if err != nil && !alreadyOnChainError(err) {
return err
}

c.logger.Debug("notary request based on script invoked",
zap.Uint32("valid_until_block", untilActual),
zap.String("tx_hash", mainH.StringLE()),
zap.String("fallback_hash", fbH.StringLE()))

return nil
}

func (c *Client) notaryCosigners(invokedByAlpha bool, ir []*keys.PublicKey, committee bool) ([]actor.SignerAccount, error) {
multiaddrAccount, err := c.notaryMultisigAccount(ir, committee, invokedByAlpha)
if err != nil {
Expand Down
10 changes: 10 additions & 0 deletions pkg/morph/client/static.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,16 @@ func (s StaticClient) Invoke(prm InvokePrm) error {
)
}

// RunAlphabetNotaryScript invokes script by sending tx to notary contract in
// blockchain. Fallback tx is a `RET`. Panics if Notary support is not enabled.
// TX is signed with internal key, 2/3+1 multisigners are expected.
func (s StaticClient) RunAlphabetNotaryScript(sc []byte) error {
// default nonce for Alphabet transactions that must be send asynchronous;
// it is chosen to be the same as in Invoke method
const nonce = 1
return s.client.runAlphabetNotaryScript(sc, nonce)
}

// TestInvokePrm groups parameters of the TestInvoke operation.
type TestInvokePrm struct {
method string
Expand Down