github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/native/native_test/cryptolib_verification_test.go (about)

     1  package native_test
     2  
     3  import (
     4  	"math/big"
     5  	"sort"
     6  	"testing"
     7  
     8  	"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
     9  	"github.com/nspcc-dev/neo-go/pkg/core/native"
    10  	"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
    11  	"github.com/nspcc-dev/neo-go/pkg/core/state"
    12  	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
    13  	"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
    14  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    15  	"github.com/nspcc-dev/neo-go/pkg/io"
    16  	"github.com/nspcc-dev/neo-go/pkg/neotest"
    17  	"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
    18  	"github.com/nspcc-dev/neo-go/pkg/util"
    19  	"github.com/nspcc-dev/neo-go/pkg/vm/emit"
    20  	"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  // TestCryptoLib_KoblitzVerificationScript builds transaction with custom witness that contains
    25  // the Koblitz tx signature bytes and Koblitz signature verification script.
    26  // This test ensures that transaction signed by Koblitz key passes verification and can
    27  // be successfully accepted to the chain.
    28  func TestCryptoLib_KoblitzVerificationScript(t *testing.T) {
    29  	check := func(
    30  		t *testing.T,
    31  		buildVerificationScript func(t *testing.T, pub *keys.PublicKey) []byte,
    32  		constructMsg func(t *testing.T, magic uint32, tx hash.Hashable) []byte,
    33  	) {
    34  		c := newGasClient(t)
    35  		gasInvoker := c.WithSigners(c.Committee)
    36  		e := c.Executor
    37  
    38  		// Consider the user that is able to sign txs only with Secp256k1 private key.
    39  		// Let this user build, sign and push a GAS transfer transaction from its account
    40  		// to some other account.
    41  		pk, err := keys.NewSecp256k1PrivateKey()
    42  		require.NoError(t, err)
    43  
    44  		// Firstly, we need to build the N3 user's account address based on the user's public key.
    45  		// The address itself is Hash160 from the verification script corresponding to the user's public key.
    46  		// Since user's private key belongs to Koblitz curve, we can't use System.Crypto.CheckSig interop
    47  		// in the verification script. Likely, we have a 'verifyWithECDsa' method in native CriptoLib contract
    48  		// that is able to check Koblitz signature. So let's build custom verification script based on this call.
    49  		// The script should call 'verifyWithECDsa' method of native CriptoLib contract with Koblitz curve identifier
    50  		// and check the provided message signature against the user's Koblitz public key.
    51  		vrfBytes := buildVerificationScript(t, pk.PublicKey())
    52  
    53  		// Construct the user's account script hash. It's effectively a verification script hash.
    54  		from := hash.Hash160(vrfBytes)
    55  
    56  		// Supply this account with some initial balance so that the user is able to pay for his transactions.
    57  		gasInvoker.Invoke(t, true, "transfer", c.Committee.ScriptHash(), from, 10000_0000_0000, nil)
    58  
    59  		// Construct transaction that transfers 5 GAS from the user's account to some other account.
    60  		to := util.Uint160{1, 2, 3}
    61  		amount := 5
    62  		tx := gasInvoker.PrepareInvokeNoSign(t, "transfer", from, to, amount, nil)
    63  		tx.Signers = []transaction.Signer{
    64  			{
    65  				Account: from,
    66  				Scopes:  transaction.CalledByEntry,
    67  			},
    68  		}
    69  		neotest.AddNetworkFee(t, e.Chain, tx)
    70  		neotest.AddSystemFee(e.Chain, tx, -1)
    71  
    72  		// Add some more network fee to pay for the witness verification. This value may be calculated precisely,
    73  		// but let's keep some inaccurate value for the test.
    74  		tx.NetworkFee += 540_0000
    75  
    76  		// This transaction (along with the network magic) should be signed by the user's Koblitz private key.
    77  		msg := constructMsg(t, uint32(e.Chain.GetConfig().Magic), tx)
    78  
    79  		// The user has to sign the hash of the message by his Koblitz key.
    80  		// Please, note that this Keccak256 hash may easily be replaced by sha256 hash if needed.
    81  		signature := pk.SignHash(native.Keccak256(msg))
    82  
    83  		// Ensure that signature verification passes. This line here is just for testing purposes,
    84  		// it won't be present in the real code.
    85  		require.True(t, pk.PublicKey().Verify(signature, native.Keccak256(msg).BytesBE()))
    86  
    87  		// Build invocation witness script for the user's account.
    88  		invBytes := buildKoblitzInvocationScript(t, [][]byte{signature})
    89  
    90  		// Construct witness for signer #0 (the user itself).
    91  		tx.Scripts = []transaction.Witness{
    92  			{
    93  				InvocationScript:   invBytes,
    94  				VerificationScript: vrfBytes,
    95  			},
    96  		}
    97  
    98  		// Add transaction to the chain. No error is expected on new block addition. Note, that this line performs
    99  		// all those checks that are executed during transaction acceptance in the real network.
   100  		e.AddNewBlock(t, tx)
   101  
   102  		// Double-check: ensure funds have been transferred.
   103  		e.CheckGASBalance(t, to, big.NewInt(int64(amount)))
   104  	}
   105  
   106  	// The proposed preferable witness verification script
   107  	// (110 bytes, 2154270 GAS including Invocation script execution).
   108  	// The user has to sign the keccak256([4-bytes-network-magic-LE, txHash-bytes-BE]).
   109  	check(t, buildKoblitzVerificationScript, constructMessage)
   110  
   111  	// Below presented some variations of verification scripts that were also considered, but
   112  	// they are not as good as the first one.
   113  
   114  	// The simplest witness verification script with low length and low execution cost
   115  	// (98 bytes, 2092530 GAS including Invocation script execution).
   116  	// The user has to sign the keccak256([var-bytes-network-magic, txHash-bytes-BE]).
   117  	check(t, buildKoblitzVerificationScriptSimpleSingleHash, constructMessageNoHash)
   118  
   119  	// Even more simple witness verification script with low length and low execution cost
   120  	// (95 bytes, 2092320 GAS including Invocation script execution).
   121  	// The user has to sign the keccak256([var-bytes-network-magic, txHash-bytes-BE]).
   122  	// The difference is that network magic is a static value, thus, both verification script and
   123  	// user address are network-specific.
   124  	check(t, buildKoblitzVerificationScriptSimpleSingleHashStaticMagic, constructMessageNoHash)
   125  
   126  	// More complicated verification script with higher length and higher execution cost
   127  	// (136 bytes, 4120620 GAS including Invocation script execution).
   128  	// The user has to sign the keccak256(sha256([var-bytes-network-magic, txHash-bytes-BE])).
   129  	check(t, buildKoblitzVerificationScriptSimple, constructMessageSimple)
   130  
   131  	// Witness verification script that follows the existing standard CheckSig account generation rules
   132  	// and has larger length and higher execution cost.
   133  	// (186 bytes, 5116020 GAS including Invocation script execution).
   134  	// The user has to sign the keccak256(sha256([4-bytes-network-magic-LE, txHash-bytes-BE]))
   135  	check(t, buildKoblitzVerificationScriptCompat, constructMessageCompat)
   136  }
   137  
   138  // buildKoblitzVerificationScript builds witness verification script for Koblitz public key.
   139  // This method checks
   140  //
   141  //	keccak256([4-bytes-network-magic-LE, txHash-bytes-BE])
   142  //
   143  // instead of (comparing with N3)
   144  //
   145  //	sha256([4-bytes-network-magic-LE, txHash-bytes-BE]).
   146  func buildKoblitzVerificationScript(t *testing.T, pub *keys.PublicKey) []byte {
   147  	criptoLibH := state.CreateNativeContractHash(nativenames.CryptoLib)
   148  
   149  	// vrf is witness verification script corresponding to the pub.
   150  	vrf := io.NewBufBinWriter()
   151  	emit.Int(vrf.BinWriter, int64(native.Secp256k1Keccak256)) // push Koblitz curve identifier.
   152  	emit.Opcodes(vrf.BinWriter, opcode.SWAP)                  // swap curve identifier with the signature.
   153  	emit.Bytes(vrf.BinWriter, pub.Bytes())                    // emit the caller's public key.
   154  	// Construct and push the signed message. The signed message is effectively the network-dependent transaction hash,
   155  	// i.e. msg = [4-network-magic-bytes-LE, tx-hash-BE]
   156  	// Firstly, retrieve network magic (it's uint32 wrapped into BigInteger and represented as Integer stackitem on stack).
   157  	emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetNetwork) // push network magic (Integer stackitem), can have 0-5 bytes length serialized.
   158  	// Convert network magic to 4-bytes-length LE byte array representation.
   159  	emit.Int(vrf.BinWriter, 0x100000000)
   160  	emit.Opcodes(vrf.BinWriter, opcode.ADD, // some new number that is 5 bytes at least when serialized, but first 4 bytes are intact network value (LE).
   161  		opcode.PUSH4, opcode.LEFT) // cut the first 4 bytes out of a number that is at least 5 bytes long, the result is 4-bytes-length LE network representation.
   162  	// Retrieve executing transaction hash.
   163  	emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetScriptContainer) // push the script container (executing transaction, actually).
   164  	emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.PICKITEM)                // pick 0-th transaction item (the transaction hash).
   165  	// Concatenate network magic and transaction hash.
   166  	emit.Opcodes(vrf.BinWriter, opcode.CAT) // this instruction will convert network magic to bytes using BigInteger rules of conversion.
   167  	// Continue construction of 'verifyWithECDsa' call.
   168  	emit.Opcodes(vrf.BinWriter, opcode.PUSH4, opcode.PACK)                              // pack arguments for 'verifyWithECDsa' call.
   169  	emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "verifyWithECDsa", callflag.NoneFlag) // emit the call to 'verifyWithECDsa' itself.
   170  	require.NoError(t, vrf.Err)
   171  
   172  	return vrf.Bytes()
   173  	// Here's an example of the resulting witness verification script (110 bytes length, always constant length, with constant length of signed data):
   174  	// NEO-GO-VM > loadbase64 ABhQDCECoIi/qx5LS+3n1GJFcoYbQByyDDsU6QaHvYhiJypOYWZBxfug4AMAAAAAAQAAAJ4UjUEtUQgwEM6LFMAfDA92ZXJpZnlXaXRoRUNEc2EMFBv1dasRiWiEE2EKNaEohs3gtmxyQWJ9W1I=
   175  	// READY: loaded 110 instructions
   176  	// NEO-GO-VM 0 > ops
   177  	// INDEX    OPCODE       PARAMETER
   178  	// 0        PUSHINT8     122 (7a)    <<
   179  	// 2        SWAP
   180  	// 3        PUSHDATA1    02a088bfab1e4b4bede7d4624572861b401cb20c3b14e90687bd8862272a4e6166
   181  	// 38       SYSCALL      System.Runtime.GetNetwork (c5fba0e0)
   182  	// 43       PUSHINT64    4294967296 (0000000001000000)
   183  	// 52       ADD
   184  	// 53       PUSH4
   185  	// 54       LEFT
   186  	// 55       SYSCALL      System.Runtime.GetScriptContainer (2d510830)
   187  	// 60       PUSH0
   188  	// 61       PICKITEM
   189  	// 62       CAT
   190  	// 63       PUSH4
   191  	// 64       PACK
   192  	// 65       PUSH0
   193  	// 66       PUSHDATA1    766572696679576974684543447361 ("verifyWithECDsa")
   194  	// 83       PUSHDATA1    1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
   195  	// 105      SYSCALL      System.Contract.Call (627d5b52)
   196  }
   197  
   198  // buildKoblitzVerificationScriptSimpleSingleHash builds witness verification script for Koblitz public key.
   199  // This method differs from buildKoblitzVerificationScriptCompat in that it checks
   200  //
   201  //	keccak256([var-bytes-network-magic, txHash-bytes-BE])
   202  //
   203  // instead of (comparing with N3)
   204  //
   205  //	sha256([4-bytes-network-magic-LE, txHash-bytes-BE]).
   206  func buildKoblitzVerificationScriptSimpleSingleHash(t *testing.T, pub *keys.PublicKey) []byte {
   207  	criptoLibH := state.CreateNativeContractHash(nativenames.CryptoLib)
   208  
   209  	// vrf is witness verification script corresponding to the pub.
   210  	// vrf is witness verification script corresponding to the pk.
   211  	vrf := io.NewBufBinWriter()
   212  	emit.Int(vrf.BinWriter, int64(native.Secp256k1Keccak256)) // push Koblitz curve identifier.
   213  	emit.Opcodes(vrf.BinWriter, opcode.SWAP)                  // swap curve identifier with the signature.
   214  	emit.Bytes(vrf.BinWriter, pub.Bytes())                    // emit the caller's public key.
   215  	// Construct and push the signed message. The signed message is effectively the network-dependent transaction hash,
   216  	// i.e. msg = [network-magic-bytes, tx.Hash()]
   217  	// Firstly, retrieve network magic (it's uint32 wrapped into BigInteger and represented as Integer stackitem on stack).
   218  	emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetNetwork) // push network magic.
   219  	// Retrieve executing transaction hash.
   220  	emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetScriptContainer) // push the script container (executing transaction, actually).
   221  	emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.PICKITEM)                // pick 0-th transaction item (the transaction hash).
   222  	// Concatenate network magic and transaction hash.
   223  	emit.Opcodes(vrf.BinWriter, opcode.CAT) // this instruction will convert network magic to bytes using BigInteger rules of conversion.
   224  	// Continue construction of 'verifyWithECDsa' call.
   225  	emit.Opcodes(vrf.BinWriter, opcode.PUSH4, opcode.PACK)                              // pack arguments for 'verifyWithECDsa' call.
   226  	emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "verifyWithECDsa", callflag.NoneFlag) // emit the call to 'verifyWithECDsa' itself.
   227  	require.NoError(t, vrf.Err)
   228  
   229  	return vrf.Bytes()
   230  	// Here's an example of the resulting witness verification script (98 bytes length, always constant length, with variable length of signed data):
   231  	// NEO-GO-VM > loadbase64 ABZQDCEDY9ekgSWnbN6m4JjJ8SjoKSDtQo5ftMrx1/gcFsrQwgVBxfug4EEtUQgwEM6LFMAfDA92ZXJpZnlXaXRoRUNEc2EMFBv1dasRiWiEE2EKNaEohs3gtmxyQWJ9W1I=
   232  	// READY: loaded 98 instructions
   233  	// NEO-GO-VM 0 > ops
   234  	// INDEX    OPCODE       PARAMETER
   235  	// 0        PUSHINT8     122 (7a)    <<
   236  	// 2        SWAP
   237  	// 3        PUSHDATA1    0363d7a48125a76cdea6e098c9f128e82920ed428e5fb4caf1d7f81c16cad0c205
   238  	// 38       SYSCALL      System.Runtime.GetNetwork (c5fba0e0)
   239  	// 43       SYSCALL      System.Runtime.GetScriptContainer (2d510830)
   240  	// 48       PUSH0
   241  	// 49       PICKITEM
   242  	// 50       CAT
   243  	// 51       PUSH4
   244  	// 52       PACK
   245  	// 53       PUSH0
   246  	// 54       PUSHDATA1    766572696679576974684543447361 ("verifyWithECDsa")
   247  	// 71       PUSHDATA1    1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
   248  	// 93       SYSCALL      System.Contract.Call (627d5b52)
   249  }
   250  
   251  // buildKoblitzVerificationScriptSimpleSingleHashStaticMagic builds witness verification script for Koblitz public key.
   252  // This method differs from buildKoblitzVerificationScriptCompat in that it checks
   253  //
   254  //	keccak256([var-bytes-network-magic, txHash-bytes-BE])
   255  //
   256  // instead of (comparing with N3)
   257  //
   258  //	sha256([4-bytes-network-magic-LE, txHash-bytes-BE]).
   259  //
   260  // and it uses static magic value (simple PUSHINT* + magic, or PUSHDATA1 + magicBytes is also possible)
   261  // which results in network-specific verification script and, consequently, network-specific user address.
   262  func buildKoblitzVerificationScriptSimpleSingleHashStaticMagic(t *testing.T, pub *keys.PublicKey) []byte {
   263  	criptoLibH := state.CreateNativeContractHash(nativenames.CryptoLib)
   264  
   265  	// vrf is witness verification script corresponding to the pub.
   266  	// vrf is witness verification script corresponding to the pk.
   267  	vrf := io.NewBufBinWriter()
   268  	emit.Int(vrf.BinWriter, int64(native.Secp256k1Keccak256)) // push Koblitz curve identifier.
   269  	emit.Opcodes(vrf.BinWriter, opcode.SWAP)                  // swap curve identifier with the signature.
   270  	emit.Bytes(vrf.BinWriter, pub.Bytes())                    // emit the caller's public key.
   271  	// Construct and push the signed message. The signed message is effectively the network-dependent transaction hash,
   272  	// i.e. msg = [network-magic-bytes, tx.Hash()]
   273  	// Firstly, push static network magic (it's 42 for unit test chain).
   274  	emit.Int(vrf.BinWriter, 42)
   275  	// Retrieve executing transaction hash.
   276  	emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetScriptContainer) // push the script container (executing transaction, actually).
   277  	emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.PICKITEM)                // pick 0-th transaction item (the transaction hash).
   278  	// Concatenate network magic and transaction hash.
   279  	emit.Opcodes(vrf.BinWriter, opcode.CAT) // this instruction will convert network magic to bytes using BigInteger rules of conversion.
   280  	// Continue construction of 'verifyWithECDsa' call.
   281  	emit.Opcodes(vrf.BinWriter, opcode.PUSH4, opcode.PACK)                              // pack arguments for 'verifyWithECDsa' call.
   282  	emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "verifyWithECDsa", callflag.NoneFlag) // emit the call to 'verifyWithECDsa' itself.
   283  	require.NoError(t, vrf.Err)
   284  
   285  	return vrf.Bytes()
   286  	// Here's an example of the resulting witness verification script (95 bytes length, always constant length, with variable length of signed data):
   287  	// NEO-GO-VM > loadbase64 ABZQDCECluEwgK3pKiq3IjOMKiSe6Ng6FPZJxoMhZkFl8GvREL0AKkEtUQgwEM6LFMAfDA92ZXJpZnlXaXRoRUNEc2EMFBv1dasRiWiEE2EKNaEohs3gtmxyQWJ9W1I=
   288  	// READY: loaded 95 instructions
   289  	// NEO-GO-VM 0 > ops
   290  	// INDEX    OPCODE       PARAMETER
   291  	// 0        PUSHINT8     122 (7a)    <<
   292  	// 2        SWAP
   293  	// 3        PUSHDATA1    0296e13080ade92a2ab722338c2a249ee8d83a14f649c68321664165f06bd110bd
   294  	// 38       PUSHINT8     42 (2a)
   295  	// 40       SYSCALL      System.Runtime.GetScriptContainer (2d510830)
   296  	// 45       PUSH0
   297  	// 46       PICKITEM
   298  	// 47       CAT
   299  	// 48       PUSH4
   300  	// 49       PACK
   301  	// 50       PUSH0
   302  	// 51       PUSHDATA1    766572696679576974684543447361 ("verifyWithECDsa")
   303  	// 68       PUSHDATA1    1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
   304  	// 90       SYSCALL      System.Contract.Call (627d5b52)
   305  }
   306  
   307  // buildKoblitzVerificationScriptSimple builds witness verification script for Koblitz public key.
   308  // This method differs from buildKoblitzVerificationScriptCompat in that it checks
   309  //
   310  //	keccak256(sha256([var-bytes-network-magic, txHash-bytes-BE]))
   311  //
   312  // instead of (comparing with N3)
   313  //
   314  //	sha256([4-bytes-network-magic-LE, txHash-bytes-BE]).
   315  //
   316  // It produces constant-length verification script (136 bytes) independently of the network parameters.
   317  // However, the length of signed message is variable and depends on the network magic (since network
   318  // magic Integer stackitem being converted to Buffer has the resulting byte slice length that depends on
   319  // the magic).
   320  func buildKoblitzVerificationScriptSimple(t *testing.T, pub *keys.PublicKey) []byte {
   321  	criptoLibH := state.CreateNativeContractHash(nativenames.CryptoLib)
   322  
   323  	// vrf is witness verification script corresponding to the pub.
   324  	// vrf is witness verification script corresponding to the pk.
   325  	vrf := io.NewBufBinWriter()
   326  	emit.Int(vrf.BinWriter, int64(native.Secp256k1Keccak256)) // push Koblitz curve identifier.
   327  	emit.Opcodes(vrf.BinWriter, opcode.SWAP)                  // swap curve identifier with the signature.
   328  	emit.Bytes(vrf.BinWriter, pub.Bytes())                    // emit the caller's public key.
   329  	// Construct and push the signed message. The signed message is effectively the network-dependent transaction hash,
   330  	// i.e. msg = Sha256([network-magic-bytes, tx.Hash()])
   331  	// Firstly, retrieve network magic (it's uint32 wrapped into BigInteger and represented as Integer stackitem on stack).
   332  	emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetNetwork) // push network magic.
   333  	// Retrieve executing transaction hash.
   334  	emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetScriptContainer) // push the script container (executing transaction, actually).
   335  	emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.PICKITEM,                // pick 0-th transaction item (the transaction hash).
   336  		opcode.CAT,   // concatenate network magic and transaction hash; this instruction will convert network magic to bytes using BigInteger rules of conversion.
   337  		opcode.PUSH1, // push 1 (the number of arguments of 'sha256' method of native CryptoLib).
   338  		opcode.PACK)  // pack arguments for 'sha256' call.
   339  	emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "sha256", callflag.NoneFlag) // emit the call to 'sha256' itself.
   340  	// Continue construction of 'verifyWithECDsa' call.
   341  	emit.Opcodes(vrf.BinWriter, opcode.PUSH4, opcode.PACK)                              // pack arguments for 'verifyWithECDsa' call.
   342  	emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "verifyWithECDsa", callflag.NoneFlag) // emit the call to 'verifyWithECDsa' itself.
   343  	require.NoError(t, vrf.Err)
   344  
   345  	return vrf.Bytes()
   346  	// Here's an example of the resulting witness verification script (136 bytes length, always constant length, with variable length of signed data):
   347  	// NEO-GO-VM 0 > loadbase64 ABZQDCEDp38Tevu0to16RQqloo/jNfgExYmoCElLS2JuuYcH831Bxfug4EEtUQgwEM6LEcAfDAZzaGEyNTYMFBv1dasRiWiEE2EKNaEohs3gtmxyQWJ9W1IUwB8MD3ZlcmlmeVdpdGhFQ0RzYQwUG/V1qxGJaIQTYQo1oSiGzeC2bHJBYn1bUg==
   348  	// READY: loaded 136 instructions
   349  	// NEO-GO-VM 0 > ops
   350  	// INDEX    OPCODE       PARAMETER
   351  	// 0        PUSHINT8     122 (7a)    <<
   352  	// 2        SWAP
   353  	// 3        PUSHDATA1    03a77f137afbb4b68d7a450aa5a28fe335f804c589a808494b4b626eb98707f37d
   354  	// 38       SYSCALL      System.Runtime.GetNetwork (c5fba0e0)
   355  	// 43       SYSCALL      System.Runtime.GetScriptContainer (2d510830)
   356  	// 48       PUSH0
   357  	// 49       PICKITEM
   358  	// 50       CAT
   359  	// 51       PUSH1
   360  	// 52       PACK
   361  	// 53       PUSH0
   362  	// 54       PUSHDATA1    736861323536 ("sha256")
   363  	// 62       PUSHDATA1    1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
   364  	// 84       SYSCALL      System.Contract.Call (627d5b52)
   365  	// 89       PUSH4
   366  	// 90       PACK
   367  	// 91       PUSH0
   368  	// 92       PUSHDATA1    766572696679576974684543447361 ("verifyWithECDsa")
   369  	// 109      PUSHDATA1    1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
   370  	// 131      SYSCALL      System.Contract.Call (627d5b52)
   371  }
   372  
   373  // buildKoblitzVerificationScript builds custom verification script for the provided Koblitz public key.
   374  // It checks that the following message is signed by the provided public key:
   375  //
   376  //	keccak256(sha256([4-bytes-network-magic-LE, txHash-bytes-BE]))
   377  //
   378  // It produces constant-length verification script (186 bytes) independently of the network parameters.
   379  func buildKoblitzVerificationScriptCompat(t *testing.T, pub *keys.PublicKey) []byte {
   380  	criptoLibH := state.CreateNativeContractHash(nativenames.CryptoLib)
   381  
   382  	// vrf is witness verification script corresponding to the pub.
   383  	vrf := io.NewBufBinWriter()
   384  	emit.Int(vrf.BinWriter, int64(native.Secp256k1Keccak256)) // push Koblitz curve identifier.
   385  	emit.Opcodes(vrf.BinWriter, opcode.SWAP)                  // swap curve identifier with the signature.
   386  	emit.Bytes(vrf.BinWriter, pub.Bytes())                    // emit the caller's public key.
   387  	// Construct and push the signed message. The signed message is effectively the network-dependent transaction hash,
   388  	// i.e. msg = Sha256([4-bytes-network-magic-LE, tx.Hash()])
   389  	// Firstly, convert network magic (uint32) to LE buffer.
   390  	emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetNetwork) // push network magic.
   391  	// First byte: n & 0xFF
   392  	emit.Opcodes(vrf.BinWriter, opcode.DUP)
   393  	emit.Int(vrf.BinWriter, 0xFF) // TODO: this can be optimize in order not to allocate 0xFF every time, but need to compare execution price.
   394  	emit.Opcodes(vrf.BinWriter, opcode.AND,
   395  		opcode.SWAP, // Swap with the original network n.
   396  		opcode.PUSH8,
   397  		opcode.SHR)
   398  	// Second byte: n >> 8 & 0xFF
   399  	emit.Opcodes(vrf.BinWriter, opcode.DUP)
   400  	emit.Int(vrf.BinWriter, 0xFF)
   401  	emit.Opcodes(vrf.BinWriter, opcode.AND,
   402  		opcode.SWAP, // Swap with the n >> 8.
   403  		opcode.PUSH8,
   404  		opcode.SHR)
   405  	// Third byte: n >> 16 & 0xFF
   406  	emit.Opcodes(vrf.BinWriter, opcode.DUP)
   407  	emit.Int(vrf.BinWriter, 0xFF)
   408  	emit.Opcodes(vrf.BinWriter, opcode.AND,
   409  		opcode.SWAP, // Swap with the n >> 16.
   410  		opcode.PUSH8,
   411  		opcode.SHR)
   412  	// Fourth byte: n >> 24 & 0xFF
   413  	emit.Int(vrf.BinWriter, 0xFF) // no DUP is needed since it's the last shift.
   414  	emit.Opcodes(vrf.BinWriter, opcode.AND)
   415  	// Put these 4 bytes into buffer.
   416  	emit.Opcodes(vrf.BinWriter, opcode.PUSH4, opcode.NEWBUFFER) // allocate new 4-bytes-length buffer.
   417  	emit.Opcodes(vrf.BinWriter,
   418  		// Set fourth byte.
   419  		opcode.DUP, opcode.PUSH3,
   420  		opcode.PUSH3, opcode.ROLL,
   421  		opcode.SETITEM,
   422  		// Set third byte.
   423  		opcode.DUP, opcode.PUSH2,
   424  		opcode.PUSH3, opcode.ROLL,
   425  		opcode.SETITEM,
   426  		// Set second byte.
   427  		opcode.DUP, opcode.PUSH1,
   428  		opcode.PUSH3, opcode.ROLL,
   429  		opcode.SETITEM,
   430  		// Set first byte.
   431  		opcode.DUP, opcode.PUSH0,
   432  		opcode.PUSH3, opcode.ROLL,
   433  		opcode.SETITEM)
   434  	// Retrieve executing transaction hash.
   435  	emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetScriptContainer) // push the script container (executing transaction, actually).
   436  	emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.PICKITEM,                // pick 0-th transaction item (the transaction hash).
   437  		opcode.CAT,   // concatenate network magic and transaction hash.
   438  		opcode.PUSH1, // push 1 (the number of arguments of 'sha256' method of native CryptoLib).
   439  		opcode.PACK)  // pack arguments for 'sha256' call.
   440  	emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "sha256", callflag.NoneFlag) // emit the call to 'sha256' itself.
   441  	// Continue construction of 'verifyWithECDsa' call.
   442  	emit.Opcodes(vrf.BinWriter, opcode.PUSH4, opcode.PACK)                              // pack arguments for 'verifyWithECDsa' call.
   443  	emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "verifyWithECDsa", callflag.NoneFlag) // emit the call to 'verifyWithECDsa' itself.
   444  	require.NoError(t, vrf.Err)
   445  
   446  	return vrf.Bytes()
   447  	// Here's an example of the resulting witness verification script (186 bytes length, always constant length, the length of signed data is also always constant):
   448  	// NEO-GO-VM 0 > loadbase64 ABZQDCECYn75w2MePMuPvExbbEnjjM7eWnmvseGwcI+7lYp4AtdBxfug4EoB/wCRUBipSgH/AJFQGKlKAf8AkVAYqQH/AJEUiEoTE1LQShITUtBKERNS0EoQE1LQQS1RCDAQzosRwB8MBnNoYTI1NgwUG/V1qxGJaIQTYQo1oSiGzeC2bHJBYn1bUhTAHwwPdmVyaWZ5V2l0aEVDRHNhDBQb9XWrEYlohBNhCjWhKIbN4LZsckFifVtS
   449  	// READY: loaded 186 instructions
   450  	// NEO-GO-VM 0 > ops
   451  	// INDEX    OPCODE       PARAMETER
   452  	// 0        PUSHINT8     122 (7a)    <<
   453  	// 2        SWAP
   454  	// 3        PUSHDATA1    02627ef9c3631e3ccb8fbc4c5b6c49e38ccede5a79afb1e1b0708fbb958a7802d7
   455  	// 38       SYSCALL      System.Runtime.GetNetwork (c5fba0e0)
   456  	// 43       DUP
   457  	// 44       PUSHINT16    255 (ff00)
   458  	// 47       AND
   459  	// 48       SWAP
   460  	// 49       PUSH8
   461  	// 50       SHR
   462  	// 51       DUP
   463  	// 52       PUSHINT16    255 (ff00)
   464  	// 55       AND
   465  	// 56       SWAP
   466  	// 57       PUSH8
   467  	// 58       SHR
   468  	// 59       DUP
   469  	// 60       PUSHINT16    255 (ff00)
   470  	// 63       AND
   471  	// 64       SWAP
   472  	// 65       PUSH8
   473  	// 66       SHR
   474  	// 67       PUSHINT16    255 (ff00)
   475  	// 70       AND
   476  	// 71       PUSH4
   477  	// 72       NEWBUFFER
   478  	// 73       DUP
   479  	// 74       PUSH3
   480  	// 75       PUSH3
   481  	// 76       ROLL
   482  	// 77       SETITEM
   483  	// 78       DUP
   484  	// 79       PUSH2
   485  	// 80       PUSH3
   486  	// 81       ROLL
   487  	// 82       SETITEM
   488  	// 83       DUP
   489  	// 84       PUSH1
   490  	// 85       PUSH3
   491  	// 86       ROLL
   492  	// 87       SETITEM
   493  	// 88       DUP
   494  	// 89       PUSH0
   495  	// 90       PUSH3
   496  	// 91       ROLL
   497  	// 92       SETITEM
   498  	// 93       SYSCALL      System.Runtime.GetScriptContainer (2d510830)
   499  	// 98       PUSH0
   500  	// 99       PICKITEM
   501  	// 100      CAT
   502  	// 101      PUSH1
   503  	// 102      PACK
   504  	// 103      PUSH0
   505  	// 104      PUSHDATA1    736861323536 ("sha256")
   506  	// 112      PUSHDATA1    1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
   507  	// 134      SYSCALL      System.Contract.Call (627d5b52)
   508  	// 139      PUSH4
   509  	// 140      PACK
   510  	// 141      PUSH0
   511  	// 142      PUSHDATA1    766572696679576974684543447361 ("verifyWithECDsa")
   512  	// 159      PUSHDATA1    1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
   513  	// 181      SYSCALL      System.Contract.Call (627d5b52)
   514  }
   515  
   516  // buildKoblitzInvocationScript builds witness invocation script for the transaction signatures. The signature
   517  // itself may be produced by public key over any curve (not required Koblitz, the algorithm is the same).
   518  // The signatures expected to be sorted by public key (if multiple signatures are provided).
   519  func buildKoblitzInvocationScript(t *testing.T, signatures [][]byte) []byte {
   520  	//Exactly like during standard
   521  	// signature verification, the resulting script pushes Koblitz signature bytes onto stack.
   522  	inv := io.NewBufBinWriter()
   523  	for _, sig := range signatures {
   524  		emit.Bytes(inv.BinWriter, sig) // message signature bytes.
   525  	}
   526  	require.NoError(t, inv.Err)
   527  
   528  	return inv.Bytes()
   529  	// Here's an example of the resulting single witness invocation script (66 bytes length, always constant length):
   530  	// NEO-GO-VM > loadbase64 DEBMGKU/MdSizlzaVNDUUbd1zMZQJ43eTaZ4vBCpmkJ/wVh1TYrAWEbFyHhkqq+aYxPCUS43NKJdJTXavcjB8sTP
   531  	// READY: loaded 66 instructions
   532  	// NEO-GO-VM 0 > ops
   533  	// INDEX    OPCODE       PARAMETER
   534  	// 0        PUSHDATA1    4c18a53f31d4a2ce5cda54d0d451b775ccc650278dde4da678bc10a99a427fc158754d8ac05846c5c87864aaaf9a6313c2512e3734a25d2535dabdc8c1f2c4cf    <<
   535  	//
   536  	// Here's an example of the 3 out of 4 multisignature invocation script (66 * m bytes length, always constant length):
   537  	// NEO-GO-VM > loadbase64 DEBsPMY3+7sWyZf0gCVcqPzwZ79p+KpeylgtbYIrXp4Tdi6E/8q3DIrEgK7DdVe3YdbfE+VPrpwym/ufBb8MRTB6DED5B9OZDGWdJApRfuy9LeUTa2mLsXP7mBRa181g0Jo7beylWzVgDqHHF2PilECMcLmRbFRknmQm4KgiGkDE+O6ZDEAYt61O2dMfasJHiQD95M5b4mR6NBnDsMTo2e59H3y4YguroVLiUxnQSc4qu9LWvEIKr4/ytjCCuANXOkJmSw8C
   538  	// READY: loaded 198 instructions
   539  	// NEO-GO-VM 0 > ops
   540  	// INDEX    OPCODE       PARAMETER
   541  	// 0        PUSHDATA1    6c3cc637fbbb16c997f480255ca8fcf067bf69f8aa5eca582d6d822b5e9e13762e84ffcab70c8ac480aec37557b761d6df13e54fae9c329bfb9f05bf0c45307a    <<
   542  	// 66       PUSHDATA1    f907d3990c659d240a517eecbd2de5136b698bb173fb98145ad7cd60d09a3b6deca55b35600ea1c71763e294408c70b9916c54649e6426e0a8221a40c4f8ee99
   543  	// 132      PUSHDATA1    18b7ad4ed9d31f6ac2478900fde4ce5be2647a3419c3b0c4e8d9ee7d1f7cb8620baba152e25319d049ce2abbd2d6bc420aaf8ff2b63082b803573a42664b0f02
   544  }
   545  
   546  // constructMessage constructs message for signing that consists of the
   547  // unhashed constant 4-bytes length LE magic and transaction hash bytes:
   548  //
   549  //	[4-bytes-network-magic-LE, txHash-bytes-BE]
   550  func constructMessage(t *testing.T, magic uint32, tx hash.Hashable) []byte {
   551  	return hash.GetSignedData(magic, tx)
   552  }
   553  
   554  // constructMessageNoHash constructs message for signing that consists of the
   555  // unhashed magic and transaction hash bytes:
   556  //
   557  //	[var-bytes-network-magic, txHash-bytes-BE]
   558  func constructMessageNoHash(t *testing.T, magic uint32, tx hash.Hashable) []byte {
   559  	m := big.NewInt(int64(magic))
   560  	return append(m.Bytes(), tx.Hash().BytesBE()...)
   561  }
   562  
   563  // constructMessageCompat constructs message for signing that does not follow N3 rules,
   564  // but entails smaller verification script size and smaller verification price:
   565  //
   566  //	sha256([var-bytes-network-magic, txHash-bytes-BE])
   567  func constructMessageSimple(t *testing.T, magic uint32, tx hash.Hashable) []byte {
   568  	m := big.NewInt(int64(magic))
   569  	return hash.Sha256(append(m.Bytes(), tx.Hash().BytesBE()...)).BytesBE()
   570  }
   571  
   572  // constructMessageCompat constructs message for signing following the N3 rules:
   573  //
   574  //	sha256([4-bytes-network-magic-LE, txHash-bytes-BE])
   575  func constructMessageCompat(t *testing.T, magic uint32, tx hash.Hashable) []byte {
   576  	return hash.NetSha256(magic, tx).BytesBE()
   577  }
   578  
   579  // TestCryptoLib_KoblitzMultisigVerificationScript builds transaction with custom witness that contains
   580  // the Koblitz tx multisignature bytes and Koblitz multisignature verification script.
   581  // This test ensures that transaction signed by m out of n Koblitz keys passes verification and can
   582  // be successfully accepted to the chain.
   583  func TestCryptoLib_KoblitzMultisigVerificationScript(t *testing.T) {
   584  	check := func(
   585  		t *testing.T,
   586  		buildVerificationScript func(t *testing.T, m int, pub keys.PublicKeys) []byte,
   587  		constructMsg func(t *testing.T, magic uint32, tx hash.Hashable) []byte,
   588  	) {
   589  		c := newGasClient(t)
   590  		gasInvoker := c.WithSigners(c.Committee)
   591  		e := c.Executor
   592  
   593  		// Consider 4 users willing to sign 3/4 multisignature transaction Secp256k1 private keys.
   594  		const (
   595  			n = 4
   596  			m = 3
   597  		)
   598  		pks := make([]*keys.PrivateKey, n)
   599  		for i := range pks {
   600  			var err error
   601  			pks[i], err = keys.NewSecp256k1PrivateKey()
   602  			require.NoError(t, err)
   603  		}
   604  		// Sort private keys by their public keys.
   605  		sort.Slice(pks, func(i, j int) bool {
   606  			return pks[i].PublicKey().Cmp(pks[j].PublicKey()) < 0
   607  		})
   608  
   609  		// Firstly, we need to build the N3 multisig account address based on the users' public keys.
   610  		// Pubs must be sorted, exactly like for the standard CheckMultisig.
   611  		pubs := make(keys.PublicKeys, n)
   612  		for i := range pks {
   613  			pubs[i] = pks[i].PublicKey()
   614  		}
   615  		vrfBytes := buildVerificationScript(t, m, pubs)
   616  
   617  		// Construct the user's account script hash. It's effectively a verification script hash.
   618  		from := hash.Hash160(vrfBytes)
   619  
   620  		// Supply this account with some initial balance so that the user is able to pay for his transactions.
   621  		gasInvoker.Invoke(t, true, "transfer", c.Committee.ScriptHash(), from, 10000_0000_0000, nil)
   622  
   623  		// Construct transaction that transfers 5 GAS from the user's account to some other account.
   624  		to := util.Uint160{1, 2, 3}
   625  		amount := 5
   626  		tx := gasInvoker.PrepareInvokeNoSign(t, "transfer", from, to, amount, nil)
   627  		tx.Signers = []transaction.Signer{
   628  			{
   629  				Account: from,
   630  				Scopes:  transaction.CalledByEntry,
   631  			},
   632  		}
   633  		neotest.AddNetworkFee(t, e.Chain, tx)
   634  		neotest.AddSystemFee(e.Chain, tx, -1)
   635  
   636  		// Add some more network fee to pay for the witness verification. This value may be calculated precisely,
   637  		// but let's keep some inaccurate value for the test.
   638  		tx.NetworkFee = 8995470
   639  
   640  		// This transaction (along with the network magic) should be signed by the user's Koblitz private key.
   641  		msg := constructMsg(t, uint32(e.Chain.GetConfig().Magic), tx)
   642  
   643  		// The users have to sign the hash of the message by their Koblitz key. Collect m signatures from first m keys.
   644  		// Signatures must be sorted by public key.
   645  		sigs := make([][]byte, m)
   646  		for i := range sigs {
   647  			j := i
   648  			if i > 0 {
   649  				j++ // Add some shift to ensure that verification script works correctly.
   650  			}
   651  			if i > 3 {
   652  				j++ // Add more shift for large number of public keys for the same purpose.
   653  			}
   654  			sigs[i] = pks[j].SignHash(native.Keccak256(msg))
   655  		}
   656  
   657  		// Build invocation witness script for the signatures.
   658  		invBytes := buildKoblitzInvocationScript(t, sigs)
   659  
   660  		// Construct witness for signer #0 (the multisig account itself).
   661  		tx.Scripts = []transaction.Witness{
   662  			{
   663  				InvocationScript:   invBytes,
   664  				VerificationScript: vrfBytes,
   665  			},
   666  		}
   667  
   668  		// Add transaction to the chain. No error is expected on new block addition. Note, that this line performs
   669  		// all those checks that are executed during transaction acceptance in the real network.
   670  		e.AddNewBlock(t, tx)
   671  
   672  		// Double-check: ensure funds have been transferred.
   673  		e.CheckGASBalance(t, to, big.NewInt(int64(amount)))
   674  	}
   675  
   676  	// The proposed multisig verification script.
   677  	// (264 bytes, 8390070 GAS including Invocation script execution for 3/4 multisig).
   678  	// The user has to sign the keccak256([4-bytes-network-magic-LE, txHash-bytes-BE]).
   679  	check(t, buildKoblitzMultisigVerificationScript, constructMessage)
   680  }
   681  
   682  // buildKoblitzMultisigVerificationScript builds witness verification script for m signatures out of n Koblitz public keys.
   683  // Public keys must be sorted. Signatures (pushed by witness Invocation script) must be sorted by public keys.
   684  // It checks m out of n multisignature of the following message:
   685  //
   686  //	keccak256([4-bytes-network-magic-LE, txHash-bytes-BE])
   687  func buildKoblitzMultisigVerificationScript(t *testing.T, m int, pubs keys.PublicKeys) []byte {
   688  	if len(pubs) == 0 {
   689  		t.Fatalf("empty pubs list")
   690  	}
   691  	if m > len(pubs) {
   692  		t.Fatalf("m must be not greater than the number of public keys")
   693  	}
   694  
   695  	n := len(pubs) // public keys must be sorted.
   696  	cryptoLibH := state.CreateNativeContractHash(nativenames.CryptoLib)
   697  
   698  	// In fact, the following algorithm is implemented via NeoVM instructions:
   699  	//
   700  	// func Check(sigs []interop.Signature) bool {
   701  	// 	if m != len(sigs) {
   702  	// 		return false
   703  	// 	}
   704  	// 	var pubs []interop.PublicKey = []interop.PublicKey{...}
   705  	// 	msg := append(convert.ToBytes(runtime.GetNetwork()), runtime.GetScriptContainer().Hash...)
   706  	// 	var sigCnt = 0
   707  	// 	var pubCnt = 0
   708  	// 	for ; sigCnt < m && pubCnt < n; { // sigs must be sorted by pub
   709  	// 		sigCnt += crypto.VerifyWithECDsa(msg, pubs[pubCnt], sigs[sigCnt], crypto.Secp256k1Keccak256)
   710  	// 		pubCnt++
   711  	// 	}
   712  	// 	return sigCnt == m
   713  	// }
   714  	vrf := io.NewBufBinWriter()
   715  
   716  	// Start the same way as regular multisig script.
   717  	emit.Int(vrf.BinWriter, int64(m)) // push m.
   718  	for _, pub := range pubs {
   719  		emit.Bytes(vrf.BinWriter, pub.Bytes()) // push public keys in compressed form.
   720  	}
   721  	emit.Int(vrf.BinWriter, int64(n)) // push n.
   722  
   723  	// Initialize slots for local variables. Locals slot scheme:
   724  	// LOC0 -> sigs
   725  	// LOC1 -> pubs
   726  	// LOC2 -> msg (ByteString)
   727  	// LOC3 -> sigCnt (Integer)
   728  	// LOC4 -> pubCnt (Integer)
   729  	// LOC5 -> n
   730  	// LOC6 -> m
   731  	emit.InitSlot(vrf.BinWriter, 7, 0)
   732  
   733  	// Store n.
   734  	emit.Opcodes(vrf.BinWriter, opcode.STLOC5)
   735  
   736  	// Pack public keys and store at LOC1.
   737  	emit.Opcodes(vrf.BinWriter, opcode.LDLOC5)
   738  	emit.Opcodes(vrf.BinWriter, opcode.PACK, opcode.STLOC1)
   739  
   740  	// Store m.
   741  	emit.Opcodes(vrf.BinWriter, opcode.STLOC6)
   742  
   743  	// Check the number of signatures is m. Abort the execution if not.
   744  	emit.Opcodes(vrf.BinWriter, opcode.DEPTH)                // push the number of signatures onto stack.
   745  	emit.Opcodes(vrf.BinWriter, opcode.LDLOC6)               // load m.
   746  	emit.Instruction(vrf.BinWriter, opcode.JMPEQ, []byte{0}) // here and below short jumps are sufficient.
   747  	sigsLenCheckEndOffset := vrf.Len()                       // offset of the signatures count check.
   748  	emit.Opcodes(vrf.BinWriter, opcode.ABORT)                // abort execution if length of the signatures not equal to m.
   749  
   750  	// Start the check.
   751  	checkStartOffset := vrf.Len()
   752  
   753  	// Pack signatures and store at LOC0.
   754  	emit.Opcodes(vrf.BinWriter, opcode.LDLOC6) // load m.
   755  	emit.Opcodes(vrf.BinWriter, opcode.PACK, opcode.STLOC0)
   756  
   757  	// Get message and store it at LOC2.
   758  	// msg = [4-network-magic-bytes-LE, tx-hash-BE]
   759  	emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetNetwork) // push network magic (Integer stackitem), can have 0-5 bytes length serialized.
   760  	// Convert network magic to 4-bytes-length LE byte array representation.
   761  	emit.Int(vrf.BinWriter, 0x100000000)
   762  	emit.Opcodes(vrf.BinWriter, opcode.ADD, // some new number that is 5 bytes at least when serialized, but first 4 bytes are intact network value (LE).
   763  		opcode.PUSH4, opcode.LEFT) // cut the first 4 bytes out of a number that is at least 5 bytes long, the result is 4-bytes-length LE network representation.
   764  	// Retrieve executing transaction hash.
   765  	emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetScriptContainer) // push the script container (executing transaction, actually).
   766  	emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.PICKITEM)                // pick 0-th transaction item (the transaction hash).
   767  	// Concatenate network magic and transaction hash.
   768  	emit.Opcodes(vrf.BinWriter, opcode.CAT)    // this instruction will convert network magic to bytes using BigInteger rules of conversion.
   769  	emit.Opcodes(vrf.BinWriter, opcode.STLOC2) // store msg as a local variable #2.
   770  
   771  	// Initialize local variables: sigCnt, pubCnt.
   772  	emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.STLOC3, // initialize sigCnt.
   773  		opcode.PUSH0, opcode.STLOC4) // initialize pubCnt.
   774  
   775  	// Loop condition check.
   776  	loopStartOffset := vrf.Len()
   777  	emit.Opcodes(vrf.BinWriter, opcode.LDLOC3) // load sigCnt.
   778  	emit.Opcodes(vrf.BinWriter, opcode.LDLOC6) // push m.
   779  	emit.Opcodes(vrf.BinWriter, opcode.GE,     // sigCnt >= m
   780  		opcode.LDLOC4) // load pubCnt
   781  	emit.Opcodes(vrf.BinWriter, opcode.LDLOC5) // push n.
   782  	emit.Opcodes(vrf.BinWriter, opcode.GE,     // pubCnt >= n
   783  		opcode.OR) // sigCnt >= m || pubCnt >= n
   784  	emit.Instruction(vrf.BinWriter, opcode.JMPIF, []byte{0}) // jump to the end of the script if (sigCnt >= m || pubCnt >= n).
   785  	loopConditionOffset := vrf.Len()
   786  
   787  	// Loop start. Prepare arguments and call CryptoLib's verifyWithECDsa.
   788  	emit.Int(vrf.BinWriter, int64(native.Secp256k1Keccak256)) // push Koblitz curve identifier.
   789  	emit.Opcodes(vrf.BinWriter, opcode.LDLOC0,                // load signatures.
   790  		opcode.LDLOC3,             // load sigCnt.
   791  		opcode.PICKITEM,           // pick signature at index sigCnt.
   792  		opcode.LDLOC1,             // load pubs.
   793  		opcode.LDLOC4,             // load pubCnt.
   794  		opcode.PICKITEM,           // pick pub at index pubCnt.
   795  		opcode.LDLOC2,             // load msg.
   796  		opcode.PUSH4, opcode.PACK) // pack 4 arguments for 'verifyWithECDsa' call.
   797  	emit.AppCallNoArgs(vrf.BinWriter, cryptoLibH, "verifyWithECDsa", callflag.NoneFlag) // emit the call to 'verifyWithECDsa' itself.
   798  
   799  	// Update loop variables.
   800  	emit.Opcodes(vrf.BinWriter, opcode.LDLOC3, opcode.ADD, opcode.STLOC3, // increment sigCnt if signature is valid.
   801  		opcode.LDLOC4, opcode.INC, opcode.STLOC4) // increment pubCnt.
   802  
   803  	// End of the loop.
   804  	emit.Instruction(vrf.BinWriter, opcode.JMP, []byte{0}) // jump to the start of cycle.
   805  	loopEndOffset := vrf.Len()
   806  
   807  	// Return condition: the number of valid signatures should be equal to m.
   808  	progRetOffset := vrf.Len()
   809  	emit.Opcodes(vrf.BinWriter, opcode.LDLOC3)   // load sigCnt.
   810  	emit.Opcodes(vrf.BinWriter, opcode.LDLOC6)   // push m.
   811  	emit.Opcodes(vrf.BinWriter, opcode.NUMEQUAL) // push m == sigCnt.
   812  
   813  	require.NoError(t, vrf.Err)
   814  	script := vrf.Bytes()
   815  
   816  	// Set JMP* instructions offsets. "-1" is for short JMP parameter offset. JMP parameters
   817  	// are relative offsets.
   818  	script[sigsLenCheckEndOffset-1] = byte(checkStartOffset - sigsLenCheckEndOffset + 2)
   819  	script[loopEndOffset-1] = byte(loopStartOffset - loopEndOffset + 2)
   820  	script[loopConditionOffset-1] = byte(progRetOffset - loopConditionOffset + 2)
   821  
   822  	return script
   823  	// Here's an example of the resulting single witness invocation script (264 bytes length, the length may vary depending on m/n):
   824  	// NEO-GO-VM > loadbase64 EwwhAg1khs9yqTuG8R7dEj8/GhCqKwkL+6shSOczeaHENFo8DCECibz2wVNY1zRkRCbn+Qr87lQFjStnrQrwv1CSoea/91sMIQPiiV+wNGl5g5SVULR+BM/G2n6WO0WrGIsq+GBRqQHYwAwhAuwZz40NwnerrmSusUUgNqsZiv0WFj3KQE1BYd7lU7mDFFcHAHVtwHF2Q24oAzhuwHBBxfug4AMAAAAAAQAAAJ4UjUEtUQgwEM6LchBzEHRrbrhsbbiSJEIAGGhrzmlszmoUwB8MD3ZlcmlmeVdpdGhFQ0RzYQwUG/V1qxGJaIQTYQo1oSiGzeC2bHJBYn1bUmuec2ycdCK5a26z
   825  	// READY: loaded 264 instructions
   826  	// NEO-GO-VM 0 > ops
   827  	// INDEX    OPCODE       PARAMETER
   828  	// 0        PUSH3            <<
   829  	// 1        PUSHDATA1    020d6486cf72a93b86f11edd123f3f1a10aa2b090bfbab2148e73379a1c4345a3c
   830  	// 36       PUSHDATA1    0289bcf6c15358d734644426e7f90afcee54058d2b67ad0af0bf5092a1e6bff75b
   831  	// 71       PUSHDATA1    03e2895fb034697983949550b47e04cfc6da7e963b45ab188b2af86051a901d8c0
   832  	// 106      PUSHDATA1    02ec19cf8d0dc277abae64aeb1452036ab198afd16163dca404d4161dee553b983
   833  	// 141      PUSH4
   834  	// 142      INITSLOT     7 local, 0 arg
   835  	// 145      STLOC5
   836  	// 146      LDLOC5
   837  	// 147      PACK
   838  	// 148      STLOC1
   839  	// 149      STLOC6
   840  	// 150      DEPTH
   841  	// 151      LDLOC6
   842  	// 152      JMPEQ        155 (3/03)
   843  	// 154      ABORT
   844  	// 155      LDLOC6
   845  	// 156      PACK
   846  	// 157      STLOC0
   847  	// 158      SYSCALL      System.Runtime.GetNetwork (c5fba0e0)
   848  	// 163      PUSHINT64    4294967296 (0000000001000000)
   849  	// 172      ADD
   850  	// 173      PUSH4
   851  	// 174      LEFT
   852  	// 175      SYSCALL      System.Runtime.GetScriptContainer (2d510830)
   853  	// 180      PUSH0
   854  	// 181      PICKITEM
   855  	// 182      CAT
   856  	// 183      STLOC2
   857  	// 184      PUSH0
   858  	// 185      STLOC3
   859  	// 186      PUSH0
   860  	// 187      STLOC4
   861  	// 188      LDLOC3
   862  	// 189      LDLOC6
   863  	// 190      GE
   864  	// 191      LDLOC4
   865  	// 192      LDLOC5
   866  	// 193      GE
   867  	// 194      OR
   868  	// 195      JMPIF        261 (66/42)
   869  	// 197      PUSHINT8     122 (7a)
   870  	// 199      LDLOC0
   871  	// 200      LDLOC3
   872  	// 201      PICKITEM
   873  	// 202      LDLOC1
   874  	// 203      LDLOC4
   875  	// 204      PICKITEM
   876  	// 205      LDLOC2
   877  	// 206      PUSH4
   878  	// 207      PACK
   879  	// 208      PUSH0
   880  	// 209      PUSHDATA1    766572696679576974684543447361 ("verifyWithECDsa")
   881  	// 226      PUSHDATA1    1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
   882  	// 248      SYSCALL      System.Contract.Call (627d5b52)
   883  	// 253      LDLOC3
   884  	// 254      ADD
   885  	// 255      STLOC3
   886  	// 256      LDLOC4
   887  	// 257      INC
   888  	// 258      STLOC4
   889  	// 259      JMP          188 (-71/b9)
   890  	// 261      LDLOC3
   891  	// 262      LDLOC6
   892  	// 263      NUMEQUAL
   893  }