forked from TrueCloudLab/frostfs-node
[#975] morph/client: Use 4 witnesses in notary request
Add invoker witness as the third witness. Required for netmap methods checks. Signed-off-by: Alex Vanin <alexey@nspcc.ru>
This commit is contained in:
parent
6f23dbfefe
commit
a0ff5b1bf8
2 changed files with 71 additions and 22 deletions
|
@ -441,7 +441,11 @@ func (c *Client) notaryInvoke(committee, invokedByAlpha bool, contract util.Uint
|
||||||
_, n := mn(alphabetList, committee)
|
_, n := mn(alphabetList, committee)
|
||||||
u8n := uint8(n)
|
u8n := uint8(n)
|
||||||
|
|
||||||
cosigners, err := c.notaryCosigners(alphabetList, committee)
|
if !invokedByAlpha {
|
||||||
|
u8n++
|
||||||
|
}
|
||||||
|
|
||||||
|
cosigners, err := c.notaryCosigners(invokedByAlpha, alphabetList, committee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -510,7 +514,7 @@ func (c *Client) notaryInvoke(committee, invokedByAlpha bool, contract util.Uint
|
||||||
err = c.client.AddNetworkFee(
|
err = c.client.AddNetworkFee(
|
||||||
mainTx,
|
mainTx,
|
||||||
notaryFee,
|
notaryFee,
|
||||||
c.notaryAccounts(multiaddrAccount)...,
|
c.notaryAccounts(invokedByAlpha, multiaddrAccount)...,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -538,8 +542,8 @@ func (c *Client) notaryInvoke(committee, invokedByAlpha bool, contract util.Uint
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) notaryCosigners(ir []*keys.PublicKey, committee bool) ([]transaction.Signer, error) {
|
func (c *Client) notaryCosigners(invokedByAlpha bool, ir []*keys.PublicKey, committee bool) ([]transaction.Signer, error) {
|
||||||
s := make([]transaction.Signer, 0, 3)
|
s := make([]transaction.Signer, 0, 4)
|
||||||
|
|
||||||
// first we have proxy contract signature, as it will pay for the execution
|
// first we have proxy contract signature, as it will pay for the execution
|
||||||
s = append(s, transaction.Signer{
|
s = append(s, transaction.Signer{
|
||||||
|
@ -563,6 +567,16 @@ func (c *Client) notaryCosigners(ir []*keys.PublicKey, committee bool) ([]transa
|
||||||
AllowedGroups: c.signer.AllowedGroups,
|
AllowedGroups: c.signer.AllowedGroups,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if !invokedByAlpha {
|
||||||
|
// then we have invoker signature
|
||||||
|
s = append(s, transaction.Signer{
|
||||||
|
Account: hash.Hash160(c.acc.GetVerificationScript()),
|
||||||
|
Scopes: c.signer.Scopes,
|
||||||
|
AllowedContracts: c.signer.AllowedContracts,
|
||||||
|
AllowedGroups: c.signer.AllowedGroups,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// last one is a placeholder for notary contract signature
|
// last one is a placeholder for notary contract signature
|
||||||
s = append(s, transaction.Signer{
|
s = append(s, transaction.Signer{
|
||||||
Account: c.notary.notary,
|
Account: c.notary.notary,
|
||||||
|
@ -572,12 +586,12 @@ func (c *Client) notaryCosigners(ir []*keys.PublicKey, committee bool) ([]transa
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) notaryAccounts(multiaddr *wallet.Account) []*wallet.Account {
|
func (c *Client) notaryAccounts(invokedByAlpha bool, multiaddr *wallet.Account) []*wallet.Account {
|
||||||
if multiaddr == nil {
|
if multiaddr == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
a := make([]*wallet.Account, 0, 3)
|
a := make([]*wallet.Account, 0, 4)
|
||||||
|
|
||||||
// first we have proxy account, as it will pay for the execution
|
// first we have proxy account, as it will pay for the execution
|
||||||
a = append(a, &wallet.Account{
|
a = append(a, &wallet.Account{
|
||||||
|
@ -589,6 +603,11 @@ func (c *Client) notaryAccounts(multiaddr *wallet.Account) []*wallet.Account {
|
||||||
// then we have inner ring multiaddress account
|
// then we have inner ring multiaddress account
|
||||||
a = append(a, multiaddr)
|
a = append(a, multiaddr)
|
||||||
|
|
||||||
|
if !invokedByAlpha {
|
||||||
|
// then we have invoker account
|
||||||
|
a = append(a, c.acc)
|
||||||
|
}
|
||||||
|
|
||||||
// last one is a placeholder for notary contract account
|
// last one is a placeholder for notary contract account
|
||||||
a = append(a, &wallet.Account{
|
a = append(a, &wallet.Account{
|
||||||
Contract: &wallet.Contract{},
|
Contract: &wallet.Contract{},
|
||||||
|
@ -602,7 +621,7 @@ func (c *Client) notaryWitnesses(invokedByAlpha bool, multiaddr *wallet.Account,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
w := make([]transaction.Witness, 0, 3)
|
w := make([]transaction.Witness, 0, 4)
|
||||||
|
|
||||||
// first we have empty proxy witness, because notary will execute `Verify`
|
// first we have empty proxy witness, because notary will execute `Verify`
|
||||||
// method on the proxy contract to check witness
|
// method on the proxy contract to check witness
|
||||||
|
@ -639,6 +658,19 @@ func (c *Client) notaryWitnesses(invokedByAlpha bool, multiaddr *wallet.Account,
|
||||||
VerificationScript: multiaddr.GetVerificationScript(),
|
VerificationScript: multiaddr.GetVerificationScript(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if !invokedByAlpha {
|
||||||
|
// then we have invoker witness
|
||||||
|
invokeScript = append(
|
||||||
|
[]byte{byte(opcode.PUSHDATA1), 64},
|
||||||
|
c.acc.PrivateKey().SignHashable(uint32(c.client.GetNetwork()), tx)...,
|
||||||
|
)
|
||||||
|
|
||||||
|
w = append(w, transaction.Witness{
|
||||||
|
InvocationScript: invokeScript,
|
||||||
|
VerificationScript: c.acc.GetVerificationScript(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// last one is a placeholder for notary contract witness
|
// last one is a placeholder for notary contract witness
|
||||||
w = append(w, transaction.Witness{
|
w = append(w, transaction.Witness{
|
||||||
InvocationScript: append(
|
InvocationScript: append(
|
||||||
|
|
|
@ -25,6 +25,7 @@ var (
|
||||||
errUnexpectedCosignersAmount = errors.New("received main tx has unexpected amount of cosigners")
|
errUnexpectedCosignersAmount = errors.New("received main tx has unexpected amount of cosigners")
|
||||||
errIncorrectAlphabetSigner = errors.New("received main tx has incorrect Alphabet signer")
|
errIncorrectAlphabetSigner = errors.New("received main tx has incorrect Alphabet signer")
|
||||||
errIncorrectProxyWitnesses = errors.New("received main tx has non-empty Proxy witnesses")
|
errIncorrectProxyWitnesses = errors.New("received main tx has non-empty Proxy witnesses")
|
||||||
|
errIncorrectInvokerWitnesses = errors.New("received main tx has empty Invoker witness")
|
||||||
errIncorrectAlphabet = errors.New("received main tx has incorrect Alphabet verification")
|
errIncorrectAlphabet = errors.New("received main tx has incorrect Alphabet verification")
|
||||||
errIncorrectNotaryPlaceholder = errors.New("received main tx has incorrect Notary contract placeholder")
|
errIncorrectNotaryPlaceholder = errors.New("received main tx has incorrect Notary contract placeholder")
|
||||||
errIncorrectAttributesAmount = errors.New("received main tx has incorrect attributes amount")
|
errIncorrectAttributesAmount = errors.New("received main tx has incorrect attributes amount")
|
||||||
|
@ -104,12 +105,16 @@ func notaryPreparator(prm PreparatorPrm) NotaryPreparator {
|
||||||
// since every notary call is a new notary request in fact.
|
// since every notary call is a new notary request in fact.
|
||||||
func (p Preparator) Prepare(nr *payload.P2PNotaryRequest) (NotaryEvent, error) {
|
func (p Preparator) Prepare(nr *payload.P2PNotaryRequest) (NotaryEvent, error) {
|
||||||
// notary request's main tx is expected to have
|
// notary request's main tx is expected to have
|
||||||
// exactly three witnesses: one for proxy contract,
|
// three or four witnesses: one for proxy contract,
|
||||||
// one for notary's invoker and one is for notary
|
// one for alphabet multisignature, one optional for
|
||||||
// contract
|
// notary's invoker and one is for notary contract
|
||||||
if len(nr.MainTransaction.Scripts) != 3 {
|
ln := len(nr.MainTransaction.Scripts)
|
||||||
|
switch ln {
|
||||||
|
case 3, 4:
|
||||||
|
default:
|
||||||
return nil, errUnexpectedWitnessAmount
|
return nil, errUnexpectedWitnessAmount
|
||||||
}
|
}
|
||||||
|
invokerWitness := ln == 4
|
||||||
|
|
||||||
// alphabet node should handle only notary requests
|
// alphabet node should handle only notary requests
|
||||||
// that have been sent unsigned(by storage nodes) =>
|
// that have been sent unsigned(by storage nodes) =>
|
||||||
|
@ -126,19 +131,19 @@ func (p Preparator) Prepare(nr *payload.P2PNotaryRequest) (NotaryEvent, error) {
|
||||||
return nil, fmt.Errorf("could not fetch Alphabet public keys: %w", err)
|
return nil, fmt.Errorf("could not fetch Alphabet public keys: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = p.validateCosigners(nr.MainTransaction.Signers, currentAlphabet)
|
err = p.validateCosigners(ln, nr.MainTransaction.Signers, currentAlphabet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate main TX's notary attribute
|
// validate main TX's notary attribute
|
||||||
err = p.validateAttributes(nr.MainTransaction.Attributes, currentAlphabet)
|
err = p.validateAttributes(nr.MainTransaction.Attributes, currentAlphabet, invokerWitness)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate main TX's witnesses
|
// validate main TX's witnesses
|
||||||
err = p.validateWitnesses(nr.MainTransaction.Scripts, currentAlphabet)
|
err = p.validateWitnesses(nr.MainTransaction.Scripts, currentAlphabet, invokerWitness)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -303,8 +308,8 @@ func (p Preparator) validateExpiration(fbTX *transaction.Transaction) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Preparator) validateCosigners(s []transaction.Signer, alphaKeys keys.PublicKeys) error {
|
func (p Preparator) validateCosigners(expected int, s []transaction.Signer, alphaKeys keys.PublicKeys) error {
|
||||||
if len(s) != 3 {
|
if len(s) != expected {
|
||||||
return errUnexpectedCosignersAmount
|
return errUnexpectedCosignersAmount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,7 +325,7 @@ func (p Preparator) validateCosigners(s []transaction.Signer, alphaKeys keys.Pub
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Preparator) validateWitnesses(w []transaction.Witness, alphaKeys keys.PublicKeys) error {
|
func (p Preparator) validateWitnesses(w []transaction.Witness, alphaKeys keys.PublicKeys, invokerWitness bool) error {
|
||||||
// the first one(proxy contract) must have empty
|
// the first one(proxy contract) must have empty
|
||||||
// witnesses
|
// witnesses
|
||||||
if len(w[0].VerificationScript)+len(w[0].InvocationScript) != 0 {
|
if len(w[0].VerificationScript)+len(w[0].InvocationScript) != 0 {
|
||||||
|
@ -338,23 +343,35 @@ func (p Preparator) validateWitnesses(w []transaction.Witness, alphaKeys keys.Pu
|
||||||
return errIncorrectAlphabet
|
return errIncorrectAlphabet
|
||||||
}
|
}
|
||||||
|
|
||||||
// the third one must be a placeholder for notary
|
if invokerWitness {
|
||||||
// contract witness
|
// the optional third one must be an invoker witness
|
||||||
if !bytes.Equal(w[2].InvocationScript, p.dummyInvocationScript) || len(w[2].VerificationScript) != 0 {
|
if len(w[2].VerificationScript)+len(w[2].InvocationScript) == 0 {
|
||||||
|
return errIncorrectInvokerWitnesses
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the last one must be a placeholder for notary contract witness
|
||||||
|
last := len(w) - 1
|
||||||
|
if !bytes.Equal(w[last].InvocationScript, p.dummyInvocationScript) || len(w[last].VerificationScript) != 0 {
|
||||||
return errIncorrectNotaryPlaceholder
|
return errIncorrectNotaryPlaceholder
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Preparator) validateAttributes(aa []transaction.Attribute, alphaKeys keys.PublicKeys) error {
|
func (p Preparator) validateAttributes(aa []transaction.Attribute, alphaKeys keys.PublicKeys, invokerWitness bool) error {
|
||||||
// main tx must have exactly one attribute
|
// main tx must have exactly one attribute
|
||||||
if len(aa) != 1 {
|
if len(aa) != 1 {
|
||||||
return errIncorrectAttributesAmount
|
return errIncorrectAttributesAmount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expectedN := uint8(len(alphaKeys))
|
||||||
|
if invokerWitness {
|
||||||
|
expectedN++
|
||||||
|
}
|
||||||
|
|
||||||
val, ok := aa[0].Value.(*transaction.NotaryAssisted)
|
val, ok := aa[0].Value.(*transaction.NotaryAssisted)
|
||||||
if !ok || val.NKeys != uint8(len(alphaKeys)) {
|
if !ok || val.NKeys != expectedN {
|
||||||
return errIncorrectAttribute
|
return errIncorrectAttribute
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue