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 }