From 9921358f09e0d574627b44780dddb9a3745068bf Mon Sep 17 00:00:00 2001
From: Pavel Karpy <carpawell@nspcc.ru>
Date: Fri, 3 Sep 2021 19:23:18 +0300
Subject: [PATCH] [#770] pkg/morph: Add `NotarySignAndInvokeTX`

Add `NotarySignAndInvokeTX` method to morph
client. This function allows invoking notary
request with passed main TX(not creating a
new one). It signs passed main TX with
client's key.

Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
---
 pkg/morph/client/notary.go | 45 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/pkg/morph/client/notary.go b/pkg/morph/client/notary.go
index 998ddac03..a5a6e0b50 100644
--- a/pkg/morph/client/notary.go
+++ b/pkg/morph/client/notary.go
@@ -296,6 +296,51 @@ func (c *Client) NotaryInvokeNotAlpha(contract util.Uint160, fee fixedn.Fixed8,
 	return c.notaryInvoke(false, false, contract, method, args...)
 }
 
+// NotarySignAndInvokeTX signs and sends notary request that was received from
+// Notary service.
+// NOTE: does not fallback to simple `Invoke()`. Expected to be used only for
+// TXs retrieved from the received notary requests.
+func (c *Client) NotarySignAndInvokeTX(mainTx *transaction.Transaction) error {
+	if c.multiClient != nil {
+		return c.multiClient.iterateClients(func(c *Client) error {
+			return c.NotarySignAndInvokeTX(mainTx)
+		})
+	}
+
+	alphabetList, err := c.notary.alphabetSource()
+	if err != nil {
+		return fmt.Errorf("could not fetch current alphabet keys: %w", err)
+	}
+
+	multiaddrAccount, err := c.notaryMultisigAccount(alphabetList, false, true)
+	if err != nil {
+		return err
+	}
+
+	// mainTX is expected to be pre-validated: second witness must exist and be empty
+	mainTx.Scripts[1].VerificationScript = multiaddrAccount.GetVerificationScript()
+	mainTx.Scripts[1].InvocationScript = append(
+		[]byte{byte(opcode.PUSHDATA1), 64},
+		multiaddrAccount.PrivateKey().SignHashable(uint32(c.client.GetNetwork()), mainTx)...,
+	)
+
+	resp, err := c.client.SignAndPushP2PNotaryRequest(mainTx,
+		[]byte{byte(opcode.RET)},
+		-1,
+		0,
+		c.notary.fallbackTime,
+		c.acc)
+	if err != nil && !alreadyOnChainError(err) {
+		return err
+	}
+
+	c.logger.Debug("notary request with prepared main TX invoked",
+		zap.Uint32("fallback_valid_for", c.notary.fallbackTime),
+		zap.Stringer("tx_hash", resp.Hash().Reverse()))
+
+	return nil
+}
+
 func (c *Client) notaryInvokeAsCommittee(method string, args ...interface{}) error {
 	designate, err := c.GetDesignateHash()
 	if err != nil {