diff --git a/.docker/wallets/wallet1.json b/.docker/wallets/wallet1.json index 27a48a3ed..1ade05d76 100644 --- a/.docker/wallets/wallet1.json +++ b/.docker/wallets/wallet1.json @@ -2,11 +2,11 @@ "version": "3.0", "accounts": [ { - "address": "NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc", - "key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux", + "address": "NTh9TnZTstvAePEYWDGLLxidBikJE24uTo", + "key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL", "label": "", "contract": { - "script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcILQZVEDXg=", + "script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcJBdHR2qg==", "parameters": [ { "name": "parameter0", @@ -19,11 +19,11 @@ "isdefault": false }, { - "address": "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY", - "key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux", + "address": "NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6", + "key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL", "label": "", "contract": { - "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw==", + "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEF7zmyl", "parameters": [ { "name": "parameter0", diff --git a/.docker/wallets/wallet1_solo.json b/.docker/wallets/wallet1_solo.json index 1612925d6..2fcf9fd44 100644 --- a/.docker/wallets/wallet1_solo.json +++ b/.docker/wallets/wallet1_solo.json @@ -2,11 +2,11 @@ "version": "3.0", "accounts": [ { - "address": "NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc", - "key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux", + "address": "NTh9TnZTstvAePEYWDGLLxidBikJE24uTo", + "key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL", "label": "", "contract": { - "script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcILQZVEDXg=", + "script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcJBdHR2qg==", "parameters": [ { "name": "parameter0", @@ -19,11 +19,11 @@ "isdefault": false }, { - "address": "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY", - "key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux", + "address": "NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6", + "key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL", "label": "", "contract": { - "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw==", + "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEF7zmyl", "parameters": [ { "name": "parameter0", @@ -44,11 +44,11 @@ "isdefault": false }, { - "address": "NVNvVRW5Q5naSx2k2iZm7xRgtRNGuZppAK", - "key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux", + "address": "NNudMSGzEoktFzdYGYoNb3bzHzbmM1genF", + "key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL", "label": "", "contract": { - "script": "EQwhArNiK/QBe9/jF8WK7V9MdT8ga324lgRvp9d0u8S/f43CEQtBE43vrw==", + "script": "EQwhArNiK/QBe9/jF8WK7V9MdT8ga324lgRvp9d0u8S/f43CEUF7zmyl", "parameters": [ { "name": "parameter0", diff --git a/.docker/wallets/wallet2.json b/.docker/wallets/wallet2.json index 11b12bf5a..d09780587 100644 --- a/.docker/wallets/wallet2.json +++ b/.docker/wallets/wallet2.json @@ -2,11 +2,11 @@ "version": "3.0", "accounts": [ { - "address": "NWvKSwutC8D6VKmmPxAEgFKx2NLvFhn8q5", - "key": "6PYKEHagXJ3mDLdga1FoyTGRtPdJgPz6Gb8sjEFwZvRu7ncD9PVZfHtMzL", + "address": "NUREbqw2kfbPgDeEz8Dac2QxntGGqqFMm7", + "key": "6PYXADog3RQCwKRhqQsobwZEFopdcCJuMfPosM9pXPaDWSguKvznLdpADk", "label": "", "contract": { - "script": "DCECEDp/fdAWVYWX95YNJ8UWpDlP2Wi55lFV60sBPkBAQG4LQZVEDXg=", + "script": "DCECEDp/fdAWVYWX95YNJ8UWpDlP2Wi55lFV60sBPkBAQG5BdHR2qg==", "parameters": [ { "name": "parameter0", @@ -19,11 +19,11 @@ "isdefault": false }, { - "address": "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY", - "key": "6PYKEHagXJ3mDLdga1FoyTGRtPdJgPz6Gb8sjEFwZvRu7ncD9PVZfHtMzL", + "address": "NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6", + "key": "6PYXADog3RQCwKRhqQsobwZEFopdcCJuMfPosM9pXPaDWSguKvznLdpADk", "label": "", "contract": { - "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw==", + "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEF7zmyl", "parameters": [ { "name": "parameter0", diff --git a/.docker/wallets/wallet3.json b/.docker/wallets/wallet3.json index e4dd102f7..2b7691e7f 100644 --- a/.docker/wallets/wallet3.json +++ b/.docker/wallets/wallet3.json @@ -2,11 +2,11 @@ "version": "3.0", "accounts": [ { - "address": "NNB3RsnTABEwoKEudNG92njds91WtiCuxd", - "key": "6PYLjn1Zw3RQmP3CkDxoZvYtMpu7ZUdjHnvu7wPuohUcXWCMh9vY661R8A", + "address": "NQP81vKVRmwZHveX8C9Rbf2qejSpT1W1Eu", + "key": "6PYScv3Vgvdi9EkhDNvHXdvQeuaXK9gRwXDmytCswZMNpTzMLvfgR3U5dK", "label": "", "contract": { - "script": "DCED2QwH32PmkM53kS4Qq1GsyUS2aGAje2CMT4+DCece5pkLQZVEDXg=", + "script": "DCED2QwH32PmkM53kS4Qq1GsyUS2aGAje2CMT4+DCece5plBdHR2qg==", "parameters": [ { "name": "parameter0", @@ -19,11 +19,11 @@ "isdefault": false }, { - "address": "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY", - "key": "6PYLjn1Zw3RQmP3CkDxoZvYtMpu7ZUdjHnvu7wPuohUcXWCMh9vY661R8A", + "address": "NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6", + "key": "6PYScv3Vgvdi9EkhDNvHXdvQeuaXK9gRwXDmytCswZMNpTzMLvfgR3U5dK", "label": "", "contract": { - "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw==", + "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEF7zmyl", "parameters": [ { "name": "parameter0", diff --git a/.docker/wallets/wallet4.json b/.docker/wallets/wallet4.json index 2fe62f899..92909322b 100644 --- a/.docker/wallets/wallet4.json +++ b/.docker/wallets/wallet4.json @@ -2,11 +2,11 @@ "version": "3.0", "accounts": [ { - "address": "Nfzo95iBXfeAGx5rdjPedZRAqHKh9hwMdR", - "key": "6PYLbYYg9jUgzJQpKhpvNExa2UEgtp4356XPg56pHuCpE7gQmj84ESNjYW", + "address": "NLA34vf8eXGGUhRjVaYe5f8YsyYHTehbDZ", + "key": "6PYVwp1Sdg9DfTzvg42PZxgzMDf5a5FYBgT6ynKKzwmSHuhGkipoNjyW3a", "label": "", "contract": { - "script": "DCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWILQZVEDXg=", + "script": "DCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWJBdHR2qg==", "parameters": [ { "name": "parameter0", @@ -19,11 +19,11 @@ "isdefault": false }, { - "address": "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY", - "key": "6PYLbYYg9jUgzJQpKhpvNExa2UEgtp4356XPg56pHuCpE7gQmj84ESNjYW", + "address": "NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6", + "key": "6PYVwp1Sdg9DfTzvg42PZxgzMDf5a5FYBgT6ynKKzwmSHuhGkipoNjyW3a", "label": "", "contract": { - "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw==", + "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEF7zmyl", "parameters": [ { "name": "parameter0", diff --git a/cli/executor_test.go b/cli/executor_test.go index 92435afc6..64b488145 100644 --- a/cli/executor_test.go +++ b/cli/executor_test.go @@ -31,8 +31,8 @@ import ( const ( validatorWIF = "KxyjQ8eUa4FHt3Gvioyt1Wz29cTUrE4eTqX3yFSk1YFCsPL8uNsY" - validatorAddr = "NVNvVRW5Q5naSx2k2iZm7xRgtRNGuZppAK" - multisigAddr = "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY" + validatorAddr = "NNudMSGzEoktFzdYGYoNb3bzHzbmM1genF" + multisigAddr = "NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6" validatorWallet = "testdata/wallet1_solo.json" ) diff --git a/cli/nep17_test.go b/cli/nep17_test.go index 3994b318a..6930cfe85 100644 --- a/cli/nep17_test.go +++ b/cli/nep17_test.go @@ -51,7 +51,7 @@ func TestNEP17Balance(t *testing.T) { }) t.Run("all accounts", func(t *testing.T) { e.Run(t, cmdbase...) - addr1, err := address.StringToUint160("NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc") + addr1, err := address.StringToUint160("NTh9TnZTstvAePEYWDGLLxidBikJE24uTo") require.NoError(t, err) e.checkNextLine(t, "^Account "+address.Uint160ToString(addr1)) e.checkNextLine(t, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)") @@ -60,12 +60,12 @@ func TestNEP17Balance(t *testing.T) { e.checkNextLine(t, "^\\s*Updated:") e.checkNextLine(t, "^\\s*$") - addr2, err := address.StringToUint160("NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY") + addr2, err := address.StringToUint160("NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6") require.NoError(t, err) e.checkNextLine(t, "^Account "+address.Uint160ToString(addr2)) e.checkNextLine(t, "^\\s*$") - addr3, err := address.StringToUint160("NVNvVRW5Q5naSx2k2iZm7xRgtRNGuZppAK") + addr3, err := address.StringToUint160("NNudMSGzEoktFzdYGYoNb3bzHzbmM1genF") require.NoError(t, err) e.checkNextLine(t, "^Account "+address.Uint160ToString(addr3)) // The order of assets is undefined. @@ -85,7 +85,7 @@ func TestNEP17Balance(t *testing.T) { } e.checkNextLine(t, "^\\s*$") - addr4, err := address.StringToUint160("NWTDxsHVde5qSjRkTRUAg6i8xC3JSWEC9k") // deployed verify.go contract + addr4, err := address.StringToUint160("NTe3yHH5zsaEGvEHTsFRpCjTef6Aod4yb6") // deployed verify.go contract require.NoError(t, err) e.checkNextLine(t, "^Account "+address.Uint160ToString(addr4)) e.checkEOF(t) @@ -136,7 +136,7 @@ func TestNEP17Transfer(t *testing.T) { require.Equal(t, big.NewInt(1), b) t.Run("default address", func(t *testing.T) { - const validatorDefault = "NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc" + const validatorDefault = "NTh9TnZTstvAePEYWDGLLxidBikJE24uTo" e.In.WriteString("one\r") e.Run(t, "neo-go", "wallet", "nep17", "multitransfer", "--rpc-endpoint", "http://"+e.RPC.Addr, diff --git a/cli/testdata/chain50x2.acc b/cli/testdata/chain50x2.acc index 02f46c5e4..cae5ac69f 100644 Binary files a/cli/testdata/chain50x2.acc and b/cli/testdata/chain50x2.acc differ diff --git a/cli/testdata/testwallet.json b/cli/testdata/testwallet.json index 97b2e913a..e80c00a1f 100644 --- a/cli/testdata/testwallet.json +++ b/cli/testdata/testwallet.json @@ -1 +1,31 @@ -{"version":"3.0","accounts":[{"address":"NNuJqXDnRqvwgzhSzhH4jnVFWB1DyZ34EM","key":"6PYT6enT6eh4gu4ew3Mx58pFFDQNhuR1qQPuU594Eo5u4sA2ZvE4MqJV12","label":"kek","contract":{"script":"DCECl3UyEIq6T5RRIXS6z4tNdZPTzQ7NvXyx7FwK05d9UyYLQZVEDXg=","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"lock":false,"isdefault":false}],"scrypt":{"n":16384,"r":8,"p":8},"extra":{"Tokens":null}} +{ + "version" : "3.0", + "extra" : { + "Tokens" : null + }, + "accounts" : [ + { + "lock" : false, + "isdefault" : false, + "key" : "6PYRjaxsdSrW8zBzPynkC9uJRHpkhFHiQTuYY33gNL1wTimTbpN8S8eCNc", + "address" : "NUSEsqon6PikQA5mDFaV4njemF9Su8JEmf", + "contract" : { + "parameters" : [ + { + "type" : "Signature", + "name" : "parameter0" + } + ], + "script" : "DCECl3UyEIq6T5RRIXS6z4tNdZPTzQ7NvXyx7FwK05d9UyZBdHR2qg==", + "deployed" : false + }, + "label" : "kek" + } + ], + "scrypt" : { + "r" : 8, + "p" : 8, + "n" : 16384 + } +} + diff --git a/cli/testdata/wallet1_solo.json b/cli/testdata/wallet1_solo.json index 050459eb0..bab9b7b66 100644 --- a/cli/testdata/wallet1_solo.json +++ b/cli/testdata/wallet1_solo.json @@ -2,11 +2,11 @@ "version": "3.0", "accounts": [ { - "address": "NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc", - "key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux", + "address": "NTh9TnZTstvAePEYWDGLLxidBikJE24uTo", + "key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL", "label": "", "contract": { - "script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcILQZVEDXg=", + "script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcJBdHR2qg==", "parameters": [ { "name": "parameter0", @@ -19,11 +19,11 @@ "isdefault": true }, { - "address": "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY", - "key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux", + "address": "NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6", + "key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL", "label": "", "contract": { - "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw==", + "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEF7zmyl", "parameters": [ { "name": "parameter0", @@ -44,11 +44,11 @@ "isdefault": false }, { - "address": "NVNvVRW5Q5naSx2k2iZm7xRgtRNGuZppAK", - "key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux", + "address": "NNudMSGzEoktFzdYGYoNb3bzHzbmM1genF", + "key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL", "label": "", "contract": { - "script": "EQwhArNiK/QBe9/jF8WK7V9MdT8ga324lgRvp9d0u8S/f43CEQtBE43vrw==", + "script": "EQwhArNiK/QBe9/jF8WK7V9MdT8ga324lgRvp9d0u8S/f43CEUF7zmyl", "parameters": [ { "name": "parameter0", @@ -61,11 +61,11 @@ "isdefault": false }, { - "address" : "NWTDxsHVde5qSjRkTRUAg6i8xC3JSWEC9k", - "key" : "6PYXDze5Ak4HahYKygcNzk6n65ACjWdDCYLSuKgA5KG8vyMJSFboUNSiPD", + "address" : "NTe3yHH5zsaEGvEHTsFRpCjTef6Aod4yb6", + "key" : "6PYSgdjUPVjo3ZJLg2CsheXnEZzyvUuSm4jCtXP6X7FT82sAQHWt2wpu5A", "label" : "", "contract" : { - "script" : "VwEAEUBXAANA", + "script" : "VwEAEdsgQFcAA0A=", "deployed" : true, "parameters" : [] }, diff --git a/cli/testdata/wallets/testwallet_NEO3.json b/cli/testdata/wallets/testwallet_NEO3.json index 27a48a3ed..1ade05d76 100644 --- a/cli/testdata/wallets/testwallet_NEO3.json +++ b/cli/testdata/wallets/testwallet_NEO3.json @@ -2,11 +2,11 @@ "version": "3.0", "accounts": [ { - "address": "NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc", - "key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux", + "address": "NTh9TnZTstvAePEYWDGLLxidBikJE24uTo", + "key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL", "label": "", "contract": { - "script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcILQZVEDXg=", + "script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcJBdHR2qg==", "parameters": [ { "name": "parameter0", @@ -19,11 +19,11 @@ "isdefault": false }, { - "address": "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY", - "key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux", + "address": "NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6", + "key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL", "label": "", "contract": { - "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw==", + "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEF7zmyl", "parameters": [ { "name": "parameter0", diff --git a/cli/wallet_test.go b/cli/wallet_test.go index 4ee7e09f2..67d44fa4b 100644 --- a/cli/wallet_test.go +++ b/cli/wallet_test.go @@ -318,7 +318,7 @@ func TestWalletDump(t *testing.T) { w := new(wallet.Wallet) require.NoError(t, json.Unmarshal([]byte(rawStr), w)) require.Equal(t, 1, len(w.Accounts)) - require.Equal(t, "NNuJqXDnRqvwgzhSzhH4jnVFWB1DyZ34EM", w.Accounts[0].Address) + require.Equal(t, "NUSEsqon6PikQA5mDFaV4njemF9Su8JEmf", w.Accounts[0].Address) t.Run("with decrypt", func(t *testing.T) { cmd = append(cmd, "--decrypt") @@ -333,7 +333,7 @@ func TestWalletDump(t *testing.T) { w := new(wallet.Wallet) require.NoError(t, json.Unmarshal([]byte(rawStr), w)) require.Equal(t, 1, len(w.Accounts)) - require.Equal(t, "NNuJqXDnRqvwgzhSzhH4jnVFWB1DyZ34EM", w.Accounts[0].Address) + require.Equal(t, "NUSEsqon6PikQA5mDFaV4njemF9Su8JEmf", w.Accounts[0].Address) }) } @@ -343,27 +343,27 @@ func TestDumpKeys(t *testing.T) { pubRegex := "^0[23][a-hA-H0-9]{64}$" t.Run("all", func(t *testing.T) { e.Run(t, cmd...) - e.checkNextLine(t, "NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc") + e.checkNextLine(t, "NTh9TnZTstvAePEYWDGLLxidBikJE24uTo") e.checkNextLine(t, pubRegex) e.checkNextLine(t, "^\\s*$") - e.checkNextLine(t, "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY") + e.checkNextLine(t, "NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6") for i := 0; i < 4; i++ { e.checkNextLine(t, pubRegex) } e.checkNextLine(t, "^\\s*$") - e.checkNextLine(t, "NVNvVRW5Q5naSx2k2iZm7xRgtRNGuZppAK") + e.checkNextLine(t, "NNudMSGzEoktFzdYGYoNb3bzHzbmM1genF") e.checkNextLine(t, pubRegex) e.checkEOF(t) }) t.Run("simple signature", func(t *testing.T) { - cmd := append(cmd, "--address", "NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc") + cmd := append(cmd, "--address", "NTh9TnZTstvAePEYWDGLLxidBikJE24uTo") e.Run(t, cmd...) e.checkNextLine(t, "simple signature contract") e.checkNextLine(t, pubRegex) e.checkEOF(t) }) t.Run("3/4 multisig", func(t *testing.T) { - cmd := append(cmd, "-a", "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY") + cmd := append(cmd, "-a", "NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6") e.Run(t, cmd...) e.checkNextLine(t, "3 out of 4 multisig contract") for i := 0; i < 4; i++ { @@ -372,7 +372,7 @@ func TestDumpKeys(t *testing.T) { e.checkEOF(t) }) t.Run("1/1 multisig", func(t *testing.T) { - cmd := append(cmd, "--address", "NVNvVRW5Q5naSx2k2iZm7xRgtRNGuZppAK") + cmd := append(cmd, "--address", "NNudMSGzEoktFzdYGYoNb3bzHzbmM1genF") e.Run(t, cmd...) e.checkNextLine(t, "1 out of 1 multisig contract") e.checkNextLine(t, pubRegex) diff --git a/examples/timer/timer.go b/examples/timer/timer.go index a38d90ce1..6cbc02d2f 100644 --- a/examples/timer/timer.go +++ b/examples/timer/timer.go @@ -2,8 +2,8 @@ package timer import ( "github.com/nspcc-dev/neo-go/pkg/interop" - "github.com/nspcc-dev/neo-go/pkg/interop/binary" "github.com/nspcc-dev/neo-go/pkg/interop/contract" + "github.com/nspcc-dev/neo-go/pkg/interop/native/std" "github.com/nspcc-dev/neo-go/pkg/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/interop/storage" "github.com/nspcc-dev/neo-go/pkg/interop/util" @@ -35,7 +35,7 @@ func _deploy(_ interface{}, isUpdate bool) { sh := runtime.GetCallingScriptHash() storage.Put(ctx, mgmtKey, sh) storage.Put(ctx, ticksKey, defaultTicks) - i := binary.Itoa(defaultTicks, 10) + i := std.Itoa(defaultTicks, 10) runtime.Log("Timer set to " + i + " ticks.") } @@ -61,7 +61,7 @@ func Tick() bool { return contract.Call(runtime.GetExecutingScriptHash(), "selfDestroy", contract.All).(bool) } storage.Put(ctx, ticksKey, ticksLeft) - i := binary.Itoa(ticksLeft.(int), 10) + i := std.Itoa(ticksLeft.(int), 10) runtime.Log(i + " ticks left.") return true } diff --git a/internal/keytestcases/testcases.go b/internal/keytestcases/testcases.go index 62f8dc85d..828379151 100644 --- a/internal/keytestcases/testcases.go +++ b/internal/keytestcases/testcases.go @@ -14,36 +14,36 @@ type Ktype struct { // Arr contains a set of known keys in Ktype format. var Arr = []Ktype{ { - Address: "NNWAo5vdVJz1oyCuNiaTBA3amBHnWCF4Yk", + Address: "NQrEVKgpx2qEg6DpVMT5H8kFa7kc2DFgqS", PrivateKey: "7d128a6d096f0c14c3a25a2b0c41cf79661bfcb4a8cc95aaaea28bde4d732344", PublicKey: "02028a99826edc0c97d18e22b6932373d908d323aa7f92656a77ec26e8861699ef", Wif: "L1QqQJnpBwbsPGAuutuzPTac8piqvbR1HRjrY5qHup48TBCBFe4g", Passphrase: "city of zion", - EncryptedWif: "6PYSeMMbJtfMRD81eHzriwrRKquu2dgLNurYcAbmJa7YqAiThgA2vGQu5o", + EncryptedWif: "6PYWaEBMd9UTVFKi1YahYXY5NMLDg9U6w2gpQYUnx8wvaFgdo8EeVPaD7o", }, { - Address: "NiwvMyWYeNghLG8tDyKkWwuZV3wS8CPrrV", + Address: "NYaVsrMV9GS8aaspRS4odXf1WHZdMmJiPC", PrivateKey: "9ab7e154840daca3a2efadaf0df93cd3a5b51768c632f5433f86909d9b994a69", PublicKey: "031d8e1630ce640966967bc6d95223d21f44304133003140c3b52004dc981349c9", Wif: "L2QTooFoDFyRFTxmtiVHt5CfsXfVnexdbENGDkkrrgTTryiLsPMG", Passphrase: "我的密码", - EncryptedWif: "6PYKWKaq5NMyjt8cjvnJnvmV13inhFuePpWZMkddFAMCgjC3ETt7kX16V9", + EncryptedWif: "6PYUpn5uxTpsoawM3YKEWamk2oiKeafQBBK3Vutsowogy8a86jPu71xhE9", }, { - Address: "NTWHAzB82LRGWNuuqjVyyzpGvF3WxbbPoG", + Address: "NWcpK2143ZjgzDYyQJhoKrodJUymHTxPzR", PrivateKey: "3edee7036b8fd9cef91de47386b191dd76db2888a553e7736bb02808932a915b", PublicKey: "02232ce8d2e2063dce0451131851d47421bfc4fc1da4db116fca5302c0756462fa", Wif: "KyKvWLZsNwBJx5j9nurHYRwhYfdQUu9tTEDsLCUHDbYBL8cHxMiG", Passphrase: "MyL33tP@33w0rd", - EncryptedWif: "6PYSzKoJBQMj9uHUv1Sc2ZhMrydqDF8ZCTeE9FuPiNdEx7Lo9NoEuaXeyk", + EncryptedWif: "6PYRbKt55d4NXxCESqk8n9kURqopvixEY5nhAYe2ZJ4c1oDWAjtFX8hd1M", }, { - Address: "xdf4UGKevVrMR1j3UkPsuoYKSC4ocoAkKx", - PrivateKey: "zzdee7036b8fd9cef91de47386b191dd76db2888a553e7736bb02808932a915b", - PublicKey: "zz232ce8d2e2063dce0451131851d47421bfc4fc1da4db116fca5302c0756462fa", - Wif: "zzKvWLZsNwBJx5j9nurHYRwhYfdQUu9tTEDsLCUHDbYBL8cHxMiG", - Passphrase: "zzL33tP@33w0rd", - EncryptedWif: "6PYSzKoJBQMj9uHUv1Sc2ZhMrydqDF8ZCTeE9FuPiNdEx7Lo9NoEuaXeyk", + Address: "NWcpK2143ZjgzDYyQJhoKrodJUymHTxPzR", + PrivateKey: "3edee7036b8fd9cef91de47386b191dd76db2888a553e7736bb02808932a915", + PublicKey: "02232ce8d2e2063dce0451131851d47421bfc4fc1da4db116fca5302c0756462fa", + Wif: "KyKvWLZsNwBJx5j9nurHYRwhYfdQUu9tTEDsLCUHDbYBL8cHxMiS", + Passphrase: "invalid_pass_but_valid_wif", + EncryptedWif: "6PYRbKt55d4NXxCESqk8n9kURqopvixEY5nhAYe2ZJ4c1oDWAjtFX8hd1M", Invalid: true, }, } diff --git a/internal/testchain/transaction.go b/internal/testchain/transaction.go index 8f8a4aa11..d9a42b43f 100644 --- a/internal/testchain/transaction.go +++ b/internal/testchain/transaction.go @@ -49,44 +49,44 @@ func NewTransferFromOwner(bc blockchainer.Blockchainer, contractHash, to util.Ui } // NewDeployTx returns new deployment tx for contract with name with Go code read from r. -func NewDeployTx(bc blockchainer.Blockchainer, name string, sender util.Uint160, r gio.Reader) (*transaction.Transaction, util.Uint160, error) { +func NewDeployTx(bc blockchainer.Blockchainer, name string, sender util.Uint160, r gio.Reader) (*transaction.Transaction, util.Uint160, []byte, error) { // nef.NewFile() cares about version a lot. config.Version = "0.90.0-test" avm, di, err := compiler.CompileWithDebugInfo(name, r) if err != nil { - return nil, util.Uint160{}, err + return nil, util.Uint160{}, nil, err } ne, err := nef.NewFile(avm) if err != nil { - return nil, util.Uint160{}, err + return nil, util.Uint160{}, nil, err } m, err := di.ConvertToManifest(&compiler.Options{Name: name}) if err != nil { - return nil, util.Uint160{}, err + return nil, util.Uint160{}, nil, err } rawManifest, err := json.Marshal(m) if err != nil { - return nil, util.Uint160{}, err + return nil, util.Uint160{}, nil, err } neb, err := ne.Bytes() if err != nil { - return nil, util.Uint160{}, err + return nil, util.Uint160{}, nil, err } buf := io.NewBufBinWriter() emit.AppCall(buf.BinWriter, bc.ManagementContractHash(), "deploy", callflag.All, neb, rawManifest) if buf.Err != nil { - return nil, util.Uint160{}, buf.Err + return nil, util.Uint160{}, nil, buf.Err } tx := transaction.New(Network(), buf.Bytes(), 100*native.GASFactor) tx.Signers = []transaction.Signer{{Account: sender}} h := state.CreateContractHash(tx.Sender(), ne.Checksum, name) - return tx, h, nil + return tx, h, avm, nil } // SignTx signs provided transactions with validator keys. diff --git a/pkg/compiler/inline_test.go b/pkg/compiler/inline_test.go index c7dee4599..eeb9bfe3a 100644 --- a/pkg/compiler/inline_test.go +++ b/pkg/compiler/inline_test.go @@ -118,13 +118,14 @@ func TestInline(t *testing.T) { func TestInlineInLoop(t *testing.T) { t.Run("simple", func(t *testing.T) { src := `package foo - import "github.com/nspcc-dev/neo-go/pkg/interop/binary" + import "github.com/nspcc-dev/neo-go/pkg/interop/storage" import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/inline" func Main() int { sum := 0 values := []int{10, 11} for _, v := range values { - binary.Itoa(v, 10) + _ = v // use 'v' + storage.GetContext() // push something on stack to check it's dropped sum += inline.VarSum(1, 2, 3, 4) } return sum @@ -133,14 +134,16 @@ func TestInlineInLoop(t *testing.T) { }) t.Run("inlined argument", func(t *testing.T) { src := `package foo - import "github.com/nspcc-dev/neo-go/pkg/interop/binary" + import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + import "github.com/nspcc-dev/neo-go/pkg/interop/storage" import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/inline" func Main() int { sum := 0 values := []int{10, 11} for _, v := range values { - binary.Itoa(v, 10) - sum += inline.VarSum(1, 2, 3, binary.Atoi("4", 10)) + _ = v // use 'v' + storage.GetContext() // push something on stack to check it's dropped + sum += inline.VarSum(1, 2, 3, runtime.GetTime()) // runtime.GetTime always returns 4 in these tests } return sum }` @@ -148,12 +151,12 @@ func TestInlineInLoop(t *testing.T) { }) t.Run("check clean stack on return", func(t *testing.T) { src := `package foo - import "github.com/nspcc-dev/neo-go/pkg/interop/binary" + import "github.com/nspcc-dev/neo-go/pkg/interop/storage" import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/inline" func Main() int { values := []int{10, 11, 12} for _, v := range values { - binary.Itoa(v, 10) + storage.GetContext() // push something on stack to check it's dropped if v == 11 { return inline.VarSum(2, 20, 200) } diff --git a/pkg/compiler/native_test.go b/pkg/compiler/native_test.go index 7c194f9b5..ae99a46ea 100644 --- a/pkg/compiler/native_test.go +++ b/pkg/compiler/native_test.go @@ -9,6 +9,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "github.com/nspcc-dev/neo-go/pkg/core/native" + "github.com/nspcc-dev/neo-go/pkg/interop/native/crypto" "github.com/nspcc-dev/neo-go/pkg/interop/native/gas" "github.com/nspcc-dev/neo-go/pkg/interop/native/ledger" "github.com/nspcc-dev/neo-go/pkg/interop/native/management" @@ -18,6 +19,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/interop/native/oracle" "github.com/nspcc-dev/neo-go/pkg/interop/native/policy" "github.com/nspcc-dev/neo-go/pkg/interop/native/roles" + "github.com/nspcc-dev/neo-go/pkg/interop/native/std" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/util" @@ -37,6 +39,8 @@ func TestContractHashes(t *testing.T) { require.Equal(t, []byte(ledger.Hash), cs.Ledger.Hash.BytesBE()) require.Equal(t, []byte(management.Hash), cs.Management.Hash.BytesBE()) require.Equal(t, []byte(notary.Hash), cs.Notary.Hash.BytesBE()) + require.Equal(t, []byte(crypto.Hash), cs.Crypto.Hash.BytesBE()) + require.Equal(t, []byte(std.Hash), cs.Std.Hash.BytesBE()) } // testPrintHash is a helper for updating contract hashes. @@ -77,6 +81,11 @@ func TestNameServiceRecordType(t *testing.T) { require.EqualValues(t, native.RecordTypeAAAA, nameservice.TypeAAAA) } +func TestCryptoLibNamedCurve(t *testing.T) { + require.EqualValues(t, native.Secp256k1, crypto.Secp256k1) + require.EqualValues(t, native.Secp256r1, crypto.Secp256r1) +} + type nativeTestCase struct { method string params []string @@ -88,6 +97,7 @@ func TestNativeHelpersCompile(t *testing.T) { u160 := `interop.Hash160("aaaaaaaaaaaaaaaaaaaa")` u256 := `interop.Hash256("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")` pub := `interop.PublicKey("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")` + sig := `interop.Signature("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")` nep17TestCases := []nativeTestCase{ {"balanceOf", []string{u160}}, {"decimals", nil}, @@ -176,6 +186,23 @@ func TestNativeHelpersCompile(t *testing.T) { {"update", []string{"nil", "nil"}}, {"updateWithData", []string{"nil", "nil", "123"}}, }) + runNativeTestCases(t, cs.Crypto.ContractMD, "crypto", []nativeTestCase{ + {"sha256", []string{"[]byte{1, 2, 3}"}}, + {"ripemd160", []string{"[]byte{1, 2, 3}"}}, + {"verifyWithECDsa", []string{"[]byte{1, 2, 3}", pub, sig, "crypto.Secp256k1"}}, + }) + runNativeTestCases(t, cs.Std.ContractMD, "std", []nativeTestCase{ + {"serialize", []string{"[]byte{1, 2, 3}"}}, + {"deserialize", []string{"[]byte{1, 2, 3}"}}, + {"jsonSerialize", []string{"[]byte{1, 2, 3}"}}, + {"jsonDeserialize", []string{"[]byte{1, 2, 3}"}}, + {"base64Encode", []string{"[]byte{1, 2, 3}"}}, + {"base64Decode", []string{"[]byte{1, 2, 3}"}}, + {"base58Encode", []string{"[]byte{1, 2, 3}"}}, + {"base58Decode", []string{"[]byte{1, 2, 3}"}}, + {"itoa", []string{"4", "10"}}, + {"atoi", []string{`"4"`, "10"}}, + }) } func runNativeTestCases(t *testing.T, ctr interop.ContractMD, name string, testCases []nativeTestCase) { @@ -205,6 +232,7 @@ func runNativeTestCase(t *testing.T, ctr interop.ContractMD, name, method string } methodUpper := strings.ToUpper(method[:1]) + method[1:] // ASCII only methodUpper = strings.ReplaceAll(methodUpper, "Gas", "GAS") + methodUpper = strings.ReplaceAll(methodUpper, "Json", "JSON") src := fmt.Sprintf(srcTmpl, name, name, methodUpper, strings.Join(params, ",")) v, s := vmAndCompileInterop(t, src) diff --git a/pkg/compiler/syscall_test.go b/pkg/compiler/syscall_test.go index ace3adb1b..c9a960629 100644 --- a/pkg/compiler/syscall_test.go +++ b/pkg/compiler/syscall_test.go @@ -61,50 +61,36 @@ func TestSyscallExecution(t *testing.T) { sigs := "[]interop.Signature{" + sig + "}" sctx := "storage.Context{}" interops := map[string]syscallTestCase{ - "binary.Atoi": {interopnames.SystemBinaryAtoi, []string{`"123"`, "10"}, false}, - "binary.Base58Decode": {interopnames.SystemBinaryBase58Decode, []string{b}, false}, - "binary.Base58Encode": {interopnames.SystemBinaryBase58Encode, []string{b}, false}, - "binary.Base64Decode": {interopnames.SystemBinaryBase64Decode, []string{b}, false}, - "binary.Base64Encode": {interopnames.SystemBinaryBase64Encode, []string{b}, false}, - "binary.Deserialize": {interopnames.SystemBinaryDeserialize, []string{b}, false}, - "binary.Itoa": {interopnames.SystemBinaryItoa, []string{"123", "10"}, false}, - "binary.Serialize": {interopnames.SystemBinarySerialize, []string{"10"}, false}, - "contract.Call": {interopnames.SystemContractCall, []string{u160, `"m"`, "1", "3"}, false}, - "contract.CreateMultisigAccount": {interopnames.SystemContractCreateMultisigAccount, []string{"1", pubs}, false}, - "contract.CreateStandardAccount": {interopnames.SystemContractCreateStandardAccount, []string{pub}, false}, - "contract.IsStandard": {interopnames.SystemContractIsStandard, []string{b}, false}, - "contract.GetCallFlags": {interopnames.SystemContractGetCallFlags, nil, false}, - "iterator.Create": {interopnames.SystemIteratorCreate, []string{pubs}, false}, - "iterator.Next": {interopnames.SystemIteratorNext, []string{"iterator.Iterator{}"}, false}, - "iterator.Value": {interopnames.SystemIteratorValue, []string{"iterator.Iterator{}"}, false}, - "json.FromJSON": {interopnames.SystemJSONDeserialize, []string{b}, false}, - "json.ToJSON": {interopnames.SystemJSONSerialize, []string{b}, false}, - "runtime.CheckWitness": {interopnames.SystemRuntimeCheckWitness, []string{b}, false}, - "runtime.GasLeft": {interopnames.SystemRuntimeGasLeft, nil, false}, - "runtime.GetCallingScriptHash": {interopnames.SystemRuntimeGetCallingScriptHash, nil, false}, - "runtime.GetEntryScriptHash": {interopnames.SystemRuntimeGetEntryScriptHash, nil, false}, - "runtime.GetExecutingScriptHash": {interopnames.SystemRuntimeGetExecutingScriptHash, nil, false}, - "runtime.GetInvocationCounter": {interopnames.SystemRuntimeGetInvocationCounter, nil, false}, - "runtime.GetNotifications": {interopnames.SystemRuntimeGetNotifications, []string{u160}, false}, - "runtime.GetScriptContainer": {interopnames.SystemRuntimeGetScriptContainer, nil, false}, - "runtime.GetTime": {interopnames.SystemRuntimeGetTime, nil, false}, - "runtime.GetTrigger": {interopnames.SystemRuntimeGetTrigger, nil, false}, - "runtime.Log": {interopnames.SystemRuntimeLog, []string{`"msg"`}, true}, - "runtime.Notify": {interopnames.SystemRuntimeNotify, []string{`"ev"`, "1"}, true}, - "runtime.Platform": {interopnames.SystemRuntimePlatform, nil, false}, - "storage.Delete": {interopnames.SystemStorageDelete, []string{sctx, b}, true}, - "storage.Find": {interopnames.SystemStorageFind, []string{sctx, b, "storage.None"}, false}, - "storage.Get": {interopnames.SystemStorageGet, []string{sctx, b}, false}, - "storage.GetContext": {interopnames.SystemStorageGetContext, nil, false}, - "storage.GetReadOnlyContext": {interopnames.SystemStorageGetReadOnlyContext, nil, false}, - "storage.Put": {interopnames.SystemStoragePut, []string{sctx, b, b}, true}, - "storage.ConvertContextToReadOnly": {interopnames.SystemStorageAsReadOnly, []string{sctx}, false}, - "crypto.ECDsaSecp256r1Verify": {interopnames.NeoCryptoVerifyWithECDsaSecp256r1, []string{b, pub, sig}, false}, - "crypto.ECDsaSecp256k1Verify": {interopnames.NeoCryptoVerifyWithECDsaSecp256k1, []string{b, pub, sig}, false}, - "crypto.ECDSASecp256r1CheckMultisig": {interopnames.NeoCryptoCheckMultisigWithECDsaSecp256r1, []string{b, pubs, sigs}, false}, - "crypto.ECDSASecp256k1CheckMultisig": {interopnames.NeoCryptoCheckMultisigWithECDsaSecp256k1, []string{b, pubs, sigs}, false}, - "crypto.SHA256": {interopnames.NeoCryptoSHA256, []string{b}, false}, - "crypto.RIPEMD160": {interopnames.NeoCryptoRIPEMD160, []string{b}, false}, + "contract.Call": {interopnames.SystemContractCall, []string{u160, `"m"`, "1", "3"}, false}, + "contract.CreateMultisigAccount": {interopnames.SystemContractCreateMultisigAccount, []string{"1", pubs}, false}, + "contract.CreateStandardAccount": {interopnames.SystemContractCreateStandardAccount, []string{pub}, false}, + "contract.IsStandard": {interopnames.SystemContractIsStandard, []string{u160}, false}, + "contract.GetCallFlags": {interopnames.SystemContractGetCallFlags, nil, false}, + "iterator.Create": {interopnames.SystemIteratorCreate, []string{pubs}, false}, + "iterator.Next": {interopnames.SystemIteratorNext, []string{"iterator.Iterator{}"}, false}, + "iterator.Value": {interopnames.SystemIteratorValue, []string{"iterator.Iterator{}"}, false}, + "runtime.CheckWitness": {interopnames.SystemRuntimeCheckWitness, []string{b}, false}, + "runtime.GasLeft": {interopnames.SystemRuntimeGasLeft, nil, false}, + "runtime.GetCallingScriptHash": {interopnames.SystemRuntimeGetCallingScriptHash, nil, false}, + "runtime.GetEntryScriptHash": {interopnames.SystemRuntimeGetEntryScriptHash, nil, false}, + "runtime.GetExecutingScriptHash": {interopnames.SystemRuntimeGetExecutingScriptHash, nil, false}, + "runtime.GetInvocationCounter": {interopnames.SystemRuntimeGetInvocationCounter, nil, false}, + "runtime.GetNotifications": {interopnames.SystemRuntimeGetNotifications, []string{u160}, false}, + "runtime.GetScriptContainer": {interopnames.SystemRuntimeGetScriptContainer, nil, false}, + "runtime.GetTime": {interopnames.SystemRuntimeGetTime, nil, false}, + "runtime.GetTrigger": {interopnames.SystemRuntimeGetTrigger, nil, false}, + "runtime.Log": {interopnames.SystemRuntimeLog, []string{`"msg"`}, true}, + "runtime.Notify": {interopnames.SystemRuntimeNotify, []string{`"ev"`, "1"}, true}, + "runtime.Platform": {interopnames.SystemRuntimePlatform, nil, false}, + "storage.Delete": {interopnames.SystemStorageDelete, []string{sctx, b}, true}, + "storage.Find": {interopnames.SystemStorageFind, []string{sctx, b, "storage.None"}, false}, + "storage.Get": {interopnames.SystemStorageGet, []string{sctx, b}, false}, + "storage.GetContext": {interopnames.SystemStorageGetContext, nil, false}, + "storage.GetReadOnlyContext": {interopnames.SystemStorageGetReadOnlyContext, nil, false}, + "storage.Put": {interopnames.SystemStoragePut, []string{sctx, b, b}, true}, + "storage.ConvertContextToReadOnly": {interopnames.SystemStorageAsReadOnly, []string{sctx}, false}, + "crypto.CheckMultisig": {interopnames.NeoCryptoCheckMultisig, []string{pubs, sigs}, false}, + "crypto.CheckSig": {interopnames.NeoCryptoCheckSig, []string{pub, sig}, false}, } ic := &interop.Context{} core.SpawnVM(ic) // set Functions field @@ -209,20 +195,20 @@ func TestNotify(t *testing.T) { func TestSyscallInGlobalInit(t *testing.T) { src := `package foo - import "github.com/nspcc-dev/neo-go/pkg/interop/binary" - var a = binary.Base58Decode([]byte("5T")) - func Main() []byte { + import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + var a = runtime.CheckWitness([]byte("5T")) + func Main() bool { return a }` v, s := vmAndCompileInterop(t, src) - s.interops[interopnames.ToID([]byte(interopnames.SystemBinaryBase58Decode))] = func(v *vm.VM) error { + s.interops[interopnames.ToID([]byte(interopnames.SystemRuntimeCheckWitness))] = func(v *vm.VM) error { s := v.Estack().Pop().Value().([]byte) require.Equal(t, "5T", string(s)) - v.Estack().PushVal([]byte{1, 2}) + v.Estack().PushVal(true) return nil } require.NoError(t, v.Run()) - require.Equal(t, []byte{1, 2}, v.Estack().Pop().Value()) + require.Equal(t, true, v.Estack().Pop().Value()) } func TestOpcode(t *testing.T) { diff --git a/pkg/compiler/util_test.go b/pkg/compiler/util_test.go deleted file mode 100644 index 67eeb8118..000000000 --- a/pkg/compiler/util_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package compiler_test - -import ( - "testing" - - "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/core/interop/crypto" - "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" - "github.com/stretchr/testify/require" -) - -func TestSHA256(t *testing.T) { - src := ` - package foo - import ( - "github.com/nspcc-dev/neo-go/pkg/interop/crypto" - ) - func Main() []byte { - src := []byte{0x97} - hash := crypto.SHA256(src) - return hash - } - ` - v := vmAndCompile(t, src) - ic := &interop.Context{Trigger: trigger.Verification} - ic.VM = v - crypto.Register(ic) - v.SyscallHandler = ic.SyscallHandler - require.NoError(t, v.Run()) - require.True(t, v.Estack().Len() >= 1) - - h := []byte{0x2a, 0xa, 0xb7, 0x32, 0xb4, 0xe9, 0xd8, 0x5e, 0xf7, 0xdc, 0x25, 0x30, 0x3b, 0x64, 0xab, 0x52, 0x7c, 0x25, 0xa4, 0xd7, 0x78, 0x15, 0xeb, 0xb5, 0x79, 0xf3, 0x96, 0xec, 0x6c, 0xac, 0xca, 0xd3} - require.Equal(t, h, v.PopResult()) -} diff --git a/pkg/compiler/verify_test.go b/pkg/compiler/verify_test.go index a1793fce8..c945d6bfc 100644 --- a/pkg/compiler/verify_test.go +++ b/pkg/compiler/verify_test.go @@ -16,11 +16,10 @@ import ( func TestVerifyGood(t *testing.T) { msg := []byte("test message") pub, sig := signMessage(t, msg) - src := getVerifyProg(pub, sig, msg) + src := getVerifyProg(pub, sig) v, p := vmAndCompileInterop(t, src) - p.interops[interopnames.ToID([]byte(interopnames.NeoCryptoVerifyWithECDsaSecp256r1))] = func(v *vm.VM) error { - assert.Equal(t, msg, v.Estack().Pop().Bytes()) + p.interops[interopnames.ToID([]byte(interopnames.NeoCryptoCheckSig))] = func(v *vm.VM) error { assert.Equal(t, pub, v.Estack().Pop().Bytes()) assert.Equal(t, sig, v.Estack().Pop().Bytes()) v.Estack().PushVal(true) @@ -40,10 +39,9 @@ func signMessage(t *testing.T, msg []byte) ([]byte, []byte) { return pub, sig } -func getVerifyProg(pub, sig, msg []byte) string { +func getVerifyProg(pub, sig []byte) string { pubS := fmt.Sprintf("%#v", pub) sigS := fmt.Sprintf("%#v", sig) - msgS := fmt.Sprintf("%#v", msg) return ` package hello @@ -53,8 +51,7 @@ func getVerifyProg(pub, sig, msg []byte) string { func Main() bool { pub := ` + pubS + ` sig := ` + sigS + ` - msg := ` + msgS + ` - return crypto.ECDsaSecp256r1Verify(msg, pub, sig) + return crypto.CheckSig(pub, sig) } ` } diff --git a/pkg/compiler/vm_test.go b/pkg/compiler/vm_test.go index 1f414a42b..4752ef708 100644 --- a/pkg/compiler/vm_test.go +++ b/pkg/compiler/vm_test.go @@ -3,8 +3,6 @@ package compiler_test import ( "errors" "fmt" - "math/big" - "strconv" "strings" "testing" @@ -109,8 +107,7 @@ func newStoragePlugin() *storagePlugin { s.interops[interopnames.ToID([]byte(interopnames.SystemStoragePut))] = s.Put s.interops[interopnames.ToID([]byte(interopnames.SystemStorageGetContext))] = s.GetContext s.interops[interopnames.ToID([]byte(interopnames.SystemRuntimeNotify))] = s.Notify - s.interops[interopnames.ToID([]byte(interopnames.SystemBinaryAtoi))] = s.Atoi - s.interops[interopnames.ToID([]byte(interopnames.SystemBinaryItoa))] = s.Itoa + s.interops[interopnames.ToID([]byte(interopnames.SystemRuntimeGetTime))] = s.GetTime return s } @@ -126,24 +123,6 @@ func (s *storagePlugin) syscallHandler(v *vm.VM, id uint32) error { return errors.New("syscall not found") } -func (s *storagePlugin) Atoi(v *vm.VM) error { - str := v.Estack().Pop().String() - base := v.Estack().Pop().BigInt().Int64() - n, err := strconv.ParseInt(str, int(base), 64) - if err != nil { - return err - } - v.Estack().PushVal(big.NewInt(n)) - return nil -} - -func (s *storagePlugin) Itoa(v *vm.VM) error { - n := v.Estack().Pop().BigInt() - base := v.Estack().Pop().BigInt() - v.Estack().PushVal(n.Text(int(base.Int64()))) - return nil -} - func (s *storagePlugin) Notify(v *vm.VM) error { name := v.Estack().Pop().String() item := stackitem.NewArray(v.Estack().Pop().Array()) @@ -185,3 +164,10 @@ func (s *storagePlugin) GetContext(vm *vm.VM) error { vm.Estack().PushVal(10) return nil } + +func (s *storagePlugin) GetTime(vm *vm.VM) error { + // Pushing anything on the stack here will work. This is just to satisfy + // the compiler, thinking it has pushed the context ^^. + vm.Estack().PushVal(4) + return nil +} diff --git a/pkg/consensus/testdata/wallet1.json b/pkg/consensus/testdata/wallet1.json index 1612925d6..1ade05d76 100644 --- a/pkg/consensus/testdata/wallet1.json +++ b/pkg/consensus/testdata/wallet1.json @@ -2,11 +2,11 @@ "version": "3.0", "accounts": [ { - "address": "NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc", - "key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux", + "address": "NTh9TnZTstvAePEYWDGLLxidBikJE24uTo", + "key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL", "label": "", "contract": { - "script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcILQZVEDXg=", + "script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcJBdHR2qg==", "parameters": [ { "name": "parameter0", @@ -19,11 +19,11 @@ "isdefault": false }, { - "address": "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY", - "key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux", + "address": "NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6", + "key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL", "label": "", "contract": { - "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw==", + "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEF7zmyl", "parameters": [ { "name": "parameter0", @@ -42,23 +42,6 @@ }, "lock": false, "isdefault": false - }, - { - "address": "NVNvVRW5Q5naSx2k2iZm7xRgtRNGuZppAK", - "key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux", - "label": "", - "contract": { - "script": "EQwhArNiK/QBe9/jF8WK7V9MdT8ga324lgRvp9d0u8S/f43CEQtBE43vrw==", - "parameters": [ - { - "name": "parameter0", - "type": "Signature" - } - ], - "deployed": false - }, - "lock": false, - "isdefault": false } ], "scrypt": { diff --git a/pkg/consensus/testdata/wallet2.json b/pkg/consensus/testdata/wallet2.json index 11b12bf5a..d09780587 100644 --- a/pkg/consensus/testdata/wallet2.json +++ b/pkg/consensus/testdata/wallet2.json @@ -2,11 +2,11 @@ "version": "3.0", "accounts": [ { - "address": "NWvKSwutC8D6VKmmPxAEgFKx2NLvFhn8q5", - "key": "6PYKEHagXJ3mDLdga1FoyTGRtPdJgPz6Gb8sjEFwZvRu7ncD9PVZfHtMzL", + "address": "NUREbqw2kfbPgDeEz8Dac2QxntGGqqFMm7", + "key": "6PYXADog3RQCwKRhqQsobwZEFopdcCJuMfPosM9pXPaDWSguKvznLdpADk", "label": "", "contract": { - "script": "DCECEDp/fdAWVYWX95YNJ8UWpDlP2Wi55lFV60sBPkBAQG4LQZVEDXg=", + "script": "DCECEDp/fdAWVYWX95YNJ8UWpDlP2Wi55lFV60sBPkBAQG5BdHR2qg==", "parameters": [ { "name": "parameter0", @@ -19,11 +19,11 @@ "isdefault": false }, { - "address": "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY", - "key": "6PYKEHagXJ3mDLdga1FoyTGRtPdJgPz6Gb8sjEFwZvRu7ncD9PVZfHtMzL", + "address": "NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6", + "key": "6PYXADog3RQCwKRhqQsobwZEFopdcCJuMfPosM9pXPaDWSguKvznLdpADk", "label": "", "contract": { - "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw==", + "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEF7zmyl", "parameters": [ { "name": "parameter0", diff --git a/pkg/consensus/testdata/wallet3.json b/pkg/consensus/testdata/wallet3.json index e4dd102f7..2b7691e7f 100644 --- a/pkg/consensus/testdata/wallet3.json +++ b/pkg/consensus/testdata/wallet3.json @@ -2,11 +2,11 @@ "version": "3.0", "accounts": [ { - "address": "NNB3RsnTABEwoKEudNG92njds91WtiCuxd", - "key": "6PYLjn1Zw3RQmP3CkDxoZvYtMpu7ZUdjHnvu7wPuohUcXWCMh9vY661R8A", + "address": "NQP81vKVRmwZHveX8C9Rbf2qejSpT1W1Eu", + "key": "6PYScv3Vgvdi9EkhDNvHXdvQeuaXK9gRwXDmytCswZMNpTzMLvfgR3U5dK", "label": "", "contract": { - "script": "DCED2QwH32PmkM53kS4Qq1GsyUS2aGAje2CMT4+DCece5pkLQZVEDXg=", + "script": "DCED2QwH32PmkM53kS4Qq1GsyUS2aGAje2CMT4+DCece5plBdHR2qg==", "parameters": [ { "name": "parameter0", @@ -19,11 +19,11 @@ "isdefault": false }, { - "address": "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY", - "key": "6PYLjn1Zw3RQmP3CkDxoZvYtMpu7ZUdjHnvu7wPuohUcXWCMh9vY661R8A", + "address": "NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6", + "key": "6PYScv3Vgvdi9EkhDNvHXdvQeuaXK9gRwXDmytCswZMNpTzMLvfgR3U5dK", "label": "", "contract": { - "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw==", + "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEF7zmyl", "parameters": [ { "name": "parameter0", diff --git a/pkg/consensus/testdata/wallet4.json b/pkg/consensus/testdata/wallet4.json index 2fe62f899..92909322b 100644 --- a/pkg/consensus/testdata/wallet4.json +++ b/pkg/consensus/testdata/wallet4.json @@ -2,11 +2,11 @@ "version": "3.0", "accounts": [ { - "address": "Nfzo95iBXfeAGx5rdjPedZRAqHKh9hwMdR", - "key": "6PYLbYYg9jUgzJQpKhpvNExa2UEgtp4356XPg56pHuCpE7gQmj84ESNjYW", + "address": "NLA34vf8eXGGUhRjVaYe5f8YsyYHTehbDZ", + "key": "6PYVwp1Sdg9DfTzvg42PZxgzMDf5a5FYBgT6ynKKzwmSHuhGkipoNjyW3a", "label": "", "contract": { - "script": "DCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWILQZVEDXg=", + "script": "DCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWJBdHR2qg==", "parameters": [ { "name": "parameter0", @@ -19,11 +19,11 @@ "isdefault": false }, { - "address": "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY", - "key": "6PYLbYYg9jUgzJQpKhpvNExa2UEgtp4356XPg56pHuCpE7gQmj84ESNjYW", + "address": "NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6", + "key": "6PYVwp1Sdg9DfTzvg42PZxgzMDf5a5FYBgT6ynKKzwmSHuhGkipoNjyW3a", "label": "", "contract": { - "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw==", + "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEF7zmyl", "parameters": [ { "name": "parameter0", diff --git a/pkg/core/blockchain_test.go b/pkg/core/blockchain_test.go index 05e2a27a3..251497e9c 100644 --- a/pkg/core/blockchain_test.go +++ b/pkg/core/blockchain_test.go @@ -1229,7 +1229,7 @@ func TestIsTxStillRelevant(t *testing.T) { currentHeight := contract.Call(addr, "currentIndex", contract.ReadStates) return currentHeight.(int) < %d }`, bc.BlockHeight()+2) // deploy + next block - txDeploy, h, err := testchain.NewDeployTx(bc, "TestVerify", neoOwner, strings.NewReader(src)) + txDeploy, h, _, err := testchain.NewDeployTx(bc, "TestVerify", neoOwner, strings.NewReader(src)) require.NoError(t, err) txDeploy.ValidUntilBlock = bc.BlockHeight() + 1 addSigners(neoOwner, txDeploy) diff --git a/pkg/core/fee/calculate.go b/pkg/core/fee/calculate.go index 4966ad4f5..7a64caa2a 100644 --- a/pkg/core/fee/calculate.go +++ b/pkg/core/fee/calculate.go @@ -18,13 +18,13 @@ func Calculate(base int64, script []byte) (int64, int) { ) if vm.IsSignatureContract(script) { size += 67 + io.GetVarSize(script) - netFee += Opcode(base, opcode.PUSHDATA1, opcode.PUSHNULL, opcode.PUSHDATA1) + base*ECDSAVerifyPrice + netFee += Opcode(base, opcode.PUSHDATA1, opcode.PUSHDATA1) + base*ECDSAVerifyPrice } else if m, pubs, ok := vm.ParseMultiSigContract(script); ok { n := len(pubs) sizeInv := 66 * m size += io.GetVarSize(sizeInv) + sizeInv + io.GetVarSize(script) netFee += calculateMultisig(base, m) + calculateMultisig(base, n) - netFee += Opcode(base, opcode.PUSHNULL) + base*ECDSAVerifyPrice*int64(n) + netFee += base * ECDSAVerifyPrice * int64(n) } else { // We can support more contract types in the future. } diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index 42956b961..7ebfc6b88 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -2,6 +2,7 @@ package core import ( "bytes" + "encoding/base64" "encoding/hex" "encoding/json" "fmt" @@ -268,7 +269,7 @@ func TestCreateBasicChain(t *testing.T) { require.NoError(t, acc0.SignTx(txSendRaw)) bw := io.NewBufBinWriter() txSendRaw.EncodeBinary(bw.BinWriter) - t.Logf("sendrawtransaction: %s", hex.EncodeToString(bw.Bytes())) + t.Logf("sendrawtransaction: %s", base64.StdEncoding.EncodeToString(bw.Bytes())) require.False(t, saveChain) } @@ -449,6 +450,9 @@ func initBasicChain(t *testing.T, bc *Blockchain) { require.NoError(t, ntr.Accounts[0].Decrypt("one")) bc.setNodesByRole(t, true, native.RoleP2PNotary, keys.PublicKeys{ntr.Accounts[0].PrivateKey().PublicKey()}) t.Logf("Designated Notary node: %s", hex.EncodeToString(ntr.Accounts[0].PrivateKey().PublicKey().Bytes())) + + // Compile contract to test `invokescript` RPC call + _, _ = newDeployTx(t, bc, priv0ScriptHash, prefix+"invokescript_contract.go", "ContractForInvokescriptTest") } func newNEP17Transfer(sc, from, to util.Uint160, amount int64, additionalArgs ...interface{}) *transaction.Transaction { @@ -466,9 +470,9 @@ func newNEP17Transfer(sc, from, to util.Uint160, amount int64, additionalArgs .. func newDeployTx(t *testing.T, bc *Blockchain, sender util.Uint160, name, ctrName string) (*transaction.Transaction, util.Uint160) { c, err := ioutil.ReadFile(name) require.NoError(t, err) - tx, h, err := testchain.NewDeployTx(bc, ctrName, sender, bytes.NewReader(c)) + tx, h, avm, err := testchain.NewDeployTx(bc, ctrName, sender, bytes.NewReader(c)) require.NoError(t, err) - t.Logf("contractHash (%s): %s", name, h.StringLE()) + t.Logf("contract (%s): \n\tHash: %s\n\tAVM: %s", name, h.StringLE(), base64.StdEncoding.EncodeToString(avm)) return tx, h } diff --git a/pkg/core/interop/binary/encode.go b/pkg/core/interop/binary/encode.go deleted file mode 100644 index 0cc294fef..000000000 --- a/pkg/core/interop/binary/encode.go +++ /dev/null @@ -1,57 +0,0 @@ -package binary - -import ( - "encoding/base64" - - "github.com/mr-tron/base58" - "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/vm" -) - -// Serialize serializes top stack item into a ByteArray. -func Serialize(ic *interop.Context) error { - return vm.RuntimeSerialize(ic.VM) -} - -// Deserialize deserializes ByteArray from a stack into an item. -func Deserialize(ic *interop.Context) error { - return vm.RuntimeDeserialize(ic.VM) -} - -// EncodeBase64 encodes top stack item into a base64 string. -func EncodeBase64(ic *interop.Context) error { - src := ic.VM.Estack().Pop().Bytes() - result := base64.StdEncoding.EncodeToString(src) - ic.VM.Estack().PushVal([]byte(result)) - return nil -} - -// DecodeBase64 decodes top stack item from base64 string to byte array. -func DecodeBase64(ic *interop.Context) error { - src := ic.VM.Estack().Pop().String() - result, err := base64.StdEncoding.DecodeString(src) - if err != nil { - return err - } - ic.VM.Estack().PushVal(result) - return nil -} - -// EncodeBase58 encodes top stack item into a base58 string. -func EncodeBase58(ic *interop.Context) error { - src := ic.VM.Estack().Pop().Bytes() - result := base58.Encode(src) - ic.VM.Estack().PushVal([]byte(result)) - return nil -} - -// DecodeBase58 decodes top stack item from base58 string to byte array. -func DecodeBase58(ic *interop.Context) error { - src := ic.VM.Estack().Pop().String() - result, err := base58.Decode(src) - if err != nil { - return err - } - ic.VM.Estack().PushVal(result) - return nil -} diff --git a/pkg/core/interop/binary/encode_test.go b/pkg/core/interop/binary/encode_test.go deleted file mode 100644 index eeda9f3a2..000000000 --- a/pkg/core/interop/binary/encode_test.go +++ /dev/null @@ -1,92 +0,0 @@ -package binary - -import ( - "encoding/base64" - "math/big" - "testing" - - "github.com/mr-tron/base58" - "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/io" - "github.com/nspcc-dev/neo-go/pkg/vm" - "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" - "github.com/stretchr/testify/require" -) - -func TestRuntimeSerialize(t *testing.T) { - t.Run("recursive", func(t *testing.T) { - arr := stackitem.NewArray(nil) - arr.Append(arr) - ic := &interop.Context{VM: vm.New()} - ic.VM.Estack().PushVal(arr) - require.Error(t, Serialize(ic)) - }) - t.Run("big item", func(t *testing.T) { - ic := &interop.Context{VM: vm.New()} - ic.VM.Estack().PushVal(make([]byte, stackitem.MaxSize)) - require.Error(t, Serialize(ic)) - }) - t.Run("good", func(t *testing.T) { - ic := &interop.Context{VM: vm.New()} - ic.VM.Estack().PushVal(42) - require.NoError(t, Serialize(ic)) - - w := io.NewBufBinWriter() - stackitem.EncodeBinaryStackItem(stackitem.Make(42), w.BinWriter) - require.NoError(t, w.Err) - - encoded := w.Bytes() - require.Equal(t, encoded, ic.VM.Estack().Pop().Bytes()) - - ic.VM.Estack().PushVal(encoded) - require.NoError(t, Deserialize(ic)) - require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().BigInt()) - - t.Run("bad", func(t *testing.T) { - encoded[0] ^= 0xFF - ic.VM.Estack().PushVal(encoded) - require.Error(t, Deserialize(ic)) - }) - }) -} - -func TestRuntimeEncodeDecode(t *testing.T) { - original := []byte("my pretty string") - encoded64 := base64.StdEncoding.EncodeToString(original) - encoded58 := base58.Encode(original) - v := vm.New() - ic := &interop.Context{VM: v} - - t.Run("Encode64", func(t *testing.T) { - v.Estack().PushVal(original) - require.NoError(t, EncodeBase64(ic)) - actual := v.Estack().Pop().Bytes() - require.Equal(t, []byte(encoded64), actual) - }) - t.Run("Encode58", func(t *testing.T) { - v.Estack().PushVal(original) - require.NoError(t, EncodeBase58(ic)) - actual := v.Estack().Pop().Bytes() - require.Equal(t, []byte(encoded58), actual) - }) - t.Run("Decode64/positive", func(t *testing.T) { - v.Estack().PushVal(encoded64) - require.NoError(t, DecodeBase64(ic)) - actual := v.Estack().Pop().Bytes() - require.Equal(t, original, actual) - }) - t.Run("Decode64/error", func(t *testing.T) { - v.Estack().PushVal(encoded64 + "%") - require.Error(t, DecodeBase64(ic)) - }) - t.Run("Decode58/positive", func(t *testing.T) { - v.Estack().PushVal(encoded58) - require.NoError(t, DecodeBase58(ic)) - actual := v.Estack().Pop().Bytes() - require.Equal(t, original, actual) - }) - t.Run("Decode58/error", func(t *testing.T) { - v.Estack().PushVal(encoded58 + "%") - require.Error(t, DecodeBase58(ic)) - }) -} diff --git a/pkg/core/interop/binary/itoa.go b/pkg/core/interop/binary/itoa.go deleted file mode 100644 index b31918836..000000000 --- a/pkg/core/interop/binary/itoa.go +++ /dev/null @@ -1,91 +0,0 @@ -package binary - -import ( - "encoding/hex" - "errors" - "math/big" - "strings" - - "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" -) - -var ( - // ErrInvalidBase is returned when base is invalid. - ErrInvalidBase = errors.New("invalid base") - // ErrInvalidFormat is returned when string is not a number. - ErrInvalidFormat = errors.New("invalid format") -) - -// Itoa converts number to string. -func Itoa(ic *interop.Context) error { - num := ic.VM.Estack().Pop().BigInt() - base := ic.VM.Estack().Pop().BigInt() - if !base.IsInt64() { - return ErrInvalidBase - } - var s string - switch b := base.Int64(); b { - case 10: - s = num.Text(10) - case 16: - if num.Sign() == 0 { - s = "0" - break - } - bs := bigint.ToBytes(num) - reverse(bs) - s = hex.EncodeToString(bs) - if pad := bs[0] & 0xF8; pad == 0 || pad == 0xF8 { - s = s[1:] - } - s = strings.ToUpper(s) - default: - return ErrInvalidBase - } - ic.VM.Estack().PushVal(s) - return nil -} - -// Atoi converts string to number. -func Atoi(ic *interop.Context) error { - num := ic.VM.Estack().Pop().String() - base := ic.VM.Estack().Pop().BigInt() - if !base.IsInt64() { - return ErrInvalidBase - } - var bi *big.Int - switch b := base.Int64(); b { - case 10: - var ok bool - bi, ok = new(big.Int).SetString(num, int(b)) - if !ok { - return ErrInvalidFormat - } - case 16: - changed := len(num)%2 != 0 - if changed { - num = "0" + num - } - bs, err := hex.DecodeString(num) - if err != nil { - return ErrInvalidFormat - } - if changed && bs[0]&0x8 != 0 { - bs[0] |= 0xF0 - } - reverse(bs) - bi = bigint.FromBytes(bs) - default: - return ErrInvalidBase - } - ic.VM.Estack().PushVal(bi) - return nil -} - -func reverse(b []byte) { - l := len(b) - for i := 0; i < l/2; i++ { - b[i], b[l-i-1] = b[l-i-1], b[i] - } -} diff --git a/pkg/core/interop/binary/itoa_test.go b/pkg/core/interop/binary/itoa_test.go deleted file mode 100644 index faf189076..000000000 --- a/pkg/core/interop/binary/itoa_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package binary - -import ( - "errors" - "math" - "math/big" - "testing" - - "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/vm" - "github.com/stretchr/testify/require" -) - -func TestItoa(t *testing.T) { - var testCases = []struct { - num *big.Int - base *big.Int - result string - }{ - {big.NewInt(0), big.NewInt(10), "0"}, - {big.NewInt(0), big.NewInt(16), "0"}, - {big.NewInt(1), big.NewInt(10), "1"}, - {big.NewInt(-1), big.NewInt(10), "-1"}, - {big.NewInt(1), big.NewInt(16), "1"}, - {big.NewInt(7), big.NewInt(16), "7"}, - {big.NewInt(8), big.NewInt(16), "08"}, - {big.NewInt(65535), big.NewInt(16), "0FFFF"}, - {big.NewInt(15), big.NewInt(16), "0F"}, - {big.NewInt(-1), big.NewInt(16), "F"}, - } - - for _, tc := range testCases { - ic := &interop.Context{VM: vm.New()} - ic.VM.Estack().PushVal(tc.base) - ic.VM.Estack().PushVal(tc.num) - require.NoError(t, Itoa(ic)) - require.Equal(t, tc.result, ic.VM.Estack().Pop().String()) - - ic = &interop.Context{VM: vm.New()} - ic.VM.Estack().PushVal(tc.base) - ic.VM.Estack().PushVal(tc.result) - - require.NoError(t, Atoi(ic)) - require.Equal(t, tc.num, ic.VM.Estack().Pop().BigInt()) - } - - t.Run("-1", func(t *testing.T) { - for _, s := range []string{"FF", "FFF", "FFFF"} { - ic := &interop.Context{VM: vm.New()} - ic.VM.Estack().PushVal(16) - ic.VM.Estack().PushVal(s) - - require.NoError(t, Atoi(ic)) - require.Equal(t, big.NewInt(-1), ic.VM.Estack().Pop().BigInt()) - } - }) -} - -func TestItoaError(t *testing.T) { - var testCases = []struct { - num *big.Int - base *big.Int - err error - }{ - {big.NewInt(1), big.NewInt(13), ErrInvalidBase}, - {big.NewInt(-1), new(big.Int).Add(big.NewInt(math.MaxInt64), big.NewInt(10)), ErrInvalidBase}, - } - - for _, tc := range testCases { - ic := &interop.Context{VM: vm.New()} - ic.VM.Estack().PushVal(tc.base) - ic.VM.Estack().PushVal(tc.num) - err := Itoa(ic) - require.True(t, errors.Is(err, tc.err), "got: %v", err) - } -} - -func TestAtoiError(t *testing.T) { - var testCases = []struct { - num string - base *big.Int - err error - }{ - {"1", big.NewInt(13), ErrInvalidBase}, - {"1", new(big.Int).Add(big.NewInt(math.MaxInt64), big.NewInt(16)), ErrInvalidBase}, - {"1_000", big.NewInt(10), ErrInvalidFormat}, - {"FE", big.NewInt(10), ErrInvalidFormat}, - {"XD", big.NewInt(16), ErrInvalidFormat}, - } - - for _, tc := range testCases { - ic := &interop.Context{VM: vm.New()} - ic.VM.Estack().PushVal(tc.base) - ic.VM.Estack().PushVal(tc.num) - err := Atoi(ic) - require.True(t, errors.Is(err, tc.err), "got: %v", err) - } -} diff --git a/pkg/core/interop/crypto/ecdsa.go b/pkg/core/interop/crypto/ecdsa.go index a31ca3e70..78b9ddb1e 100644 --- a/pkg/core/interop/crypto/ecdsa.go +++ b/pkg/core/interop/crypto/ecdsa.go @@ -5,64 +5,16 @@ import ( "errors" "fmt" - "github.com/btcsuite/btcd/btcec" "github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/crypto" - "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" - "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" - "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) -// ECDSASecp256r1Verify checks ECDSA signature using Secp256r1 elliptic curve. -func ECDSASecp256r1Verify(ic *interop.Context) error { - return ecdsaVerify(ic, elliptic.P256()) -} - -// ECDSASecp256k1Verify checks ECDSA signature using Secp256k1 elliptic curve -func ECDSASecp256k1Verify(ic *interop.Context) error { - return ecdsaVerify(ic, btcec.S256()) -} - -// ecdsaVerify is internal representation of ECDSASecp256k1Verify and -// ECDSASecp256r1Verify. -func ecdsaVerify(ic *interop.Context, curve elliptic.Curve) error { - hashToCheck, err := getMessageHash(ic, ic.VM.Estack().Pop().Item()) - if err != nil { - return err - } - keyb := ic.VM.Estack().Pop().Bytes() - signature := ic.VM.Estack().Pop().Bytes() - pkey, err := keys.NewPublicKeyFromBytes(keyb, curve) - if err != nil { - return err - } - res := pkey.Verify(signature, hashToCheck.BytesBE()) - ic.VM.Estack().PushVal(res) - return nil -} - // ECDSASecp256r1CheckMultisig checks multiple ECDSA signatures at once using // Secp256r1 elliptic curve. func ECDSASecp256r1CheckMultisig(ic *interop.Context) error { - return ecdsaCheckMultisig(ic, elliptic.P256()) -} - -// ECDSASecp256k1CheckMultisig checks multiple ECDSA signatures at once using -// Secp256k1 elliptic curve. -func ECDSASecp256k1CheckMultisig(ic *interop.Context) error { - return ecdsaCheckMultisig(ic, btcec.S256()) -} - -// ecdsaCheckMultisig is internal representation of ECDSASecp256r1CheckMultisig and -// ECDSASecp256k1CheckMultisig -func ecdsaCheckMultisig(ic *interop.Context, curve elliptic.Curve) error { - hashToCheck, err := getMessageHash(ic, ic.VM.Estack().Pop().Item()) - if err != nil { - return err - } + hashToCheck := ic.Container.GetSignedHash() pkeys, err := ic.VM.Estack().PopSigElements() if err != nil { return fmt.Errorf("wrong parameters: %w", err) @@ -79,23 +31,21 @@ func ecdsaCheckMultisig(ic *interop.Context, curve elliptic.Curve) error { if len(pkeys) < len(sigs) { return errors.New("more signatures than there are keys") } - sigok := vm.CheckMultisigPar(ic.VM, curve, hashToCheck.BytesBE(), pkeys, sigs) + sigok := vm.CheckMultisigPar(ic.VM, elliptic.P256(), hashToCheck.BytesBE(), pkeys, sigs) ic.VM.Estack().PushVal(sigok) return nil } -func getMessageHash(ic *interop.Context, item stackitem.Item) (util.Uint256, error) { - var msg []byte - switch val := item.(type) { - case *stackitem.Interop: - return val.Value().(crypto.Verifiable).GetSignedHash(), nil - case stackitem.Null: - return ic.Container.GetSignedHash(), nil - default: - var err error - if msg, err = val.TryBytes(); err != nil { - return util.Uint256{}, err - } +// ECDSASecp256r1CheckSig checks ECDSA signature using Secp256r1 elliptic curve. +func ECDSASecp256r1CheckSig(ic *interop.Context) error { + hashToCheck := ic.Container.GetSignedHash() + keyb := ic.VM.Estack().Pop().Bytes() + signature := ic.VM.Estack().Pop().Bytes() + pkey, err := keys.NewPublicKeyFromBytes(keyb, elliptic.P256()) + if err != nil { + return err } - return hash.Sha256(msg), nil + res := pkey.Verify(signature, hashToCheck.BytesBE()) + ic.VM.Estack().PushVal(res) + return nil } diff --git a/pkg/core/interop/crypto/ecdsa_test.go b/pkg/core/interop/crypto/ecdsa_test.go index 1939514f2..e82f64414 100644 --- a/pkg/core/interop/crypto/ecdsa_test.go +++ b/pkg/core/interop/crypto/ecdsa_test.go @@ -13,6 +13,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" + "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -20,116 +21,14 @@ import ( "github.com/stretchr/testify/require" ) -func TestECDSASecp256r1Verify(t *testing.T) { - testECDSAVerify(t, true) -} - -func TestECDSASecp256k1Verify(t *testing.T) { - testECDSAVerify(t, false) -} - -func testECDSAVerify(t *testing.T, isR1 bool) { - var priv *keys.PrivateKey - var err error - if isR1 { - priv, err = keys.NewPrivateKey() - } else { - priv, err = keys.NewSecp256k1PrivateKey() - } - require.NoError(t, err) - - verifyFunc := ECDSASecp256r1Verify - if !isR1 { - verifyFunc = ECDSASecp256k1Verify - } - d := dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false) - ic := &interop.Context{DAO: dao.NewCached(d)} - runCase := func(t *testing.T, isErr bool, result interface{}, args ...interface{}) { - ic.SpawnVM() - for i := range args { - ic.VM.Estack().PushVal(args[i]) - } - - var err error - func() { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("panic: %v", r) - } - }() - err = verifyFunc(ic) - }() - - if isErr { - require.Error(t, err) - return - } - require.NoError(t, err) - require.Equal(t, 1, ic.VM.Estack().Len()) - require.Equal(t, result, ic.VM.Estack().Pop().Value().(bool)) - } - - msg := []byte("test message") - - t.Run("success", func(t *testing.T) { - sign := priv.Sign(msg) - runCase(t, false, true, sign, priv.PublicKey().Bytes(), msg) - }) - - t.Run("signed interop item", func(t *testing.T) { - tx := transaction.New(netmode.UnitTestNet, []byte{0, 1, 2}, 1) - msg := tx.GetSignedPart() - sign := priv.Sign(msg) - runCase(t, false, true, sign, priv.PublicKey().Bytes(), stackitem.NewInterop(tx)) - }) - - t.Run("signed script container", func(t *testing.T) { - tx := transaction.New(netmode.UnitTestNet, []byte{0, 1, 2}, 1) - msg := tx.GetSignedPart() - sign := priv.Sign(msg) - ic.Container = tx - runCase(t, false, true, sign, priv.PublicKey().Bytes(), stackitem.Null{}) - }) - - t.Run("missing arguments", func(t *testing.T) { - runCase(t, true, false) - sign := priv.Sign(msg) - runCase(t, true, false, sign) - runCase(t, true, false, sign, priv.PublicKey().Bytes()) - }) - - t.Run("invalid signature", func(t *testing.T) { - sign := priv.Sign(msg) - sign[0] = ^sign[0] - runCase(t, false, false, sign, priv.PublicKey().Bytes(), msg) - }) - - t.Run("invalid public key", func(t *testing.T) { - sign := priv.Sign(msg) - pub := priv.PublicKey().Bytes() - pub[0] = 0xFF // invalid prefix - runCase(t, true, false, sign, pub, msg) - }) - - t.Run("invalid message", func(t *testing.T) { - sign := priv.Sign(msg) - runCase(t, true, false, sign, priv.PublicKey().Bytes(), - stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray(msg)})) - }) -} - -func initCHECKMULTISIG(isR1 bool, msg []byte, n int) ([]stackitem.Item, []stackitem.Item, map[string]*keys.PublicKey, error) { +func initCHECKMULTISIG(msg []byte, n int) ([]stackitem.Item, []stackitem.Item, map[string]*keys.PublicKey, error) { var err error keyMap := make(map[string]*keys.PublicKey) pkeys := make([]*keys.PrivateKey, n) pubs := make([]stackitem.Item, n) for i := range pubs { - if isR1 { - pkeys[i], err = keys.NewPrivateKey() - } else { - pkeys[i], err = keys.NewSecp256k1PrivateKey() - } + pkeys[i], err = keys.NewPrivateKey() if err != nil { return nil, nil, nil, err } @@ -162,27 +61,29 @@ func subSlice(arr []stackitem.Item, indices []int) []stackitem.Item { return result } -func initCheckMultisigVMNoArgs(isR1 bool) *vm.VM { +func initCheckMultisigVMNoArgs(container *transaction.Transaction) *vm.VM { buf := make([]byte, 5) buf[0] = byte(opcode.SYSCALL) - if isR1 { - binary.LittleEndian.PutUint32(buf[1:], ecdsaSecp256r1CheckMultisigID) - } else { - binary.LittleEndian.PutUint32(buf[1:], ecdsaSecp256k1CheckMultisigID) - } + binary.LittleEndian.PutUint32(buf[1:], neoCryptoCheckMultisigID) - ic := &interop.Context{Trigger: trigger.Verification} + ic := &interop.Context{ + Trigger: trigger.Verification, + Container: container, + } Register(ic) v := ic.SpawnVM() v.LoadScript(buf) return v } -func initCHECKMULTISIGVM(t *testing.T, isR1 bool, n int, ik, is []int) *vm.VM { - v := initCheckMultisigVMNoArgs(isR1) - msg := []byte("NEO - An Open Network For Smart Economy") +func initCHECKMULTISIGVM(t *testing.T, n int, ik, is []int) *vm.VM { + tx := transaction.New(netmode.UnitTestNet, []byte("NEO - An Open Network For Smart Economy"), 10) + tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}} + tx.Scripts = []transaction.Witness{{}} - pubs, sigs, _, err := initCHECKMULTISIG(isR1, msg, n) + v := initCheckMultisigVMNoArgs(tx) + + pubs, sigs, _, err := initCHECKMULTISIG(tx.GetSignedPart(), n) require.NoError(t, err) pubs = subSlice(pubs, ik) @@ -190,13 +91,12 @@ func initCHECKMULTISIGVM(t *testing.T, isR1 bool, n int, ik, is []int) *vm.VM { v.Estack().PushVal(sigs) v.Estack().PushVal(pubs) - v.Estack().PushVal(msg) return v } -func testCHECKMULTISIGGood(t *testing.T, isR1 bool, n int, is []int) { - v := initCHECKMULTISIGVM(t, isR1, n, nil, is) +func testCHECKMULTISIGGood(t *testing.T, n int, is []int) { + v := initCHECKMULTISIGVM(t, n, nil, is) require.NoError(t, v.Run()) assert.Equal(t, 1, v.Estack().Len()) @@ -204,25 +104,21 @@ func testCHECKMULTISIGGood(t *testing.T, isR1 bool, n int, is []int) { } func TestECDSASecp256r1CheckMultisigGood(t *testing.T) { - testCurveCHECKMULTISIGGood(t, true) + testCurveCHECKMULTISIGGood(t) } -func TestECDSASecp256k1CheckMultisigGood(t *testing.T) { - testCurveCHECKMULTISIGGood(t, false) +func testCurveCHECKMULTISIGGood(t *testing.T) { + t.Run("3_1", func(t *testing.T) { testCHECKMULTISIGGood(t, 3, []int{1}) }) + t.Run("2_2", func(t *testing.T) { testCHECKMULTISIGGood(t, 2, []int{0, 1}) }) + t.Run("3_3", func(t *testing.T) { testCHECKMULTISIGGood(t, 3, []int{0, 1, 2}) }) + t.Run("3_2", func(t *testing.T) { testCHECKMULTISIGGood(t, 3, []int{0, 2}) }) + t.Run("4_2", func(t *testing.T) { testCHECKMULTISIGGood(t, 4, []int{0, 2}) }) + t.Run("10_7", func(t *testing.T) { testCHECKMULTISIGGood(t, 10, []int{2, 3, 4, 5, 6, 8, 9}) }) + t.Run("12_9", func(t *testing.T) { testCHECKMULTISIGGood(t, 12, []int{0, 1, 4, 5, 6, 7, 8, 9}) }) } -func testCurveCHECKMULTISIGGood(t *testing.T, isR1 bool) { - t.Run("3_1", func(t *testing.T) { testCHECKMULTISIGGood(t, isR1, 3, []int{1}) }) - t.Run("2_2", func(t *testing.T) { testCHECKMULTISIGGood(t, isR1, 2, []int{0, 1}) }) - t.Run("3_3", func(t *testing.T) { testCHECKMULTISIGGood(t, isR1, 3, []int{0, 1, 2}) }) - t.Run("3_2", func(t *testing.T) { testCHECKMULTISIGGood(t, isR1, 3, []int{0, 2}) }) - t.Run("4_2", func(t *testing.T) { testCHECKMULTISIGGood(t, isR1, 4, []int{0, 2}) }) - t.Run("10_7", func(t *testing.T) { testCHECKMULTISIGGood(t, isR1, 10, []int{2, 3, 4, 5, 6, 8, 9}) }) - t.Run("12_9", func(t *testing.T) { testCHECKMULTISIGGood(t, isR1, 12, []int{0, 1, 4, 5, 6, 7, 8, 9}) }) -} - -func testCHECKMULTISIGBad(t *testing.T, isR1 bool, isErr bool, n int, ik, is []int) { - v := initCHECKMULTISIGVM(t, isR1, n, ik, is) +func testCHECKMULTISIGBad(t *testing.T, isErr bool, n int, ik, is []int) { + v := initCHECKMULTISIGVM(t, n, ik, is) if isErr { require.Error(t, v.Run()) @@ -234,48 +130,99 @@ func testCHECKMULTISIGBad(t *testing.T, isR1 bool, isErr bool, n int, ik, is []i } func TestECDSASecp256r1CheckMultisigBad(t *testing.T) { - testCurveCHECKMULTISIGBad(t, true) + testCurveCHECKMULTISIGBad(t) } -func TestECDSASecp256k1CheckMultisigBad(t *testing.T) { - testCurveCHECKMULTISIGBad(t, false) -} - -func testCurveCHECKMULTISIGBad(t *testing.T, isR1 bool) { - t.Run("1_1 wrong signature", func(t *testing.T) { testCHECKMULTISIGBad(t, isR1, false, 2, []int{0}, []int{1}) }) - t.Run("3_2 wrong order", func(t *testing.T) { testCHECKMULTISIGBad(t, isR1, false, 3, []int{0, 2}, []int{2, 0}) }) - t.Run("3_2 duplicate sig", func(t *testing.T) { testCHECKMULTISIGBad(t, isR1, false, 3, nil, []int{0, 0}) }) - t.Run("1_2 too many signatures", func(t *testing.T) { testCHECKMULTISIGBad(t, isR1, true, 2, []int{0}, []int{0, 1}) }) +func testCurveCHECKMULTISIGBad(t *testing.T) { + t.Run("1_1 wrong signature", func(t *testing.T) { testCHECKMULTISIGBad(t, false, 2, []int{0}, []int{1}) }) + t.Run("3_2 wrong order", func(t *testing.T) { testCHECKMULTISIGBad(t, false, 3, []int{0, 2}, []int{2, 0}) }) + t.Run("3_2 duplicate sig", func(t *testing.T) { testCHECKMULTISIGBad(t, false, 3, nil, []int{0, 0}) }) + t.Run("1_2 too many signatures", func(t *testing.T) { testCHECKMULTISIGBad(t, true, 2, []int{0}, []int{0, 1}) }) t.Run("gas limit exceeded", func(t *testing.T) { - v := initCHECKMULTISIGVM(t, isR1, 1, []int{0}, []int{0}) + v := initCHECKMULTISIGVM(t, 1, []int{0}, []int{0}) v.GasLimit = fee.ECDSAVerifyPrice - 1 require.Error(t, v.Run()) }) msg := []byte("NEO - An Open Network For Smart Economy") - pubs, sigs, _, err := initCHECKMULTISIG(isR1, msg, 1) + pubs, sigs, _, err := initCHECKMULTISIG(msg, 1) require.NoError(t, err) arr := stackitem.NewArray([]stackitem.Item{stackitem.NewArray(nil)}) + tx := transaction.New(netmode.UnitTestNet, []byte("NEO - An Open Network For Smart Economy"), 10) + tx.Signers = []transaction.Signer{{Account: util.Uint160{1, 2, 3}}} + tx.Scripts = []transaction.Witness{{}} - t.Run("invalid message type", func(t *testing.T) { - v := initCheckMultisigVMNoArgs(isR1) - v.Estack().PushVal(sigs) - v.Estack().PushVal(pubs) - v.Estack().PushVal(stackitem.NewArray(nil)) - require.Error(t, v.Run()) - }) t.Run("invalid public keys", func(t *testing.T) { - v := initCheckMultisigVMNoArgs(isR1) + v := initCheckMultisigVMNoArgs(tx) v.Estack().PushVal(sigs) v.Estack().PushVal(arr) - v.Estack().PushVal(msg) require.Error(t, v.Run()) }) t.Run("invalid signatures", func(t *testing.T) { - v := initCheckMultisigVMNoArgs(isR1) + v := initCheckMultisigVMNoArgs(tx) v.Estack().PushVal(arr) v.Estack().PushVal(pubs) - v.Estack().PushVal(msg) require.Error(t, v.Run()) }) } + +func TestCheckSig(t *testing.T) { + priv, err := keys.NewPrivateKey() + require.NoError(t, err) + + verifyFunc := ECDSASecp256r1CheckSig + d := dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false) + ic := &interop.Context{DAO: dao.NewCached(d)} + runCase := func(t *testing.T, isErr bool, result interface{}, args ...interface{}) { + ic.SpawnVM() + for i := range args { + ic.VM.Estack().PushVal(args[i]) + } + + var err error + func() { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("panic: %v", r) + } + }() + err = verifyFunc(ic) + }() + + if isErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Equal(t, 1, ic.VM.Estack().Len()) + require.Equal(t, result, ic.VM.Estack().Pop().Value().(bool)) + } + + tx := transaction.New(netmode.UnitTestNet, []byte{0, 1, 2}, 1) + msg := tx.GetSignedPart() + ic.Container = tx + + t.Run("success", func(t *testing.T) { + sign := priv.Sign(msg) + runCase(t, false, true, sign, priv.PublicKey().Bytes()) + }) + + t.Run("missing argument", func(t *testing.T) { + runCase(t, true, false) + sign := priv.Sign(msg) + runCase(t, true, false, sign) + }) + + t.Run("invalid signature", func(t *testing.T) { + sign := priv.Sign(msg) + sign[0] = ^sign[0] + runCase(t, false, false, sign, priv.PublicKey().Bytes()) + }) + + t.Run("invalid public key", func(t *testing.T) { + sign := priv.Sign(msg) + pub := priv.PublicKey().Bytes() + pub[0] = 0xFF // invalid prefix + runCase(t, true, false, sign, pub) + }) +} diff --git a/pkg/core/interop/crypto/hash.go b/pkg/core/interop/crypto/hash.go deleted file mode 100644 index f00efb3a3..000000000 --- a/pkg/core/interop/crypto/hash.go +++ /dev/null @@ -1,39 +0,0 @@ -package crypto - -import ( - "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/crypto" - "github.com/nspcc-dev/neo-go/pkg/crypto/hash" - "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" -) - -// Sha256 returns sha256 hash of the data. -func Sha256(ic *interop.Context) error { - h, err := getMessageHash(ic, ic.VM.Estack().Pop().Item()) - if err != nil { - return err - } - ic.VM.Estack().PushVal(h.BytesBE()) - return nil -} - -// RipeMD160 returns RipeMD160 hash of the data. -func RipeMD160(ic *interop.Context) error { - var msg []byte - - item := ic.VM.Estack().Pop().Item() - switch val := item.(type) { - case *stackitem.Interop: - msg = val.Value().(crypto.Verifiable).GetSignedPart() - case stackitem.Null: - msg = ic.Container.GetSignedPart() - default: - var err error - if msg, err = val.TryBytes(); err != nil { - return err - } - } - h := hash.RipeMD160(msg).BytesBE() - ic.VM.Estack().PushVal(h) - return nil -} diff --git a/pkg/core/interop/crypto/hash_test.go b/pkg/core/interop/crypto/hash_test.go deleted file mode 100644 index 313074d4a..000000000 --- a/pkg/core/interop/crypto/hash_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package crypto - -import ( - "encoding/hex" - "testing" - - "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/crypto" - "github.com/nspcc-dev/neo-go/pkg/crypto/hash" - "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neo-go/pkg/vm" - "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" - "github.com/stretchr/testify/require" -) - -type testVerifiable []byte - -var _ crypto.Verifiable = testVerifiable{} - -func (v testVerifiable) GetSignedPart() []byte { - return v -} -func (v testVerifiable) GetSignedHash() util.Uint256 { - return hash.Sha256(v) -} - -func testHash0100(t *testing.T, result string, interopFunc func(*interop.Context) error) { - t.Run("good", func(t *testing.T) { - bs := []byte{1, 0} - - checkGood := func(t *testing.T, ic *interop.Context) { - require.NoError(t, interopFunc(ic)) - require.Equal(t, 1, ic.VM.Estack().Len()) - require.Equal(t, result, hex.EncodeToString(ic.VM.Estack().Pop().Bytes())) - } - t.Run("raw bytes", func(t *testing.T) { - ic := &interop.Context{VM: vm.New()} - ic.VM.Estack().PushVal(bs) - checkGood(t, ic) - }) - t.Run("interop", func(t *testing.T) { - ic := &interop.Context{VM: vm.New()} - ic.VM.Estack().PushVal(stackitem.NewInterop(testVerifiable(bs))) - checkGood(t, ic) - }) - t.Run("container", func(t *testing.T) { - ic := &interop.Context{VM: vm.New(), Container: testVerifiable(bs)} - ic.VM.Estack().PushVal(stackitem.Null{}) - checkGood(t, ic) - }) - }) - t.Run("bad message", func(t *testing.T) { - ic := &interop.Context{VM: vm.New()} - ic.VM.Estack().PushVal(stackitem.NewArray(nil)) - require.Error(t, interopFunc(ic)) - }) -} - -func TestSHA256(t *testing.T) { - // 0x0100 hashes to 47dc540c94ceb704a23875c11273e16bb0b8a87aed84de911f2133568115f254 - res := "47dc540c94ceb704a23875c11273e16bb0b8a87aed84de911f2133568115f254" - testHash0100(t, res, Sha256) -} - -func TestRIPEMD160(t *testing.T) { - // 0x0100 hashes to 213492c0c6fc5d61497cf17249dd31cd9964b8a3 - res := "213492c0c6fc5d61497cf17249dd31cd9964b8a3" - testHash0100(t, res, RipeMD160) -} diff --git a/pkg/core/interop/crypto/interop.go b/pkg/core/interop/crypto/interop.go index 74e7815df..ebfd1c719 100644 --- a/pkg/core/interop/crypto/interop.go +++ b/pkg/core/interop/crypto/interop.go @@ -6,21 +6,13 @@ import ( ) var ( - ecdsaSecp256r1VerifyID = interopnames.ToID([]byte(interopnames.NeoCryptoVerifyWithECDsaSecp256r1)) - ecdsaSecp256k1VerifyID = interopnames.ToID([]byte(interopnames.NeoCryptoVerifyWithECDsaSecp256k1)) - ecdsaSecp256r1CheckMultisigID = interopnames.ToID([]byte(interopnames.NeoCryptoCheckMultisigWithECDsaSecp256r1)) - ecdsaSecp256k1CheckMultisigID = interopnames.ToID([]byte(interopnames.NeoCryptoCheckMultisigWithECDsaSecp256k1)) - sha256ID = interopnames.ToID([]byte(interopnames.NeoCryptoSHA256)) - ripemd160ID = interopnames.ToID([]byte(interopnames.NeoCryptoRIPEMD160)) + neoCryptoCheckMultisigID = interopnames.ToID([]byte(interopnames.NeoCryptoCheckMultisig)) + neoCryptoCheckSigID = interopnames.ToID([]byte(interopnames.NeoCryptoCheckSig)) ) var cryptoInterops = []interop.Function{ - {ID: ecdsaSecp256r1VerifyID, Func: ECDSASecp256r1Verify}, - {ID: ecdsaSecp256k1VerifyID, Func: ECDSASecp256k1Verify}, - {ID: ecdsaSecp256r1CheckMultisigID, Func: ECDSASecp256r1CheckMultisig}, - {ID: ecdsaSecp256k1CheckMultisigID, Func: ECDSASecp256k1CheckMultisig}, - {ID: sha256ID, Func: Sha256}, - {ID: ripemd160ID, Func: RipeMD160}, + {ID: neoCryptoCheckMultisigID, Func: ECDSASecp256r1CheckMultisig}, + {ID: neoCryptoCheckSigID, Func: ECDSASecp256r1CheckSig}, } func init() { diff --git a/pkg/core/interop/interopnames/names.go b/pkg/core/interop/interopnames/names.go index d342284f1..96f7e0ddd 100644 --- a/pkg/core/interop/interopnames/names.go +++ b/pkg/core/interop/interopnames/names.go @@ -2,68 +2,46 @@ package interopnames // Names of all used interops. const ( - SystemBinaryAtoi = "System.Binary.Atoi" - SystemBinaryBase58Decode = "System.Binary.Base58Decode" - SystemBinaryBase58Encode = "System.Binary.Base58Encode" - SystemBinaryBase64Decode = "System.Binary.Base64Decode" - SystemBinaryBase64Encode = "System.Binary.Base64Encode" - SystemBinaryDeserialize = "System.Binary.Deserialize" - SystemBinaryItoa = "System.Binary.Itoa" - SystemBinarySerialize = "System.Binary.Serialize" - SystemCallbackCreate = "System.Callback.Create" - SystemCallbackCreateFromMethod = "System.Callback.CreateFromMethod" - SystemCallbackCreateFromSyscall = "System.Callback.CreateFromSyscall" - SystemCallbackInvoke = "System.Callback.Invoke" - SystemContractCall = "System.Contract.Call" - SystemContractCallNative = "System.Contract.CallNative" - SystemContractCreateMultisigAccount = "System.Contract.CreateMultisigAccount" - SystemContractCreateStandardAccount = "System.Contract.CreateStandardAccount" - SystemContractIsStandard = "System.Contract.IsStandard" - SystemContractGetCallFlags = "System.Contract.GetCallFlags" - SystemContractNativeOnPersist = "System.Contract.NativeOnPersist" - SystemContractNativePostPersist = "System.Contract.NativePostPersist" - SystemIteratorCreate = "System.Iterator.Create" - SystemIteratorNext = "System.Iterator.Next" - SystemIteratorValue = "System.Iterator.Value" - SystemJSONDeserialize = "System.Json.Deserialize" - SystemJSONSerialize = "System.Json.Serialize" - SystemRuntimeCheckWitness = "System.Runtime.CheckWitness" - SystemRuntimeGasLeft = "System.Runtime.GasLeft" - SystemRuntimeGetCallingScriptHash = "System.Runtime.GetCallingScriptHash" - SystemRuntimeGetEntryScriptHash = "System.Runtime.GetEntryScriptHash" - SystemRuntimeGetExecutingScriptHash = "System.Runtime.GetExecutingScriptHash" - SystemRuntimeGetInvocationCounter = "System.Runtime.GetInvocationCounter" - SystemRuntimeGetNotifications = "System.Runtime.GetNotifications" - SystemRuntimeGetScriptContainer = "System.Runtime.GetScriptContainer" - SystemRuntimeGetTime = "System.Runtime.GetTime" - SystemRuntimeGetTrigger = "System.Runtime.GetTrigger" - SystemRuntimeLog = "System.Runtime.Log" - SystemRuntimeNotify = "System.Runtime.Notify" - SystemRuntimePlatform = "System.Runtime.Platform" - SystemStorageDelete = "System.Storage.Delete" - SystemStorageFind = "System.Storage.Find" - SystemStorageGet = "System.Storage.Get" - SystemStorageGetContext = "System.Storage.GetContext" - SystemStorageGetReadOnlyContext = "System.Storage.GetReadOnlyContext" - SystemStoragePut = "System.Storage.Put" - SystemStorageAsReadOnly = "System.Storage.AsReadOnly" - NeoCryptoVerifyWithECDsaSecp256r1 = "Neo.Crypto.VerifyWithECDsaSecp256r1" - NeoCryptoVerifyWithECDsaSecp256k1 = "Neo.Crypto.VerifyWithECDsaSecp256k1" - NeoCryptoCheckMultisigWithECDsaSecp256r1 = "Neo.Crypto.CheckMultisigWithECDsaSecp256r1" - NeoCryptoCheckMultisigWithECDsaSecp256k1 = "Neo.Crypto.CheckMultisigWithECDsaSecp256k1" - NeoCryptoSHA256 = "Neo.Crypto.SHA256" - NeoCryptoRIPEMD160 = "Neo.Crypto.RIPEMD160" + SystemCallbackCreate = "System.Callback.Create" + SystemCallbackCreateFromMethod = "System.Callback.CreateFromMethod" + SystemCallbackCreateFromSyscall = "System.Callback.CreateFromSyscall" + SystemCallbackInvoke = "System.Callback.Invoke" + SystemContractCall = "System.Contract.Call" + SystemContractCallNative = "System.Contract.CallNative" + SystemContractCreateMultisigAccount = "System.Contract.CreateMultisigAccount" + SystemContractCreateStandardAccount = "System.Contract.CreateStandardAccount" + SystemContractIsStandard = "System.Contract.IsStandard" + SystemContractGetCallFlags = "System.Contract.GetCallFlags" + SystemContractNativeOnPersist = "System.Contract.NativeOnPersist" + SystemContractNativePostPersist = "System.Contract.NativePostPersist" + SystemIteratorCreate = "System.Iterator.Create" + SystemIteratorNext = "System.Iterator.Next" + SystemIteratorValue = "System.Iterator.Value" + SystemRuntimeCheckWitness = "System.Runtime.CheckWitness" + SystemRuntimeGasLeft = "System.Runtime.GasLeft" + SystemRuntimeGetCallingScriptHash = "System.Runtime.GetCallingScriptHash" + SystemRuntimeGetEntryScriptHash = "System.Runtime.GetEntryScriptHash" + SystemRuntimeGetExecutingScriptHash = "System.Runtime.GetExecutingScriptHash" + SystemRuntimeGetInvocationCounter = "System.Runtime.GetInvocationCounter" + SystemRuntimeGetNotifications = "System.Runtime.GetNotifications" + SystemRuntimeGetScriptContainer = "System.Runtime.GetScriptContainer" + SystemRuntimeGetTime = "System.Runtime.GetTime" + SystemRuntimeGetTrigger = "System.Runtime.GetTrigger" + SystemRuntimeLog = "System.Runtime.Log" + SystemRuntimeNotify = "System.Runtime.Notify" + SystemRuntimePlatform = "System.Runtime.Platform" + SystemStorageDelete = "System.Storage.Delete" + SystemStorageFind = "System.Storage.Find" + SystemStorageGet = "System.Storage.Get" + SystemStorageGetContext = "System.Storage.GetContext" + SystemStorageGetReadOnlyContext = "System.Storage.GetReadOnlyContext" + SystemStoragePut = "System.Storage.Put" + SystemStorageAsReadOnly = "System.Storage.AsReadOnly" + NeoCryptoCheckMultisig = "Neo.Crypto.CheckMultisig" + NeoCryptoCheckSig = "Neo.Crypto.CheckSig" ) var names = []string{ - SystemBinaryAtoi, - SystemBinaryBase58Decode, - SystemBinaryBase58Encode, - SystemBinaryBase64Decode, - SystemBinaryBase64Encode, - SystemBinaryDeserialize, - SystemBinaryItoa, - SystemBinarySerialize, SystemCallbackCreate, SystemCallbackCreateFromMethod, SystemCallbackCreateFromSyscall, @@ -79,8 +57,6 @@ var names = []string{ SystemIteratorCreate, SystemIteratorNext, SystemIteratorValue, - SystemJSONDeserialize, - SystemJSONSerialize, SystemRuntimeCheckWitness, SystemRuntimeGasLeft, SystemRuntimeGetCallingScriptHash, @@ -101,10 +77,6 @@ var names = []string{ SystemStorageGetReadOnlyContext, SystemStoragePut, SystemStorageAsReadOnly, - NeoCryptoVerifyWithECDsaSecp256r1, - NeoCryptoVerifyWithECDsaSecp256k1, - NeoCryptoCheckMultisigWithECDsaSecp256r1, - NeoCryptoCheckMultisigWithECDsaSecp256k1, - NeoCryptoSHA256, - NeoCryptoRIPEMD160, + NeoCryptoCheckMultisig, + NeoCryptoCheckSig, } diff --git a/pkg/core/interop/json/json.go b/pkg/core/interop/json/json.go deleted file mode 100644 index 5be269939..000000000 --- a/pkg/core/interop/json/json.go +++ /dev/null @@ -1,28 +0,0 @@ -package json - -import ( - "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" -) - -// Serialize handles System.JSON.Serialize syscall. -func Serialize(ic *interop.Context) error { - item := ic.VM.Estack().Pop().Item() - data, err := stackitem.ToJSON(item) - if err != nil { - return err - } - ic.VM.Estack().PushVal(data) - return nil -} - -// Deserialize handles System.JSON.Deserialize syscall. -func Deserialize(ic *interop.Context) error { - data := ic.VM.Estack().Pop().Bytes() - item, err := stackitem.FromJSON(data) - if err != nil { - return err - } - ic.VM.Estack().PushVal(item) - return nil -} diff --git a/pkg/core/interop/json/json_test.go b/pkg/core/interop/json/json_test.go deleted file mode 100644 index 056a29c76..000000000 --- a/pkg/core/interop/json/json_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package json - -import ( - "encoding/binary" - "testing" - - "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" - "github.com/nspcc-dev/neo-go/pkg/vm/opcode" - "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" - "github.com/stretchr/testify/require" -) - -var ( - serializeID = interopnames.ToID([]byte(interopnames.SystemJSONSerialize)) - deserializeID = interopnames.ToID([]byte(interopnames.SystemJSONDeserialize)) -) - -var jsonInterops = []interop.Function{ - {ID: serializeID, Func: Serialize}, - {ID: deserializeID, Func: Deserialize}, -} - -func init() { - interop.Sort(jsonInterops) -} - -func getTestFunc(id uint32, arg interface{}, result interface{}) func(t *testing.T) { - prog := make([]byte, 5) - prog[0] = byte(opcode.SYSCALL) - binary.LittleEndian.PutUint32(prog[1:], id) - - return func(t *testing.T) { - ic := &interop.Context{} - ic.Functions = append(ic.Functions, jsonInterops) - v := ic.SpawnVM() - v.LoadScript(prog) - v.Estack().PushVal(arg) - if result == nil { - require.Error(t, v.Run()) - return - } - require.NoError(t, v.Run()) - require.Equal(t, stackitem.Make(result), v.Estack().Pop().Item()) - } -} - -func TestSerialize(t *testing.T) { - t.Run("Serialize", func(t *testing.T) { - t.Run("Good", getTestFunc(serializeID, 42, []byte("42"))) - t.Run("Bad", func(t *testing.T) { - arr := stackitem.NewArray([]stackitem.Item{ - stackitem.NewByteArray(make([]byte, stackitem.MaxSize/2)), - stackitem.NewByteArray(make([]byte, stackitem.MaxSize/2)), - }) - getTestFunc(serializeID, arr, nil)(t) - }) - }) - t.Run("Deserialize", func(t *testing.T) { - t.Run("Good", getTestFunc(deserializeID, []byte("42"), 42)) - t.Run("Bad", getTestFunc(deserializeID, []byte("{]"), nil)) - }) -} diff --git a/pkg/core/interop_system_test.go b/pkg/core/interop_system_test.go index ff2d090f8..bd3084beb 100644 --- a/pkg/core/interop_system_test.go +++ b/pkg/core/interop_system_test.go @@ -316,6 +316,7 @@ func TestStorageDelete(t *testing.T) { // getTestContractState returns 2 contracts second of which is allowed to call the first. func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) { mgmtHash := bc.ManagementContractHash() + stdHash := bc.contracts.Std.Hash w := io.NewBufBinWriter() emit.Opcodes(w.BinWriter, opcode.ABORT) @@ -339,16 +340,20 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) { emit.Opcodes(w.BinWriter, opcode.LDSFLD0, opcode.SUB, opcode.CONVERT, opcode.Opcode(stackitem.BooleanT), opcode.RET) deployOff := w.Len() - emit.Opcodes(w.BinWriter, opcode.SWAP, opcode.JMPIF, 2+8+1+1+5+3) - emit.String(w.BinWriter, "create") // 8 bytes - emit.Int(w.BinWriter, 2) // 1 byte - emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte - emit.Syscall(w.BinWriter, interopnames.SystemBinarySerialize) // 5 bytes - emit.Opcodes(w.BinWriter, opcode.CALL, 3+8+1+1+5+3, opcode.RET) - emit.String(w.BinWriter, "update") // 8 bytes - emit.Int(w.BinWriter, 2) // 1 byte - emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte - emit.Syscall(w.BinWriter, interopnames.SystemBinarySerialize) // 5 bytes + emit.Opcodes(w.BinWriter, opcode.SWAP, opcode.JMPIF, 2+8+1+1+1+1+39+3) + emit.String(w.BinWriter, "create") // 8 bytes + emit.Int(w.BinWriter, 2) // 1 byte + emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte + emit.Int(w.BinWriter, 1) // 1 byte (args count for `serialize`) + emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte (pack args into array for `serialize`) + emit.AppCallNoArgs(w.BinWriter, stdHash, "serialize", callflag.All) // 39 bytes + emit.Opcodes(w.BinWriter, opcode.CALL, 3+8+1+1+1+1+39+3, opcode.RET) + emit.String(w.BinWriter, "update") // 8 bytes + emit.Int(w.BinWriter, 2) // 1 byte + emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte + emit.Int(w.BinWriter, 1) // 1 byte (args count for `serialize`) + emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte (pack args into array for `serialize`) + emit.AppCallNoArgs(w.BinWriter, stdHash, "serialize", callflag.All) // 39 bytes emit.Opcodes(w.BinWriter, opcode.CALL, 3, opcode.RET) putValOff := w.Len() emit.String(w.BinWriter, "initial") diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 9b961ccff..ee30a429b 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -10,12 +10,10 @@ package core import ( "github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/core/interop/binary" "github.com/nspcc-dev/neo-go/pkg/core/interop/contract" "github.com/nspcc-dev/neo-go/pkg/core/interop/crypto" "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "github.com/nspcc-dev/neo-go/pkg/core/interop/iterator" - "github.com/nspcc-dev/neo-go/pkg/core/interop/json" "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" @@ -32,14 +30,6 @@ func SpawnVM(ic *interop.Context) *vm.VM { // All lists are sorted, keep 'em this way, please. var systemInterops = []interop.Function{ - {Name: interopnames.SystemBinaryAtoi, Func: binary.Atoi, Price: 1 << 12, ParamCount: 2}, - {Name: interopnames.SystemBinaryBase58Decode, Func: binary.DecodeBase58, Price: 1 << 12, ParamCount: 1}, - {Name: interopnames.SystemBinaryBase58Encode, Func: binary.EncodeBase58, Price: 1 << 12, ParamCount: 1}, - {Name: interopnames.SystemBinaryBase64Decode, Func: binary.DecodeBase64, Price: 1 << 12, ParamCount: 1}, - {Name: interopnames.SystemBinaryBase64Encode, Func: binary.EncodeBase64, Price: 1 << 12, ParamCount: 1}, - {Name: interopnames.SystemBinaryDeserialize, Func: binary.Deserialize, Price: 1 << 14, ParamCount: 1}, - {Name: interopnames.SystemBinaryItoa, Func: binary.Itoa, Price: 1 << 12, ParamCount: 2}, - {Name: interopnames.SystemBinarySerialize, Func: binary.Serialize, Price: 1 << 12, ParamCount: 1}, {Name: interopnames.SystemContractCall, Func: contract.Call, Price: 1 << 15, RequiredFlags: callflag.ReadStates | callflag.AllowCall, ParamCount: 4}, {Name: interopnames.SystemContractCallNative, Func: native.Call, Price: 0, ParamCount: 1}, @@ -52,8 +42,6 @@ var systemInterops = []interop.Function{ {Name: interopnames.SystemIteratorCreate, Func: iterator.Create, Price: 1 << 4, ParamCount: 1}, {Name: interopnames.SystemIteratorNext, Func: iterator.Next, Price: 1 << 15, ParamCount: 1}, {Name: interopnames.SystemIteratorValue, Func: iterator.Value, Price: 1 << 4, ParamCount: 1}, - {Name: interopnames.SystemJSONDeserialize, Func: json.Deserialize, Price: 1 << 14, ParamCount: 1}, - {Name: interopnames.SystemJSONSerialize, Func: json.Serialize, Price: 1 << 12, ParamCount: 1}, {Name: interopnames.SystemRuntimeCheckWitness, Func: runtime.CheckWitness, Price: 1 << 10, RequiredFlags: callflag.NoneFlag, ParamCount: 1}, {Name: interopnames.SystemRuntimeGasLeft, Func: runtime.GasLeft, Price: 1 << 4}, @@ -87,14 +75,8 @@ var systemInterops = []interop.Function{ } var neoInterops = []interop.Function{ - {Name: interopnames.NeoCryptoVerifyWithECDsaSecp256r1, Func: crypto.ECDSASecp256r1Verify, - Price: fee.ECDSAVerifyPrice, ParamCount: 3}, - {Name: interopnames.NeoCryptoVerifyWithECDsaSecp256k1, Func: crypto.ECDSASecp256k1Verify, - Price: fee.ECDSAVerifyPrice, ParamCount: 3}, - {Name: interopnames.NeoCryptoCheckMultisigWithECDsaSecp256r1, Func: crypto.ECDSASecp256r1CheckMultisig, Price: 0, ParamCount: 3}, - {Name: interopnames.NeoCryptoCheckMultisigWithECDsaSecp256k1, Func: crypto.ECDSASecp256k1CheckMultisig, Price: 0, ParamCount: 3}, - {Name: interopnames.NeoCryptoSHA256, Func: crypto.Sha256, Price: 1 << 15, ParamCount: 1}, - {Name: interopnames.NeoCryptoRIPEMD160, Func: crypto.RipeMD160, Price: 1 << 15, ParamCount: 1}, + {Name: interopnames.NeoCryptoCheckMultisig, Func: crypto.ECDSASecp256r1CheckMultisig, Price: 0, ParamCount: 2}, + {Name: interopnames.NeoCryptoCheckSig, Func: crypto.ECDSASecp256r1CheckSig, Price: fee.ECDSAVerifyPrice, ParamCount: 2}, } // initIDinInteropsSlice initializes IDs from names in one given diff --git a/pkg/core/native/contract.go b/pkg/core/native/contract.go index 7a0479144..20566f969 100644 --- a/pkg/core/native/contract.go +++ b/pkg/core/native/contract.go @@ -24,6 +24,8 @@ type Contracts struct { Designate *Designate NameService *NameService Notary *Notary + Crypto *Crypto + Std *Std Contracts []interop.Contract // persistScript is vm script which executes "onPersist" method of every native contract. persistScript []byte @@ -61,6 +63,14 @@ func NewContracts(p2pSigExtensionsEnabled bool) *Contracts { cs.Management = mgmt cs.Contracts = append(cs.Contracts, mgmt) + s := newStd() + cs.Std = s + cs.Contracts = append(cs.Contracts, s) + + c := newCrypto() + cs.Crypto = c + cs.Contracts = append(cs.Contracts, c) + ledger := newLedger() cs.Ledger = ledger cs.Contracts = append(cs.Contracts, ledger) diff --git a/pkg/core/native/crypto.go b/pkg/core/native/crypto.go new file mode 100644 index 000000000..b377dd82e --- /dev/null +++ b/pkg/core/native/crypto.go @@ -0,0 +1,138 @@ +package native + +import ( + "crypto/elliptic" + "errors" + "fmt" + + "github.com/btcsuite/btcd/btcec" + "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" + "github.com/nspcc-dev/neo-go/pkg/crypto/hash" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" +) + +// Crypto represents CryptoLib contract. +type Crypto struct { + interop.ContractMD +} + +// NamedCurve identifies named elliptic curves. +type NamedCurve byte + +// Various named elliptic curves. +const ( + Secp256k1 NamedCurve = 22 + Secp256r1 NamedCurve = 23 +) + +const cryptoContractID = -3 + +func newCrypto() *Crypto { + c := &Crypto{ContractMD: *interop.NewContractMD(nativenames.CryptoLib, cryptoContractID)} + defer c.UpdateHash() + + desc := newDescriptor("sha256", smartcontract.ByteArrayType, + manifest.NewParameter("data", smartcontract.ByteArrayType)) + md := newMethodAndPrice(c.sha256, 1<<15, callflag.NoneFlag) + c.AddMethod(md, desc) + + desc = newDescriptor("ripemd160", smartcontract.ByteArrayType, + manifest.NewParameter("data", smartcontract.ByteArrayType)) + md = newMethodAndPrice(c.ripemd160, 1<<15, callflag.NoneFlag) + c.AddMethod(md, desc) + + desc = newDescriptor("verifyWithECDsa", smartcontract.BoolType, + manifest.NewParameter("message", smartcontract.ByteArrayType), + manifest.NewParameter("pubkey", smartcontract.ByteArrayType), + manifest.NewParameter("signature", smartcontract.ByteArrayType), + manifest.NewParameter("curve", smartcontract.IntegerType)) + md = newMethodAndPrice(c.verifyWithECDsa, 1<<15, callflag.NoneFlag) + c.AddMethod(md, desc) + return c +} + +func (c *Crypto) sha256(_ *interop.Context, args []stackitem.Item) stackitem.Item { + bs, err := args[0].TryBytes() + if err != nil { + panic(err) + } + return stackitem.NewByteArray(hash.Sha256(bs).BytesBE()) +} + +func (c *Crypto) ripemd160(_ *interop.Context, args []stackitem.Item) stackitem.Item { + bs, err := args[0].TryBytes() + if err != nil { + panic(err) + } + return stackitem.NewByteArray(hash.RipeMD160(bs).BytesBE()) +} + +func (c *Crypto) verifyWithECDsa(_ *interop.Context, args []stackitem.Item) stackitem.Item { + msg, err := args[0].TryBytes() + if err != nil { + panic(fmt.Errorf("invalid message stackitem: %w", err)) + } + hashToCheck := hash.Sha256(msg) + pubkey, err := args[1].TryBytes() + if err != nil { + panic(fmt.Errorf("invalid pubkey stackitem: %w", err)) + } + signature, err := args[2].TryBytes() + if err != nil { + panic(fmt.Errorf("invalid signature stackitem: %w", err)) + } + curve, err := curveFromStackitem(args[3]) + if err != nil { + panic(fmt.Errorf("invalid curve stackitem: %w", err)) + } + pkey, err := keys.NewPublicKeyFromBytes(pubkey, curve) + if err != nil { + panic(fmt.Errorf("failed to decode pubkey: %w", err)) + } + res := pkey.Verify(signature, hashToCheck.BytesBE()) + return stackitem.NewBool(res) +} + +func curveFromStackitem(si stackitem.Item) (elliptic.Curve, error) { + curve, err := si.TryInteger() + if err != nil { + return nil, err + } + if !curve.IsInt64() { + return nil, errors.New("not an int64") + } + c := curve.Int64() + switch c { + case int64(Secp256k1): + return btcec.S256(), nil + case int64(Secp256r1): + return elliptic.P256(), nil + default: + return nil, errors.New("unsupported curve type") + } +} + +// Metadata implements Contract interface. +func (c *Crypto) Metadata() *interop.ContractMD { + return &c.ContractMD +} + +// Initialize implements Contract interface. +func (c *Crypto) Initialize(ic *interop.Context) error { + return nil +} + +// OnPersist implements Contract interface. +func (c *Crypto) OnPersist(ic *interop.Context) error { + return nil +} + +// PostPersist implements Contract interface. +func (c *Crypto) PostPersist(ic *interop.Context) error { + return nil +} diff --git a/pkg/core/native/crypto_test.go b/pkg/core/native/crypto_test.go new file mode 100644 index 000000000..6c20854d2 --- /dev/null +++ b/pkg/core/native/crypto_test.go @@ -0,0 +1,122 @@ +package native + +import ( + "encoding/hex" + "math" + "math/big" + "testing" + + "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/vm" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/stretchr/testify/require" +) + +func TestSha256(t *testing.T) { + c := newCrypto() + ic := &interop.Context{VM: vm.New()} + + t.Run("bad arg type", func(t *testing.T) { + require.Panics(t, func() { + c.sha256(ic, []stackitem.Item{stackitem.NewInterop(nil)}) + }) + }) + t.Run("good", func(t *testing.T) { + // 0x0100 hashes to 47dc540c94ceb704a23875c11273e16bb0b8a87aed84de911f2133568115f254 + require.Equal(t, "47dc540c94ceb704a23875c11273e16bb0b8a87aed84de911f2133568115f254", hex.EncodeToString(c.sha256(ic, []stackitem.Item{stackitem.NewByteArray([]byte{1, 0})}).Value().([]byte))) + }) +} + +func TestRIPEMD160(t *testing.T) { + c := newCrypto() + ic := &interop.Context{VM: vm.New()} + + t.Run("bad arg type", func(t *testing.T) { + require.Panics(t, func() { + c.ripemd160(ic, []stackitem.Item{stackitem.NewInterop(nil)}) + }) + }) + t.Run("good", func(t *testing.T) { + // 0x0100 hashes to 213492c0c6fc5d61497cf17249dd31cd9964b8a3 + require.Equal(t, "213492c0c6fc5d61497cf17249dd31cd9964b8a3", hex.EncodeToString(c.ripemd160(ic, []stackitem.Item{stackitem.NewByteArray([]byte{1, 0})}).Value().([]byte))) + }) +} + +func TestCryptoLibVerifyWithECDsa(t *testing.T) { + t.Run("R1", func(t *testing.T) { + testECDSAVerify(t, Secp256r1) + }) + t.Run("K1", func(t *testing.T) { + testECDSAVerify(t, Secp256k1) + }) +} + +func testECDSAVerify(t *testing.T, curve NamedCurve) { + var ( + priv *keys.PrivateKey + err error + c = newCrypto() + ic = &interop.Context{VM: vm.New()} + actual stackitem.Item + ) + switch curve { + case Secp256k1: + priv, err = keys.NewSecp256k1PrivateKey() + case Secp256r1: + priv, err = keys.NewPrivateKey() + default: + t.Fatal("unknown curve") + } + require.NoError(t, err) + + runCase := func(t *testing.T, isErr bool, result interface{}, args ...interface{}) { + argsArr := make([]stackitem.Item, len(args)) + for i := range args { + argsArr[i] = stackitem.Make(args[i]) + } + if isErr { + require.Panics(t, func() { + _ = c.verifyWithECDsa(ic, argsArr) + }) + } else { + require.NotPanics(t, func() { + actual = c.verifyWithECDsa(ic, argsArr) + }) + require.Equal(t, stackitem.Make(result), actual) + } + } + + msg := []byte("test message") + sign := priv.Sign(msg) + + t.Run("bad message item", func(t *testing.T) { + runCase(t, true, false, stackitem.NewInterop("cheburek"), priv.PublicKey().Bytes(), sign, int64(curve)) + }) + t.Run("bad pubkey item", func(t *testing.T) { + runCase(t, true, false, msg, stackitem.NewInterop("cheburek"), sign, int64(curve)) + }) + t.Run("bad pubkey bytes", func(t *testing.T) { + runCase(t, true, false, msg, []byte{1, 2, 3}, sign, int64(curve)) + }) + t.Run("bad signature item", func(t *testing.T) { + runCase(t, true, false, msg, priv.PublicKey().Bytes(), stackitem.NewInterop("cheburek"), int64(curve)) + }) + t.Run("bad curve item", func(t *testing.T) { + runCase(t, true, false, msg, priv.PublicKey().Bytes(), sign, stackitem.NewInterop("cheburek")) + }) + t.Run("bad curve value", func(t *testing.T) { + runCase(t, true, false, msg, priv.PublicKey().Bytes(), sign, new(big.Int).Add(big.NewInt(math.MaxInt64), big.NewInt(1))) + }) + t.Run("unknown curve", func(t *testing.T) { + runCase(t, true, false, msg, priv.PublicKey().Bytes(), sign, int64(123)) + }) + t.Run("invalid signature", func(t *testing.T) { + s := priv.Sign(msg) + s[0] = ^s[0] + runCase(t, false, false, s, priv.PublicKey().Bytes(), msg, int64(curve)) + }) + t.Run("success", func(t *testing.T) { + runCase(t, false, true, msg, priv.PublicKey().Bytes(), sign, int64(curve)) + }) +} diff --git a/pkg/core/native/designate.go b/pkg/core/native/designate.go index 44e40978d..1186d6d7a 100644 --- a/pkg/core/native/designate.go +++ b/pkg/core/native/designate.go @@ -52,7 +52,7 @@ type roleData struct { } const ( - designateContractID = -6 + designateContractID = -8 // maxNodeCount is the maximum number of nodes to set the role for. maxNodeCount = 32 diff --git a/pkg/core/native/ledger.go b/pkg/core/native/ledger.go index 60ea77594..4c76da1d5 100644 --- a/pkg/core/native/ledger.go +++ b/pkg/core/native/ledger.go @@ -26,7 +26,7 @@ type Ledger struct { } const ( - ledgerContractID = -2 + ledgerContractID = -4 prefixBlockHash = 9 prefixCurrentBlock = 12 diff --git a/pkg/core/native/name_service.go b/pkg/core/native/name_service.go index 23d263156..dbaefde72 100644 --- a/pkg/core/native/name_service.go +++ b/pkg/core/native/name_service.go @@ -54,7 +54,7 @@ const ( ) const ( - nameServiceID = -8 + nameServiceID = -10 prefixRoots = 10 prefixDomainPrice = 22 diff --git a/pkg/core/native/native_gas.go b/pkg/core/native/native_gas.go index 8f18c9789..61e3b79bb 100644 --- a/pkg/core/native/native_gas.go +++ b/pkg/core/native/native_gas.go @@ -18,7 +18,7 @@ type GAS struct { NEO *NEO } -const gasContractID = -4 +const gasContractID = -6 // GASFactor is a divisor for finding GAS integral value. const GASFactor = NEOTotalSupply diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index 17b35acdc..543b09842 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -50,7 +50,7 @@ type NEO struct { } const ( - neoContractID = -3 + neoContractID = -5 // NEOTotalSupply is the total amount of NEO in the system. NEOTotalSupply = 100000000 // prefixCandidate is a prefix used to store validator's data. diff --git a/pkg/core/native/nativenames/names.go b/pkg/core/native/nativenames/names.go index 0f37e5ba8..c7cbe2ca2 100644 --- a/pkg/core/native/nativenames/names.go +++ b/pkg/core/native/nativenames/names.go @@ -11,4 +11,6 @@ const ( Designation = "RoleManagement" Notary = "Notary" NameService = "NameService" + CryptoLib = "CryptoLib" + StdLib = "StdLib" ) diff --git a/pkg/core/native/oracle.go b/pkg/core/native/oracle.go index e6029cc62..bc2859fc8 100644 --- a/pkg/core/native/oracle.go +++ b/pkg/core/native/oracle.go @@ -47,7 +47,7 @@ type Oracle struct { } const ( - oracleContractID = -7 + oracleContractID = -9 maxURLLength = 256 maxFilterLength = 128 maxCallbackLength = 32 diff --git a/pkg/core/native/policy.go b/pkg/core/native/policy.go index 368d4926c..882ccc935 100644 --- a/pkg/core/native/policy.go +++ b/pkg/core/native/policy.go @@ -19,7 +19,7 @@ import ( ) const ( - policyContractID = -5 + policyContractID = -7 defaultExecFeeFactor = interop.DefaultBaseExecFee defaultFeePerByte = 1000 diff --git a/pkg/core/native/std.go b/pkg/core/native/std.go new file mode 100644 index 000000000..ed559000f --- /dev/null +++ b/pkg/core/native/std.go @@ -0,0 +1,273 @@ +package native + +import ( + "encoding/base64" + "encoding/hex" + "errors" + "math/big" + "strings" + + "github.com/mr-tron/base58" + "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" + "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" +) + +// Std represents StdLib contract. +type Std struct { + interop.ContractMD +} + +const stdContractID = -2 + +var ( + // ErrInvalidBase is returned when base is invalid. + ErrInvalidBase = errors.New("invalid base") + // ErrInvalidFormat is returned when string is not a number. + ErrInvalidFormat = errors.New("invalid format") +) + +func newStd() *Std { + s := &Std{ContractMD: *interop.NewContractMD(nativenames.StdLib, stdContractID)} + defer s.UpdateHash() + + desc := newDescriptor("serialize", smartcontract.ByteArrayType, + manifest.NewParameter("item", smartcontract.AnyType)) + md := newMethodAndPrice(s.serialize, 1<<12, callflag.NoneFlag) + s.AddMethod(md, desc) + + desc = newDescriptor("deserialize", smartcontract.AnyType, + manifest.NewParameter("data", smartcontract.ByteArrayType)) + md = newMethodAndPrice(s.deserialize, 1<<14, callflag.NoneFlag) + s.AddMethod(md, desc) + + desc = newDescriptor("jsonSerialize", smartcontract.ByteArrayType, + manifest.NewParameter("item", smartcontract.AnyType)) + md = newMethodAndPrice(s.jsonSerialize, 1<<12, callflag.NoneFlag) + s.AddMethod(md, desc) + + desc = newDescriptor("jsonDeserialize", smartcontract.AnyType, + manifest.NewParameter("json", smartcontract.ByteArrayType)) + md = newMethodAndPrice(s.jsonDeserialize, 1<<14, callflag.NoneFlag) + s.AddMethod(md, desc) + + desc = newDescriptor("itoa", smartcontract.StringType, + manifest.NewParameter("value", smartcontract.IntegerType), + manifest.NewParameter("base", smartcontract.IntegerType)) + md = newMethodAndPrice(s.itoa, 1<<12, callflag.NoneFlag) + s.AddMethod(md, desc) + + desc = newDescriptor("atoi", smartcontract.IntegerType, + manifest.NewParameter("value", smartcontract.StringType), + manifest.NewParameter("base", smartcontract.IntegerType)) + md = newMethodAndPrice(s.atoi, 1<<12, callflag.NoneFlag) + s.AddMethod(md, desc) + + desc = newDescriptor("base64Encode", smartcontract.StringType, + manifest.NewParameter("data", smartcontract.ByteArrayType)) + md = newMethodAndPrice(s.base64Encode, 1<<12, callflag.NoneFlag) + s.AddMethod(md, desc) + + desc = newDescriptor("base64Decode", smartcontract.ByteArrayType, + manifest.NewParameter("s", smartcontract.StringType)) + md = newMethodAndPrice(s.base64Decode, 1<<12, callflag.NoneFlag) + s.AddMethod(md, desc) + + desc = newDescriptor("base58Encode", smartcontract.StringType, + manifest.NewParameter("data", smartcontract.ByteArrayType)) + md = newMethodAndPrice(s.base58Encode, 1<<12, callflag.NoneFlag) + s.AddMethod(md, desc) + + desc = newDescriptor("base58Decode", smartcontract.ByteArrayType, + manifest.NewParameter("s", smartcontract.StringType)) + md = newMethodAndPrice(s.base58Decode, 1<<12, callflag.NoneFlag) + s.AddMethod(md, desc) + + return s +} + +func (s *Std) serialize(_ *interop.Context, args []stackitem.Item) stackitem.Item { + data, err := stackitem.SerializeItem(args[0]) + if err != nil { + panic(err) + } + if len(data) > stackitem.MaxSize { + panic(errors.New("too big item")) + } + + return stackitem.NewByteArray(data) +} + +func (s *Std) deserialize(_ *interop.Context, args []stackitem.Item) stackitem.Item { + data, err := args[0].TryBytes() + if err != nil { + panic(err) + } + + item, err := stackitem.DeserializeItem(data) + if err != nil { + panic(err) + } + + return item +} + +func (s *Std) jsonSerialize(_ *interop.Context, args []stackitem.Item) stackitem.Item { + data, err := stackitem.ToJSON(args[0]) + if err != nil { + panic(err) + } + if len(data) > stackitem.MaxSize { + panic(errors.New("too big item")) + } + + return stackitem.NewByteArray(data) +} + +func (s *Std) jsonDeserialize(_ *interop.Context, args []stackitem.Item) stackitem.Item { + data, err := args[0].TryBytes() + if err != nil { + panic(err) + } + + item, err := stackitem.FromJSON(data) + if err != nil { + panic(err) + } + + return item +} + +func (s *Std) itoa(_ *interop.Context, args []stackitem.Item) stackitem.Item { + num := toBigInt(args[0]) + base := toBigInt(args[1]) + if !base.IsInt64() { + panic(ErrInvalidBase) + } + var str string + switch b := base.Int64(); b { + case 10: + str = num.Text(10) + case 16: + if num.Sign() == 0 { + str = "0" + break + } + bs := bigint.ToBytes(num) + reverse(bs) + str = hex.EncodeToString(bs) + if pad := bs[0] & 0xF8; pad == 0 || pad == 0xF8 { + str = str[1:] + } + str = strings.ToUpper(str) + default: + panic(ErrInvalidBase) + } + return stackitem.NewByteArray([]byte(str)) +} + +func (s *Std) atoi(_ *interop.Context, args []stackitem.Item) stackitem.Item { + num := toString(args[0]) + base := toBigInt(args[1]) + if !base.IsInt64() { + panic(ErrInvalidBase) + } + var bi *big.Int + switch b := base.Int64(); b { + case 10: + var ok bool + bi, ok = new(big.Int).SetString(num, int(b)) + if !ok { + panic(ErrInvalidFormat) + } + case 16: + changed := len(num)%2 != 0 + if changed { + num = "0" + num + } + bs, err := hex.DecodeString(num) + if err != nil { + panic(ErrInvalidFormat) + } + if changed && bs[0]&0x8 != 0 { + bs[0] |= 0xF0 + } + reverse(bs) + bi = bigint.FromBytes(bs) + default: + panic(ErrInvalidBase) + } + + return stackitem.NewBigInteger(bi) +} + +func reverse(b []byte) { + l := len(b) + for i := 0; i < l/2; i++ { + b[i], b[l-i-1] = b[l-i-1], b[i] + } +} + +func (s *Std) base64Encode(_ *interop.Context, args []stackitem.Item) stackitem.Item { + src, err := args[0].TryBytes() + if err != nil { + panic(err) + } + result := base64.StdEncoding.EncodeToString(src) + + return stackitem.NewByteArray([]byte(result)) +} + +func (s *Std) base64Decode(_ *interop.Context, args []stackitem.Item) stackitem.Item { + src := toString(args[0]) + result, err := base64.StdEncoding.DecodeString(src) + if err != nil { + panic(err) + } + + return stackitem.NewByteArray(result) +} + +func (s *Std) base58Encode(_ *interop.Context, args []stackitem.Item) stackitem.Item { + src, err := args[0].TryBytes() + if err != nil { + panic(err) + } + result := base58.Encode(src) + + return stackitem.NewByteArray([]byte(result)) +} + +func (s *Std) base58Decode(_ *interop.Context, args []stackitem.Item) stackitem.Item { + src := toString(args[0]) + result, err := base58.Decode(src) + if err != nil { + panic(err) + } + + return stackitem.NewByteArray(result) +} + +// Metadata implements Contract interface. +func (s *Std) Metadata() *interop.ContractMD { + return &s.ContractMD +} + +// Initialize implements Contract interface. +func (s *Std) Initialize(ic *interop.Context) error { + return nil +} + +// OnPersist implements Contract interface. +func (s *Std) OnPersist(ic *interop.Context) error { + return nil +} + +// PostPersist implements Contract interface. +func (s *Std) PostPersist(ic *interop.Context) error { + return nil +} diff --git a/pkg/core/native/std_test.go b/pkg/core/native/std_test.go new file mode 100644 index 000000000..f5a3bf15c --- /dev/null +++ b/pkg/core/native/std_test.go @@ -0,0 +1,328 @@ +package native + +import ( + "encoding/base64" + "encoding/hex" + "math" + "math/big" + "testing" + + "github.com/mr-tron/base58" + "github.com/nspcc-dev/neo-go/pkg/core/interop" + "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/vm" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestStdLibItoaAtoi(t *testing.T) { + s := newStd() + ic := &interop.Context{VM: vm.New()} + var actual stackitem.Item + + t.Run("itoa-atoi", func(t *testing.T) { + var testCases = []struct { + num *big.Int + base *big.Int + result string + }{ + {big.NewInt(0), big.NewInt(10), "0"}, + {big.NewInt(0), big.NewInt(16), "0"}, + {big.NewInt(1), big.NewInt(10), "1"}, + {big.NewInt(-1), big.NewInt(10), "-1"}, + {big.NewInt(1), big.NewInt(16), "1"}, + {big.NewInt(7), big.NewInt(16), "7"}, + {big.NewInt(8), big.NewInt(16), "08"}, + {big.NewInt(65535), big.NewInt(16), "0FFFF"}, + {big.NewInt(15), big.NewInt(16), "0F"}, + {big.NewInt(-1), big.NewInt(16), "F"}, + } + + for _, tc := range testCases { + require.NotPanics(t, func() { + actual = s.itoa(ic, []stackitem.Item{stackitem.Make(tc.num), stackitem.Make(tc.base)}) + }) + require.Equal(t, stackitem.Make(tc.result), actual) + + require.NotPanics(t, func() { + actual = s.atoi(ic, []stackitem.Item{stackitem.Make(tc.result), stackitem.Make(tc.base)}) + }) + require.Equal(t, stackitem.Make(tc.num), actual) + } + + t.Run("-1", func(t *testing.T) { + for _, str := range []string{"FF", "FFF", "FFFF"} { + require.NotPanics(t, func() { + actual = s.atoi(ic, []stackitem.Item{stackitem.Make(str), stackitem.Make(16)}) + }) + + require.Equal(t, stackitem.Make(-1), actual) + } + }) + }) + + t.Run("itoa error", func(t *testing.T) { + var testCases = []struct { + num *big.Int + base *big.Int + err error + }{ + {big.NewInt(1), big.NewInt(13), ErrInvalidBase}, + {big.NewInt(-1), new(big.Int).Add(big.NewInt(math.MaxInt64), big.NewInt(10)), ErrInvalidBase}, + } + + for _, tc := range testCases { + require.PanicsWithError(t, tc.err.Error(), func() { + _ = s.itoa(ic, []stackitem.Item{stackitem.Make(tc.num), stackitem.Make(tc.base)}) + }) + } + }) + + t.Run("atoi error", func(t *testing.T) { + var testCases = []struct { + num string + base *big.Int + err error + }{ + {"1", big.NewInt(13), ErrInvalidBase}, + {"1", new(big.Int).Add(big.NewInt(math.MaxInt64), big.NewInt(16)), ErrInvalidBase}, + {"1_000", big.NewInt(10), ErrInvalidFormat}, + {"FE", big.NewInt(10), ErrInvalidFormat}, + {"XD", big.NewInt(16), ErrInvalidFormat}, + } + + for _, tc := range testCases { + require.PanicsWithError(t, tc.err.Error(), func() { + _ = s.atoi(ic, []stackitem.Item{stackitem.Make(tc.num), stackitem.Make(tc.base)}) + }) + } + }) +} + +func TestStdLibJSON(t *testing.T) { + s := newStd() + ic := &interop.Context{VM: vm.New()} + var actual stackitem.Item + + t.Run("JSONSerialize", func(t *testing.T) { + t.Run("Good", func(t *testing.T) { + require.NotPanics(t, func() { + actual = s.jsonSerialize(ic, []stackitem.Item{stackitem.Make(42)}) + }) + + require.Equal(t, stackitem.Make([]byte("42")), actual) + }) + + t.Run("Bad", func(t *testing.T) { + arr := stackitem.NewArray([]stackitem.Item{ + stackitem.NewByteArray(make([]byte, stackitem.MaxSize/2)), + stackitem.NewByteArray(make([]byte, stackitem.MaxSize/2)), + }) + require.Panics(t, func() { + _ = s.jsonSerialize(ic, []stackitem.Item{arr}) + }) + }) + }) + + t.Run("JSONDeserialize", func(t *testing.T) { + t.Run("Good", func(t *testing.T) { + require.NotPanics(t, func() { + actual = s.jsonDeserialize(ic, []stackitem.Item{stackitem.Make("42")}) + }) + + require.Equal(t, stackitem.Make(42), actual) + }) + t.Run("Bad", func(t *testing.T) { + require.Panics(t, func() { + _ = s.jsonDeserialize(ic, []stackitem.Item{stackitem.Make("{]")}) + }) + require.Panics(t, func() { + _ = s.jsonDeserialize(ic, []stackitem.Item{stackitem.NewInterop(nil)}) + }) + }) + }) +} + +func TestStdLibEncodeDecode(t *testing.T) { + s := newStd() + original := []byte("my pretty string") + encoded64 := base64.StdEncoding.EncodeToString(original) + encoded58 := base58.Encode(original) + ic := &interop.Context{VM: vm.New()} + var actual stackitem.Item + + t.Run("Encode64", func(t *testing.T) { + require.NotPanics(t, func() { + actual = s.base64Encode(ic, []stackitem.Item{stackitem.Make(original)}) + }) + require.Equal(t, stackitem.Make(encoded64), actual) + }) + t.Run("Encode58", func(t *testing.T) { + require.NotPanics(t, func() { + actual = s.base58Encode(ic, []stackitem.Item{stackitem.Make(original)}) + }) + require.Equal(t, stackitem.Make(encoded58), actual) + }) + t.Run("Decode64/positive", func(t *testing.T) { + require.NotPanics(t, func() { + actual = s.base64Decode(ic, []stackitem.Item{stackitem.Make(encoded64)}) + }) + require.Equal(t, stackitem.Make(original), actual) + }) + t.Run("Decode64/error", func(t *testing.T) { + require.Panics(t, func() { + _ = s.base64Decode(ic, []stackitem.Item{stackitem.Make(encoded64 + "%")}) + }) + require.Panics(t, func() { + _ = s.base64Decode(ic, []stackitem.Item{stackitem.NewInterop(nil)}) + }) + }) + t.Run("Decode58/positive", func(t *testing.T) { + require.NotPanics(t, func() { + actual = s.base58Decode(ic, []stackitem.Item{stackitem.Make(encoded58)}) + }) + require.Equal(t, stackitem.Make(original), actual) + }) + t.Run("Decode58/error", func(t *testing.T) { + require.Panics(t, func() { + _ = s.base58Decode(ic, []stackitem.Item{stackitem.Make(encoded58 + "%")}) + }) + require.Panics(t, func() { + _ = s.base58Decode(ic, []stackitem.Item{stackitem.NewInterop(nil)}) + }) + }) +} + +func TestStdLibSerialize(t *testing.T) { + s := newStd() + ic := &interop.Context{VM: vm.New()} + + t.Run("recursive", func(t *testing.T) { + arr := stackitem.NewArray(nil) + arr.Append(arr) + require.Panics(t, func() { + _ = s.serialize(ic, []stackitem.Item{arr}) + }) + }) + t.Run("big item", func(t *testing.T) { + require.Panics(t, func() { + _ = s.serialize(ic, []stackitem.Item{stackitem.NewByteArray(make([]byte, stackitem.MaxSize))}) + }) + }) + t.Run("good", func(t *testing.T) { + var ( + actualSerialized stackitem.Item + actualDeserialized stackitem.Item + ) + require.NotPanics(t, func() { + actualSerialized = s.serialize(ic, []stackitem.Item{stackitem.Make(42)}) + }) + + w := io.NewBufBinWriter() + stackitem.EncodeBinaryStackItem(stackitem.Make(42), w.BinWriter) + require.NoError(t, w.Err) + + encoded := w.Bytes() + require.Equal(t, stackitem.Make(encoded), actualSerialized) + + require.NotPanics(t, func() { + actualDeserialized = s.deserialize(ic, []stackitem.Item{actualSerialized}) + }) + require.Equal(t, stackitem.Make(42), actualDeserialized) + + t.Run("bad", func(t *testing.T) { + encoded[0] ^= 0xFF + require.Panics(t, func() { + _ = s.deserialize(ic, []stackitem.Item{stackitem.Make(encoded)}) + }) + }) + }) +} + +func TestStdLibSerializeDeserialize(t *testing.T) { + s := newStd() + ic := &interop.Context{VM: vm.New()} + var actual stackitem.Item + + checkSerializeDeserialize := func(t *testing.T, value interface{}, expected stackitem.Item) { + require.NotPanics(t, func() { + actual = s.serialize(ic, []stackitem.Item{stackitem.Make(value)}) + }) + require.NotPanics(t, func() { + actual = s.deserialize(ic, []stackitem.Item{actual}) + }) + require.Equal(t, expected, actual) + } + + t.Run("Bool", func(t *testing.T) { + checkSerializeDeserialize(t, true, stackitem.NewBool(true)) + }) + t.Run("ByteArray", func(t *testing.T) { + checkSerializeDeserialize(t, []byte{1, 2, 3}, stackitem.NewByteArray([]byte{1, 2, 3})) + }) + t.Run("Integer", func(t *testing.T) { + checkSerializeDeserialize(t, 48, stackitem.NewBigInteger(big.NewInt(48))) + }) + t.Run("Array", func(t *testing.T) { + arr := stackitem.NewArray([]stackitem.Item{ + stackitem.Make(true), + stackitem.Make(123), + stackitem.NewMap()}) + checkSerializeDeserialize(t, arr, arr) + }) + t.Run("Struct", func(t *testing.T) { + st := stackitem.NewStruct([]stackitem.Item{ + stackitem.Make(true), + stackitem.Make(123), + stackitem.NewMap(), + }) + checkSerializeDeserialize(t, st, st) + }) + t.Run("Map", func(t *testing.T) { + item := stackitem.NewMap() + item.Add(stackitem.Make(true), stackitem.Make([]byte{1, 2, 3})) + item.Add(stackitem.Make([]byte{0}), stackitem.Make(false)) + checkSerializeDeserialize(t, item, item) + }) + t.Run("Serialize MapCompat", func(t *testing.T) { + resHex := "480128036b6579280576616c7565" + res, err := hex.DecodeString(resHex) + require.NoError(t, err) + + item := stackitem.NewMap() + item.Add(stackitem.Make([]byte("key")), stackitem.Make([]byte("value"))) + require.NotPanics(t, func() { + actual = s.serialize(ic, []stackitem.Item{stackitem.Make(item)}) + }) + bytes, err := actual.TryBytes() + require.NoError(t, err) + assert.Equal(t, res, bytes) + }) + t.Run("Serialize Interop", func(t *testing.T) { + require.Panics(t, func() { + actual = s.serialize(ic, []stackitem.Item{stackitem.NewInterop("kek")}) + }) + }) + t.Run("Serialize Array bad", func(t *testing.T) { + item := stackitem.NewArray([]stackitem.Item{stackitem.NewBool(true), stackitem.NewBool(true)}) + item.Value().([]stackitem.Item)[1] = item + require.Panics(t, func() { + actual = s.serialize(ic, []stackitem.Item{item}) + }) + }) + t.Run("Deserialize unknown", func(t *testing.T) { + data, err := stackitem.SerializeItem(stackitem.NewBigInteger(big.NewInt(123))) + require.NoError(t, err) + + data[0] = 0xFF + require.Panics(t, func() { + actual = s.deserialize(ic, []stackitem.Item{stackitem.Make(data)}) + }) + }) + t.Run("Deserialize not a byte array", func(t *testing.T) { + require.Panics(t, func() { + actual = s.deserialize(ic, []stackitem.Item{stackitem.NewInterop(nil)}) + }) + }) +} diff --git a/pkg/core/native_oracle_test.go b/pkg/core/native_oracle_test.go index 16f792c0c..2f692f705 100644 --- a/pkg/core/native_oracle_test.go +++ b/pkg/core/native_oracle_test.go @@ -29,7 +29,7 @@ import ( ) // getTestContractState returns test contract which uses oracles. -func getOracleContractState(h util.Uint160) *state.Contract { +func getOracleContractState(h util.Uint160, stdHash util.Uint160) *state.Contract { w := io.NewBufBinWriter() emit.Int(w.BinWriter, 5) emit.Opcodes(w.BinWriter, opcode.PACK) @@ -49,7 +49,9 @@ func getOracleContractState(h util.Uint160) *state.Contract { emit.Opcodes(w.BinWriter, opcode.ABORT) emit.Int(w.BinWriter, 4) // url, userData, code, result emit.Opcodes(w.BinWriter, opcode.PACK) - emit.Syscall(w.BinWriter, interopnames.SystemBinarySerialize) + emit.Int(w.BinWriter, 1) // 1 byte (args count for `serialize`) + emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte (pack args into array for `serialize`) + emit.AppCallNoArgs(w.BinWriter, stdHash, "serialize", callflag.All) // 39 bytes emit.String(w.BinWriter, "lastOracleResponse") emit.Syscall(w.BinWriter, interopnames.SystemStorageGetContext) emit.Syscall(w.BinWriter, interopnames.SystemStoragePut) @@ -117,7 +119,7 @@ func TestOracle_Request(t *testing.T) { bc := newTestChain(t) orc := bc.contracts.Oracle - cs := getOracleContractState(orc.Hash) + cs := getOracleContractState(orc.Hash, bc.contracts.Std.Hash) require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs)) gasForResponse := int64(2000_1234) diff --git a/pkg/core/oracle_test.go b/pkg/core/oracle_test.go index 6cb63922c..332a5a338 100644 --- a/pkg/core/oracle_test.go +++ b/pkg/core/oracle_test.go @@ -97,9 +97,9 @@ func TestCreateResponseTx(t *testing.T) { bc.SetOracle(orc) tx, err := orc.CreateResponseTx(int64(req.GasForResponse), 1, resp) require.NoError(t, err) - assert.Equal(t, 167, tx.Size()) - assert.Equal(t, int64(2216640), tx.NetworkFee) - assert.Equal(t, int64(97783360), tx.SystemFee) + assert.Equal(t, 166, tx.Size()) + assert.Equal(t, int64(2215610), tx.NetworkFee) + assert.Equal(t, int64(97784390), tx.SystemFee) } func TestOracle_InvalidWallet(t *testing.T) { @@ -130,7 +130,7 @@ func TestOracle(t *testing.T) { orc1.UpdateNativeContract(orcNative.NEF.Script, orcNative.GetOracleResponseScript(), orcNative.Hash, md.MD.Offset) orc2.UpdateNativeContract(orcNative.NEF.Script, orcNative.GetOracleResponseScript(), orcNative.Hash, md.MD.Offset) - cs := getOracleContractState(bc.contracts.Oracle.Hash) + cs := getOracleContractState(bc.contracts.Oracle.Hash, bc.contracts.Std.Hash) require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs)) putOracleRequest(t, cs.Hash, bc, "http://get.1234", nil, "handle", []byte{}, 10_000_000) @@ -271,7 +271,7 @@ func TestOracleFull(t *testing.T) { orc.OnTransaction = func(tx *transaction.Transaction) { _ = mp.Add(tx, bc) } bc.SetOracle(orc) - cs := getOracleContractState(bc.contracts.Oracle.Hash) + cs := getOracleContractState(bc.contracts.Oracle.Hash, bc.contracts.Std.Hash) require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs)) go bc.Run() diff --git a/pkg/core/util_test.go b/pkg/core/util_test.go index 8772038ef..4cd46e6ca 100644 --- a/pkg/core/util_test.go +++ b/pkg/core/util_test.go @@ -17,14 +17,14 @@ func TestGenesisBlockMainNet(t *testing.T) { block, err := createGenesisBlock(cfg.ProtocolConfiguration) require.NoError(t, err) - expect := "d71dfebcc59d42b2f3b3f0e0d6b3b77a4880276db1df92c08c7c1bac94bece35" + expect := "de3bfe3e328af04d48f62bd7a9c533641cc0e1fb6a7741c5119d6a6eaedc5269" assert.Equal(t, expect, block.Hash().StringLE()) } func TestGetConsensusAddressMainNet(t *testing.T) { var ( - consensusAddr = "NiVihDFvZacZhujTWkBhRz32UDuNRp416f" - consensusScript = "f7b4d00143932f3b6243cfc06cb4a68f22c739e2" + consensusAddr = "NSX179gdoQmF8nu34rQdL4dYAfdCQhHtQS" + consensusScript = "4870eaa62eee7c76b76d2ae933d4c027f5f5c77d" ) cfg, err := config.Load("../../config", netmode.MainNet) diff --git a/pkg/crypto/keys/publickey.go b/pkg/crypto/keys/publickey.go index eb941fd88..c5ae4de3f 100644 --- a/pkg/crypto/keys/publickey.go +++ b/pkg/crypto/keys/publickey.go @@ -18,7 +18,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" - "github.com/nspcc-dev/neo-go/pkg/vm/opcode" ) // coordLen is the number of bytes in serialized X or Y coordinate. @@ -317,8 +316,7 @@ func (p *PublicKey) GetVerificationScript() []byte { return buf.Bytes() } emit.Bytes(buf.BinWriter, b) - emit.Opcodes(buf.BinWriter, opcode.PUSHNULL) - emit.Syscall(buf.BinWriter, interopnames.NeoCryptoVerifyWithECDsaSecp256r1) + emit.Syscall(buf.BinWriter, interopnames.NeoCryptoCheckSig) return buf.Bytes() } diff --git a/pkg/crypto/keys/publickey_test.go b/pkg/crypto/keys/publickey_test.go index 06c704232..fc5c32967 100644 --- a/pkg/crypto/keys/publickey_test.go +++ b/pkg/crypto/keys/publickey_test.go @@ -111,7 +111,7 @@ func TestPubkeyToAddress(t *testing.T) { pubKey, err := NewPublicKeyFromString("031ee4e73a17d8f76dc02532e2620bcb12425b33c0c9f9694cc2caa8226b68cad4") require.NoError(t, err) actual := pubKey.Address() - expected := "NcKJdJTEDeCSV9BJAKWWxkBMcHTeVnSzJo" + expected := "NWmVWWvFA6RxvTs4kgY1NnwTXeLYwWxPb1" require.Equal(t, expected, actual) } diff --git a/pkg/interop/binary/binary.go b/pkg/interop/binary/binary.go deleted file mode 100644 index 3cf244e22..000000000 --- a/pkg/interop/binary/binary.go +++ /dev/null @@ -1,58 +0,0 @@ -/* -Package binary provides binary serialization routines. -*/ -package binary - -import ( - "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" -) - -// Serialize serializes any given item into a byte slice. It works for all -// regular VM types (not ones from interop package) and allows to save them in -// storage or pass into Notify and then Deserialize them on the next run or in -// the external event receiver. It uses `System.Binary.Serialize` syscall. -func Serialize(item interface{}) []byte { - return neogointernal.Syscall1("System.Binary.Serialize", item).([]byte) -} - -// Deserialize unpacks previously serialized value from a byte slice, it's the -// opposite of Serialize. It uses `System.Binary.Deserialize` syscall. -func Deserialize(b []byte) interface{} { - return neogointernal.Syscall1("System.Binary.Deserialize", b) -} - -// Base64Encode encodes given byte slice into a base64 string and returns byte -// representation of this string. It uses `System.Binary.Base64Encode` interop. -func Base64Encode(b []byte) string { - return neogointernal.Syscall1("System.Binary.Base64Encode", b).(string) -} - -// Base64Decode decodes given base64 string represented as a byte slice into -// byte slice. It uses `System.Binary.Base64Decode` interop. -func Base64Decode(b []byte) []byte { - return neogointernal.Syscall1("System.Binary.Base64Decode", b).([]byte) -} - -// Base58Encode encodes given byte slice into a base58 string and returns byte -// representation of this string. It uses `System.Binary.Base58Encode` syscall. -func Base58Encode(b []byte) string { - return neogointernal.Syscall1("System.Binary.Base58Encode", b).(string) -} - -// Base58Decode decodes given base58 string represented as a byte slice into -// a new byte slice. It uses `System.Binary.Base58Decode` syscall. -func Base58Decode(b []byte) []byte { - return neogointernal.Syscall1("System.Binary.Base58Decode", b).([]byte) -} - -// Itoa converts num in a given base to string. Base should be either 10 or 16. -// It uses `System.Binary.Itoa` syscall. -func Itoa(num int, base int) string { - return neogointernal.Syscall2("System.Binary.Itoa", num, base).(string) -} - -// Atoi converts string to a number in a given base. Base should be either 10 or 16. -// It uses `System.Binary.Atoi` syscall. -func Atoi(s string, base int) int { - return neogointernal.Syscall2("System.Binary.Atoi", s, base).(int) -} diff --git a/pkg/interop/crypto/crypto.go b/pkg/interop/crypto/crypto.go index 28702d7e9..726a3e2cf 100644 --- a/pkg/interop/crypto/crypto.go +++ b/pkg/interop/crypto/crypto.go @@ -8,36 +8,15 @@ import ( "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" ) -// SHA256 computes SHA256 hash of b. It uses `Neo.Crypto.SHA256` syscall. -func SHA256(b []byte) interop.Hash256 { - return neogointernal.Syscall1("Neo.Crypto.SHA256", b).(interop.Hash256) +// CheckMultisig checks that script container (transaction) is signed by multiple +// ECDSA keys at once. It uses `Neo.Crypto.CheckMultisig` syscall. +func CheckMultisig(pubs []interop.PublicKey, sigs []interop.Signature) bool { + return neogointernal.Syscall2("Neo.Crypto.CheckMultisig", pubs, sigs).(bool) } -// RIPEMD160 computes RIPEMD160 hash of b. It uses `Neo.Crypto.RIPEMD160` syscall. -func RIPEMD160(b []byte) interop.Hash160 { - return neogointernal.Syscall1("Neo.Crypto.RIPEMD160", b).(interop.Hash160) -} - -// ECDsaSecp256r1Verify checks that sig is correct msg's signature for a given pub -// (serialized public key). It uses `Neo.Crypto.VerifyWithECDsaSecp256r1` syscall. -func ECDsaSecp256r1Verify(msg []byte, pub interop.PublicKey, sig interop.Signature) bool { - return neogointernal.Syscall3("Neo.Crypto.VerifyWithECDsaSecp256r1", msg, pub, sig).(bool) -} - -// ECDsaSecp256k1Verify checks that sig is correct msg's signature for a given pub -// (serialized public key). It uses `Neo.Crypto.VerifyWithECDsaSecp256k1` syscall. -func ECDsaSecp256k1Verify(msg []byte, pub interop.PublicKey, sig interop.Signature) bool { - return neogointernal.Syscall3("Neo.Crypto.VerifyWithECDsaSecp256k1", msg, pub, sig).(bool) -} - -// ECDSASecp256r1CheckMultisig checks multiple ECDSA signatures at once. It uses -// `Neo.Crypto.CheckMultisigWithECDsaSecp256r1` syscall. -func ECDSASecp256r1CheckMultisig(msg []byte, pubs []interop.PublicKey, sigs []interop.Signature) bool { - return neogointernal.Syscall3("Neo.Crypto.CheckMultisigWithECDsaSecp256r1", msg, pubs, sigs).(bool) -} - -// ECDSASecp256k1CheckMultisig checks multiple ECDSA signatures at once. It uses -// `Neo.Crypto.CheckMultisigWithECDsaSecp256k1` syscall. -func ECDSASecp256k1CheckMultisig(msg []byte, pubs []interop.PublicKey, sigs []interop.Signature) bool { - return neogointernal.Syscall3("Neo.Crypto.CheckMultisigWithECDsaSecp256k1", msg, pubs, sigs).(bool) +// CheckSig checks that sig is correct signature of the script container +// (transaction) for a given pub (serialized public key). It uses +// `Neo.Crypto.CheckSig` syscall. +func CheckSig(pub interop.PublicKey, sig interop.Signature) bool { + return neogointernal.Syscall2("Neo.Crypto.CheckSig", pub, sig).(bool) } diff --git a/pkg/interop/json/json.go b/pkg/interop/json/json.go deleted file mode 100644 index 05eede334..000000000 --- a/pkg/interop/json/json.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -Package json provides various JSON serialization/deserialization routines. -*/ -package json - -import "github.com/nspcc-dev/neo-go/pkg/interop/neogointernal" - -// ToJSON serializes value to json. It uses `System.Json.Serialize` syscall. -// Serialization format is the following: -// []byte -> base64 string -// bool -> json boolean -// nil -> Null -// string -> base64 encoded sequence of underlying bytes -// (u)int* -> integer, only value in -2^53..2^53 are allowed -// []interface{} -> json array -// map[type1]type2 -> json object with string keys marshaled as strings (not base64). -func ToJSON(item interface{}) []byte { - return neogointernal.Syscall1("System.Json.Serialize", item).([]byte) -} - -// FromJSON deserializes value from json. It uses `System.Json.Deserialize` syscall. -// It performs deserialization as follows: -// strings -> []byte (string) from base64 -// integers -> (u)int* types -// null -> interface{}(nil) -// arrays -> []interface{} -// maps -> map[string]interface{} -func FromJSON(data []byte) interface{} { - return neogointernal.Syscall1("System.Json.Deserialize", data).(interface{}) -} diff --git a/pkg/interop/native/crypto/crypto.go b/pkg/interop/native/crypto/crypto.go new file mode 100644 index 000000000..32684d551 --- /dev/null +++ b/pkg/interop/native/crypto/crypto.go @@ -0,0 +1,34 @@ +package crypto + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/contract" +) + +// Hash represents CryptoLib contract hash. +const Hash = "\x1b\xf5\x75\xab\x11\x89\x68\x84\x13\x61\x0a\x35\xa1\x28\x86\xcd\xe0\xb6\x6c\x72" + +// NamedCurve represents named elliptic curve. +type NamedCurve byte + +// Various named elliptic curves. +const ( + Secp256k1 NamedCurve = 22 + Secp256r1 NamedCurve = 23 +) + +// Sha256 calls `sha256` method of native CryptoLib contract and computes SHA256 hash of b. +func Sha256(b []byte) interop.Hash256 { + return contract.Call(interop.Hash160(Hash), "sha256", contract.NoneFlag, b).(interop.Hash256) +} + +// Ripemd160 calls `ripemd160` method of native CryptoLib contract and computes RIPEMD160 hash of b. +func Ripemd160(b []byte) interop.Hash160 { + return contract.Call(interop.Hash160(Hash), "ripemd160", contract.NoneFlag, b).(interop.Hash160) +} + +// VerifyWithECDsa calls `verifyWithECDsa` method of native CryptoLib contract and checks that sig is +// correct msg's signature for a given pub (serialized public key on a given curve). +func VerifyWithECDsa(msg []byte, pub interop.PublicKey, sig interop.Signature, curve NamedCurve) bool { + return contract.Call(interop.Hash160(Hash), "verifyWithECDsa", contract.NoneFlag, msg, pub, sig, curve).(bool) +} diff --git a/pkg/interop/native/std/std.go b/pkg/interop/native/std/std.go new file mode 100644 index 000000000..3fcc5332a --- /dev/null +++ b/pkg/interop/native/std/std.go @@ -0,0 +1,97 @@ +package std + +import ( + "github.com/nspcc-dev/neo-go/pkg/interop" + "github.com/nspcc-dev/neo-go/pkg/interop/contract" +) + +// Hash represents StdLib contract hash. +const Hash = "\xc0\xef\x39\xce\xe0\xe4\xe9\x25\xc6\xc2\xa0\x6a\x79\xe1\x44\x0d\xd8\x6f\xce\xac" + +// Serialize calls `serialize` method of StdLib native contract and serializes +// any given item into a byte slice. It works for all regular VM types (not ones +// from interop package) and allows to save them in storage or pass into Notify +// and then Deserialize them on the next run or in the external event receiver. +func Serialize(item interface{}) []byte { + return contract.Call(interop.Hash160(Hash), "serialize", contract.NoneFlag, + item).([]byte) +} + +// Deserialize calls `deserialize` method of StdLib native contract and unpacks +// previously serialized value from a byte slice, it's the opposite of Serialize. +func Deserialize(b []byte) interface{} { + return contract.Call(interop.Hash160(Hash), "deserialize", contract.NoneFlag, + b) +} + +// JSONSerialize serializes value to json. It uses `jsonSerialize` method of StdLib native +// contract. +// Serialization format is the following: +// []byte -> base64 string +// bool -> json boolean +// nil -> Null +// string -> base64 encoded sequence of underlying bytes +// (u)int* -> integer, only value in -2^53..2^53 are allowed +// []interface{} -> json array +// map[type1]type2 -> json object with string keys marshaled as strings (not base64). +func JSONSerialize(item interface{}) []byte { + return contract.Call(interop.Hash160(Hash), "jsonSerialize", contract.NoneFlag, + item).([]byte) +} + +// JSONDeserialize deserializes value from json. It uses `jsonDeserialize` method of StdLib +// native contract. +// It performs deserialization as follows: +// strings -> []byte (string) from base64 +// integers -> (u)int* types +// null -> interface{}(nil) +// arrays -> []interface{} +// maps -> map[string]interface{} +func JSONDeserialize(data []byte) interface{} { + return contract.Call(interop.Hash160(Hash), "jsonDeserialize", contract.NoneFlag, + data) +} + +// Base64Encode calls `base64Encode` method of StdLib native contract and encodes +// given byte slice into a base64 string and returns byte representation of this +// string. +func Base64Encode(b []byte) string { + return contract.Call(interop.Hash160(Hash), "base64Encode", contract.NoneFlag, + b).(string) +} + +// Base64Decode calls `base64Decode` method of StdLib native contract and decodes +// given base64 string represented as a byte slice into byte slice. +func Base64Decode(b []byte) []byte { + return contract.Call(interop.Hash160(Hash), "base64Decode", contract.NoneFlag, + b).([]byte) +} + +// Base58Encode calls `base58Encode` method of StdLib native contract and encodes +// given byte slice into a base58 string and returns byte representation of this +// string. +func Base58Encode(b []byte) string { + return contract.Call(interop.Hash160(Hash), "base58Encode", contract.NoneFlag, + b).(string) +} + +// Base58Decode calls `base58Decode` method of StdLib native contract and decodes +// given base58 string represented as a byte slice into a new byte slice. +func Base58Decode(b []byte) []byte { + return contract.Call(interop.Hash160(Hash), "base58Decode", contract.NoneFlag, + b).([]byte) +} + +// Itoa converts num in a given base to string. Base should be either 10 or 16. +// It uses `itoa` method of StdLib native contract. +func Itoa(num int, base int) string { + return contract.Call(interop.Hash160(Hash), "itoa", contract.NoneFlag, + num, base).(string) +} + +// Atoi converts string to a number in a given base. Base should be either 10 or 16. +// It uses `atoi` method of StdLib native contract. +func Atoi(s string, base int) int { + return contract.Call(interop.Hash160(Hash), "atoi", contract.NoneFlag, + s, base).(int) +} diff --git a/pkg/rpc/client/rpc_test.go b/pkg/rpc/client/rpc_test.go index f4142dd2f..9a4243389 100644 --- a/pkg/rpc/client/rpc_test.go +++ b/pkg/rpc/client/rpc_test.go @@ -1113,7 +1113,7 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ name: "getnep17transfers_invalid_params_error 2", invoke: func(c *Client) (interface{}, error) { var stop uint32 - return c.GetNEP17Transfers("NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc", nil, &stop, nil, nil) + return c.GetNEP17Transfers("NTh9TnZTstvAePEYWDGLLxidBikJE24uTo", nil, &stop, nil, nil) }, }, { @@ -1121,7 +1121,7 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ invoke: func(c *Client) (interface{}, error) { var start uint32 var limit int - return c.GetNEP17Transfers("NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc", &start, nil, &limit, nil) + return c.GetNEP17Transfers("NTh9TnZTstvAePEYWDGLLxidBikJE24uTo", &start, nil, &limit, nil) }, }, { @@ -1129,7 +1129,7 @@ var rpcClientErrorCases = map[string][]rpcClientErrorCase{ invoke: func(c *Client) (interface{}, error) { var start, stop uint32 var page int - return c.GetNEP17Transfers("NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc", &start, &stop, nil, &page) + return c.GetNEP17Transfers("NTh9TnZTstvAePEYWDGLLxidBikJE24uTo", &start, &stop, nil, &page) }, }, { diff --git a/pkg/rpc/server/client_test.go b/pkg/rpc/server/client_test.go index db4fd154d..87932f0f6 100644 --- a/pkg/rpc/server/client_test.go +++ b/pkg/rpc/server/client_test.go @@ -2,7 +2,7 @@ package server import ( "context" - "encoding/hex" + "encoding/base64" "testing" "github.com/nspcc-dev/neo-go/internal/testchain" @@ -139,8 +139,10 @@ func TestAddNetworkFee(t *testing.T) { priv := testchain.PrivateKeyByID(0) acc1 := wallet.NewAccountFromPrivateKey(priv) acc1.Contract.Deployed = true - acc1.Contract.Script, _ = hex.DecodeString(verifyContractAVM) - h, _ := util.Uint160DecodeStringLE(verifyContractHash) + acc1.Contract.Script, err = base64.StdEncoding.DecodeString(verifyContractAVM) + require.NoError(t, err) + h, err := util.Uint160DecodeStringLE(verifyContractHash) + require.NoError(t, err) tx.ValidUntilBlock = chain.BlockHeight() + 10 t.Run("Valid", func(t *testing.T) { diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 84a9a0269..2a5c18335 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -59,13 +59,13 @@ type rpcTestCase struct { check func(t *testing.T, e *executor, result interface{}) } -const testContractHash = "500858b96054d3c302078882c30e76915aac1c83" -const deploymentTxHash = "e5503038c2cd602c100690e266b75e2a9d3dda90a63791fd5ab3008ff053eaae" -const genesisBlockHash = "9e7cf6fcfc8d0d6831fac75fa895535a5f1960f45a34754b57bff4d4929635c5" +const testContractHash = "1e1c3024bd955ff3baf7cb92e3b7608c7bb3712b" +const deploymentTxHash = "7cf43b182dee2e8bd2c5209cd230799aeba1b5b13000db682d917c89eacd1eae" +const genesisBlockHash = "d237e3500d8b4cf0df3fd9c4c053016afae141207a6c732303bdd91aff444ecc" -const verifyContractHash = "03ffc0897543b9b709e0f8cab4a7682dae0ba943" -const verifyContractAVM = "570300412d51083021700c14aa8acf859d4fe402b34e673f2156821796a488ebdb30716813cedb2869db289740" -const testVerifyContractAVM = "VwcADBQBDAMOBQYMDQIODw0DDgcJAAAAANswcGgRVUH4J+yMIaonBwAAABFADBQNDwMCCQACAQMHAwQFAgEADgYMCdswcWkRVUH4J+yMIaonBwAAABJAE0A=" +const verifyContractHash = "5bb4bac40e961e334ba7bd36d2496010f67e246e" +const verifyContractAVM = "VwMAQS1RCDAhcAwUVVQtU+0PVUb61E1umZEoZwIvzl7bMHFoE87bKGnbKJdA" +const invokescriptContractAVM = "VwcADBQBDAMOBQYMDQIODw0DDgcJAAAAANswcGhB+CfsjCGqJgQRQAwUDQ8DAgkAAgEDBwMEBQIBAA4GDAnbMHFpQfgn7IwhqiYEEkATQA==" var rpcTestCases = map[string][]rpcTestCase{ "getapplicationlog": { @@ -183,7 +183,7 @@ var rpcTestCases = map[string][]rpcTestCase{ check: func(t *testing.T, e *executor, cs interface{}) { res, ok := cs.(*state.Contract) require.True(t, ok) - assert.Equal(t, int32(-5), res.ID) + assert.Equal(t, int32(-7), res.ID) }, }, { @@ -743,8 +743,8 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "positive, good witness", - // script is base64-encoded `test_verify.avm` representation, hashes are hex-encoded LE bytes of hashes used in the contract with `0x` prefix - params: fmt.Sprintf(`["%s",["0x0000000009070e030d0f0e020d0c06050e030c01","0x090c060e00010205040307030102000902030f0d"]]`, testVerifyContractAVM), + // script is base64-encoded `invokescript_contract.avm` representation, hashes are hex-encoded LE bytes of hashes used in the contract with `0x` prefix + params: fmt.Sprintf(`["%s",["0x0000000009070e030d0f0e020d0c06050e030c01","0x090c060e00010205040307030102000902030f0d"]]`, invokescriptContractAVM), result: func(e *executor) interface{} { return &result.Invoke{} }, check: func(t *testing.T, e *executor, inv interface{}) { res, ok := inv.(*result.Invoke) @@ -756,7 +756,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "positive, bad witness of second hash", - params: fmt.Sprintf(`["%s",["0x0000000009070e030d0f0e020d0c06050e030c01"]]`, testVerifyContractAVM), + params: fmt.Sprintf(`["%s",["0x0000000009070e030d0f0e020d0c06050e030c01"]]`, invokescriptContractAVM), result: func(e *executor) interface{} { return &result.Invoke{} }, check: func(t *testing.T, e *executor, inv interface{}) { res, ok := inv.(*result.Invoke) @@ -768,7 +768,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "positive, no good hashes", - params: fmt.Sprintf(`["%s"]`, testVerifyContractAVM), + params: fmt.Sprintf(`["%s"]`, invokescriptContractAVM), result: func(e *executor) interface{} { return &result.Invoke{} }, check: func(t *testing.T, e *executor, inv interface{}) { res, ok := inv.(*result.Invoke) @@ -780,7 +780,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { name: "positive, bad hashes witness", - params: fmt.Sprintf(`["%s",["0x0000000009070e030d0f0e020d0c06050e030c02"]]`, testVerifyContractAVM), + params: fmt.Sprintf(`["%s",["0x0000000009070e030d0f0e020d0c06050e030c02"]]`, invokescriptContractAVM), result: func(e *executor) interface{} { return &result.Invoke{} }, check: func(t *testing.T, e *executor, inv interface{}) { res, ok := inv.(*result.Invoke) @@ -817,7 +817,7 @@ var rpcTestCases = map[string][]rpcTestCase{ assert.NotNil(t, res.Script) assert.Equal(t, "HALT", res.State) assert.NotEqual(t, 0, res.GasConsumed) - assert.Equal(t, true, res.Stack[0].Value().(bool)) + assert.Equal(t, true, res.Stack[0].Value().(bool), fmt.Sprintf("check address in verification_contract.go: expected %s", testchain.PrivateKeyByID(0).Address())) }, }, { @@ -865,12 +865,12 @@ var rpcTestCases = map[string][]rpcTestCase{ "sendrawtransaction": { { name: "positive", - params: `["AAsAAACAlpgAAAAAACYcEwAAAAAAsAQAAAGqis+FnU/kArNOZz8hVoIXlqSI6wEAXQMA6HZIFwAAAAwUeLpMJACf5RDhNsmZWi4FIV4b5NwMFKqKz4WdT+QCs05nPyFWgheWpIjrE8AMCHRyYW5zZmVyDBQlBZ7LSHjTqHX5HFHO3tMw1Fdf3kFifVtSOAFCDEDqL1as9/ZGKdySLWWmAXbzljr9S3wlnyAXo6UTk0b46lRwRiRZCDKst3lAaaspg93IYrA7ajPUQozUxFy8CUHCKQwhArNiK/QBe9/jF8WK7V9MdT8ga324lgRvp9d0u8S/f43CC0GVRA14"]`, + params: `["ADQSAADA2KcAAAAAABDiEgAAAAAAgBYAAAFVVC1T7Q9VRvrUTW6ZkShnAi/OXgEAYBDAAwDodkgXAAAADBRdSe/t0S4+BgGLRljbEKiXX8gLTgwUVVQtU+0PVUb61E1umZEoZwIvzl4UwB8MCHRyYW5zZmVyDBT1Y+pAvCg9TQ4FxI6jBbPyoHNA70FifVtSOQFCDEAppqgOf7RZvrS+uOVzVNlcQAQnyujtzHzv9/Za+FFkxWFd8mZ6AvWnFXAL0W5NafW4xyP7Kp/qgWCmZrHINaLkKAwhArNiK/QBe9/jF8WK7V9MdT8ga324lgRvp9d0u8S/f43CQXR0dqo="]`, result: func(e *executor) interface{} { return &result.RelayResult{} }, check: func(t *testing.T, e *executor, inv interface{}) { res, ok := inv.(*result.RelayResult) require.True(t, ok) - expectedHash, err := util.Uint256DecodeStringLE("ab5573cfc8d70774f04aa7d5521350cfc1aa1239c44c24e490e139408cd46a57") + expectedHash, err := util.Uint256DecodeStringLE("3b133d0c2912da4f99680ae3a5f0e178bc761f2c360662a1fabbe1a8dbe309ea") require.NoError(t, err) assert.Equal(t, expectedHash, res.Hash) }, @@ -1598,7 +1598,7 @@ func checkNep17Balances(t *testing.T, e *executor, acc interface{}) { }, { Asset: e.chain.UtilityTokenHash(), - Amount: "78994294100", + Amount: "78994302340", LastUpdated: 8, }}, Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), diff --git a/pkg/rpc/server/testdata/test_verify.go b/pkg/rpc/server/testdata/invokescript_contract.go similarity index 100% rename from pkg/rpc/server/testdata/test_verify.go rename to pkg/rpc/server/testdata/invokescript_contract.go diff --git a/pkg/rpc/server/testdata/test_verify.avm b/pkg/rpc/server/testdata/test_verify.avm deleted file mode 100755 index 100b20621..000000000 Binary files a/pkg/rpc/server/testdata/test_verify.avm and /dev/null differ diff --git a/pkg/rpc/server/testdata/testblocks.acc b/pkg/rpc/server/testdata/testblocks.acc index 3bd6b5394..f6ff51a72 100644 Binary files a/pkg/rpc/server/testdata/testblocks.acc and b/pkg/rpc/server/testdata/testblocks.acc differ diff --git a/pkg/rpc/server/testdata/verification_contract.go b/pkg/rpc/server/testdata/verification_contract.go index 03e9bb480..5d8428ad1 100644 --- a/pkg/rpc/server/testdata/verification_contract.go +++ b/pkg/rpc/server/testdata/verification_contract.go @@ -6,9 +6,9 @@ import ( ) // Verify is a verification contract method. -// It returns true iff it is signed by NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc (id-0 private key from testchain). +// It returns true iff it is signed by NTh9TnZTstvAePEYWDGLLxidBikJE24uTo (id-0 private key from testchain). func Verify() bool { tx := runtime.GetScriptContainer() - addr := util.FromAddress("NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc") + addr := util.FromAddress("NTh9TnZTstvAePEYWDGLLxidBikJE24uTo") return util.Equals(string(tx.Sender), string(addr)) } diff --git a/pkg/services/notary/testdata/notary1.json b/pkg/services/notary/testdata/notary1.json index 0a8d45251..055296144 100644 --- a/pkg/services/notary/testdata/notary1.json +++ b/pkg/services/notary/testdata/notary1.json @@ -1 +1,65 @@ -{"version":"3.0","accounts":[{"address":"NSbjd7dSePTZ6QpADAuM5722QpBmL5124W","key":"6PYVWTfkNCYvyQhyFLHH5dyRyT6jSi8u8Z8kn122PACfsDWi4QgkGm8FyW","label":"NotaryNode1","contract":{"script":"DCEDm5PmbOfVPmYXTSVW903XnOhhNBTsF9oDlVYusIH/ui0LQZVEDXg=","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"lock":false,"isdefault":false},{"address":"NisSvmSd2Lp28tjr8EqCZB5ahHDvBExo2j","key":"6PYLvgnZNwhiiZPiSCw3B3bHSFwbSXgh3MkGt4gL69MD8Sw7LMnuUgM9KQ","label":"three","contract":{"script":"DCEDHRWEIGXHCwUU2Fc7B0qrYPezXR0sfdEduRExyzIKVC8LQZVEDXg=","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"lock":false,"isdefault":false},{"address":"NRCCdGifyUWKnFotZhcgKpmxhhVJSJb94r","key":"6PYKXkuJ7G6bTj62bjy8fsBLF5okYNdAEBhKPCv8nmcALCtk2yPtBo835p","label":"four","contract":{"script":"DCECmUfs/gqKHd3AdJm5+Ev6zkubV8pP8DZzgu8+t5WdphILQZVEDXg=","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"lock":false,"isdefault":false}],"scrypt":{"n":16384,"r":8,"p":8},"extra":{"Tokens":null}} +{ + "scrypt" : { + "n" : 16384, + "r" : 8, + "p" : 8 + }, + "accounts" : [ + { + "contract" : { + "parameters" : [ + { + "type" : "Signature", + "name" : "parameter0" + } + ], + "deployed" : false, + "script" : "DCEDm5PmbOfVPmYXTSVW903XnOhhNBTsF9oDlVYusIH/ui1BdHR2qg==" + }, + "label" : "NotaryNode1", + "address" : "NS6vb4uE8wdQfcQbFcRY7yquSbwbVcMSV3", + "isdefault" : false, + "lock" : false, + "key" : "6PYMGBef95jMZJTQcH9ZP5PuecWa2H86HFbdnfe7VQs8uPZ3S6pu4D5NpP" + }, + { + "contract" : { + "script" : "DCEDHRWEIGXHCwUU2Fc7B0qrYPezXR0sfdEduRExyzIKVC9BdHR2qg==", + "deployed" : false, + "parameters" : [ + { + "name" : "parameter0", + "type" : "Signature" + } + ] + }, + "label" : "three", + "address" : "NakELwR1i6brB7EmYLc6yPbvk78Qi5Qbpi", + "isdefault" : false, + "lock" : false, + "key" : "6PYLm6kse9FVpKoBbhYYSFHhFUUL2bZYePU95x7Ncknu798WEHYmTuUijR" + }, + { + "contract" : { + "parameters" : [ + { + "name" : "parameter0", + "type" : "Signature" + } + ], + "deployed" : false, + "script" : "DCECmUfs/gqKHd3AdJm5+Ev6zkubV8pP8DZzgu8+t5WdphJBdHR2qg==" + }, + "key" : "6PYWBWehojbBn8U2XWcXxuWqPrnp9qwQ5rD3RKQza1iR5ZBCPHXxCQonYm", + "isdefault" : false, + "lock" : false, + "address" : "NLWXE5CMEqJzEVtscF4BoTvSbiBr1FwtBb", + "label" : "four" + } + ], + "extra" : { + "Tokens" : null + }, + "version" : "3.0" +} + diff --git a/pkg/services/notary/testdata/notary2.json b/pkg/services/notary/testdata/notary2.json index 329c11390..a2a6b2f48 100644 --- a/pkg/services/notary/testdata/notary2.json +++ b/pkg/services/notary/testdata/notary2.json @@ -1 +1,30 @@ -{"version":"3.0","accounts":[{"address":"NfFcJvWcHe8SSS92hNZhyQUJ6cg3pb36Tf","key":"6PYU2QoD52Xt9Z6QmNGUJWn89qUD1W6QqAL4Y8nfTWtTKvmVpQh8wsH6qY","label":"NotaryNode2","contract":{"script":"DCECIcKj0GFdv4b1NZrw9X6zLNLWzmNKAxtw6olIMZxpPRQLQZVEDXg=","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"lock":false,"isdefault":false}],"scrypt":{"n":16384,"r":8,"p":8},"extra":{"Tokens":null}} +{ + "scrypt" : { + "r" : 8, + "p" : 8, + "n" : 16384 + }, + "version" : "3.0", + "extra" : { + "Tokens" : null + }, + "accounts" : [ + { + "address" : "NYt1oLCMMvqxnAVMJCSk87kZTP693GxK11", + "key" : "6PYWejAoqoF4JT5P9N3EcqC5tRC1MXx2gZ1R2WWryjDUH1bMAo3Bd68nCo", + "lock" : false, + "isdefault" : false, + "contract" : { + "parameters" : [ + { + "name" : "parameter0", + "type" : "Signature" + } + ], + "deployed" : false, + "script" : "DCECIcKj0GFdv4b1NZrw9X6zLNLWzmNKAxtw6olIMZxpPRRBdHR2qg==" + }, + "label" : "NotaryNode2" + } + ] +} diff --git a/pkg/services/oracle/testdata/oracle1.json b/pkg/services/oracle/testdata/oracle1.json index ae817cdbf..21bd210aa 100644 --- a/pkg/services/oracle/testdata/oracle1.json +++ b/pkg/services/oracle/testdata/oracle1.json @@ -1 +1,30 @@ -{"version":"3.0","accounts":[{"address":"NMy1PN9GCdGc26YFG7JruYg7UBStw2pPKN","key":"6PYML6dDTMXJBD7ywRwiCAhseCPToWkMfvPUViuxiXM6s5oi7ggf4ho3AK","label":"","contract":{"script":"DCEDNxK01e1DnGA+TiGU3H4DKUuGliSz89/NuZCbVvA2u0wLQZVEDXg=","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"lock":false,"isdefault":false}],"scrypt":{"n":16384,"r":8,"p":8},"extra":{"Tokens":null}} +{ + "accounts" : [ + { + "address" : "NPcxgUcZorwb1njfRGL7JEyc6SsTku42ek", + "key" : "6PYPy72Gnoif27u9Uy5r2sqMwTTYMLRBGwPcJmB1GP1FTPp4U3M9fZVdtg", + "lock" : false, + "isdefault" : false, + "contract" : { + "parameters" : [ + { + "type" : "Signature", + "name" : "parameter0" + } + ], + "script" : "DCEDNxK01e1DnGA+TiGU3H4DKUuGliSz89/NuZCbVvA2u0xBdHR2qg==", + "deployed" : false + }, + "label" : "" + } + ], + "extra" : { + "Tokens" : null + }, + "version" : "3.0", + "scrypt" : { + "n" : 16384, + "p" : 8, + "r" : 8 + } +} diff --git a/pkg/services/oracle/testdata/oracle2.json b/pkg/services/oracle/testdata/oracle2.json index d5d131b5f..ecfd9346c 100644 --- a/pkg/services/oracle/testdata/oracle2.json +++ b/pkg/services/oracle/testdata/oracle2.json @@ -1 +1,30 @@ -{"version":"3.0","accounts":[{"address":"NU7QxQXULbmZU7kaWUaeF3r9v3zimU42bV","key":"6PYKv77p5wihN64XaPB5Nbci1sCLV5CrzSu8GKv7UHXHRtytfLt8zfrMgT","label":"","contract":{"script":"DCEDEXzwIl4Jhvsj98GYIPFFiedeb1QdP8T79uSBSDNsiswLQZVEDXg=","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"lock":false,"isdefault":false}],"scrypt":{"n":16384,"r":8,"p":8},"extra":{"Tokens":null}} +{ + "version" : "3.0", + "extra" : { + "Tokens" : null + }, + "accounts" : [ + { + "isdefault" : false, + "label" : "", + "key" : "6PYTgfwm5kuahFNN391D6p21ivKrHpvr8Wnn1mBpMnavn6PxkK2prSsxye", + "address" : "NhSCnPJdgLdxnQaYHmW9A3WFLT9DiicBfK", + "lock" : false, + "contract" : { + "script" : "DCEDEXzwIl4Jhvsj98GYIPFFiedeb1QdP8T79uSBSDNsisxBdHR2qg==", + "deployed" : false, + "parameters" : [ + { + "type" : "Signature", + "name" : "parameter0" + } + ] + } + } + ], + "scrypt" : { + "p" : 8, + "n" : 16384, + "r" : 8 + } +} diff --git a/pkg/smartcontract/contract.go b/pkg/smartcontract/contract.go index 7168e83e4..598d9dbcb 100644 --- a/pkg/smartcontract/contract.go +++ b/pkg/smartcontract/contract.go @@ -8,7 +8,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/vm/emit" - "github.com/nspcc-dev/neo-go/pkg/vm/opcode" ) // CreateMultiSigRedeemScript creates an "m out of n" type verification script @@ -31,8 +30,7 @@ func CreateMultiSigRedeemScript(m int, publicKeys keys.PublicKeys) ([]byte, erro emit.Bytes(buf.BinWriter, pubKey.Bytes()) } emit.Int(buf.BinWriter, int64(len(publicKeys))) - emit.Opcodes(buf.BinWriter, opcode.PUSHNULL) - emit.Syscall(buf.BinWriter, interopnames.NeoCryptoCheckMultisigWithECDsaSecp256r1) + emit.Syscall(buf.BinWriter, interopnames.NeoCryptoCheckMultisig) return buf.Bytes(), nil } diff --git a/pkg/smartcontract/contract_test.go b/pkg/smartcontract/contract_test.go index 0be9d2b58..6031c7f09 100644 --- a/pkg/smartcontract/contract_test.go +++ b/pkg/smartcontract/contract_test.go @@ -32,9 +32,8 @@ func TestCreateMultiSigRedeemScript(t *testing.T) { } assert.Equal(t, opcode.PUSH3, opcode.Opcode(br.ReadB())) - assert.Equal(t, opcode.PUSHNULL, opcode.Opcode(br.ReadB())) assert.Equal(t, opcode.SYSCALL, opcode.Opcode(br.ReadB())) - assert.Equal(t, interopnames.ToID([]byte(interopnames.NeoCryptoCheckMultisigWithECDsaSecp256r1)), br.ReadU32LE()) + assert.Equal(t, interopnames.ToID([]byte(interopnames.NeoCryptoCheckMultisig)), br.ReadU32LE()) } func TestCreateDefaultMultiSigRedeemScript(t *testing.T) { diff --git a/pkg/vm/cli/cli_test.go b/pkg/vm/cli/cli_test.go index b32ce15a0..477e996ec 100644 --- a/pkg/vm/cli/cli_test.go +++ b/pkg/vm/cli/cli_test.go @@ -299,8 +299,8 @@ func TestRunWithDifferentArguments(t *testing.T) { func TestPrintOps(t *testing.T) { w := io.NewBufBinWriter() - emit.Opcodes(w.BinWriter, opcode.PUSH1) - emit.Syscall(w.BinWriter, interopnames.SystemBinarySerialize) + emit.String(w.BinWriter, "log") + emit.Syscall(w.BinWriter, interopnames.SystemRuntimeLog) emit.Instruction(w.BinWriter, opcode.PUSHDATA1, []byte{3, 1, 2, 3}) script := w.Bytes() e := newTestVMCLI(t) @@ -312,9 +312,9 @@ func TestPrintOps(t *testing.T) { e.checkNextLine(t, ".*no program loaded") e.checkNextLine(t, fmt.Sprintf("READY: loaded %d instructions", len(script))) e.checkNextLine(t, "INDEX.*OPCODE.*PARAMETER") - e.checkNextLine(t, "0.*PUSH1") - e.checkNextLine(t, "1.*SYSCALL.*System\\.Binary\\.Serialize") - e.checkNextLine(t, "6.*PUSHDATA1.*010203") + e.checkNextLine(t, "0.*PUSHDATA1.*6c6f67") + e.checkNextLine(t, "5.*SYSCALL.*System\\.Runtime\\.Log") + e.checkNextLine(t, "10.*PUSHDATA1.*010203") } func TestLoadAbort(t *testing.T) { diff --git a/pkg/vm/contract_checks.go b/pkg/vm/contract_checks.go index 7a721aa3a..e62a29531 100644 --- a/pkg/vm/contract_checks.go +++ b/pkg/vm/contract_checks.go @@ -13,8 +13,8 @@ import ( ) var ( - verifyInteropID = interopnames.ToID([]byte(interopnames.NeoCryptoVerifyWithECDsaSecp256r1)) - multisigInteropID = interopnames.ToID([]byte(interopnames.NeoCryptoCheckMultisigWithECDsaSecp256r1)) + verifyInteropID = interopnames.ToID([]byte(interopnames.NeoCryptoCheckSig)) + multisigInteropID = interopnames.ToID([]byte(interopnames.NeoCryptoCheckMultisig)) ) func getNumOfThingsFromInstr(instr opcode.Opcode, param []byte) (int, bool) { @@ -49,6 +49,9 @@ func IsMultiSigContract(script []byte) bool { // from the verification script of the contract. func ParseMultiSigContract(script []byte) (int, [][]byte, bool) { var nsigs, nkeys int + if len(script) < 42 { + return nsigs, nil, false + } ctx := NewContext(script) instr, param, err := ctx.Next() @@ -87,10 +90,6 @@ func ParseMultiSigContract(script []byte) (int, [][]byte, bool) { if nkeys2 != nkeys { return nsigs, nil, false } - instr, _, err = ctx.Next() - if err != nil || instr != opcode.PUSHNULL { - return nsigs, nil, false - } instr, param, err = ctx.Next() if err != nil || instr != opcode.SYSCALL || binary.LittleEndian.Uint32(param) != multisigInteropID { return nsigs, nil, false @@ -112,7 +111,7 @@ func IsSignatureContract(script []byte) bool { // ParseSignatureContract parses simple signature contract and returns // public key. func ParseSignatureContract(script []byte) ([]byte, bool) { - if len(script) != 41 { + if len(script) != 40 { return nil, false } @@ -122,10 +121,6 @@ func ParseSignatureContract(script []byte) ([]byte, bool) { return nil, false } pub := param - instr, _, err = ctx.Next() - if err != nil || instr != opcode.PUSHNULL { - return nil, false - } instr, param, err = ctx.Next() if err != nil || instr != opcode.SYSCALL || binary.LittleEndian.Uint32(param) != verifyInteropID { return nil, false diff --git a/pkg/vm/contract_checks_test.go b/pkg/vm/contract_checks_test.go index 29146c4f4..083e5db53 100644 --- a/pkg/vm/contract_checks_test.go +++ b/pkg/vm/contract_checks_test.go @@ -16,12 +16,11 @@ import ( ) func testSignatureContract() []byte { - prog := make([]byte, 41) + prog := make([]byte, 40) prog[0] = byte(opcode.PUSHDATA1) prog[1] = 33 - prog[35] = byte(opcode.PUSHNULL) - prog[36] = byte(opcode.SYSCALL) - binary.LittleEndian.PutUint32(prog[37:], verifyInteropID) + prog[35] = byte(opcode.SYSCALL) + binary.LittleEndian.PutUint32(prog[36:], verifyInteropID) return prog } @@ -43,7 +42,7 @@ func TestIsSignatureContract(t *testing.T) { t.Run("invalid interop ID", func(t *testing.T) { prog := testSignatureContract() - binary.LittleEndian.PutUint32(prog[37:], ^verifyInteropID) + binary.LittleEndian.PutUint32(prog[36:], ^verifyInteropID) assert.False(t, IsSignatureContract(prog)) assert.False(t, IsStandardContract(prog)) }) @@ -55,13 +54,6 @@ func TestIsSignatureContract(t *testing.T) { assert.False(t, IsStandardContract(prog)) }) - t.Run("no PUSHNULL", func(t *testing.T) { - prog := testSignatureContract() - prog[35] = byte(opcode.PUSH1) - assert.False(t, IsSignatureContract(prog)) - assert.False(t, IsStandardContract(prog)) - }) - t.Run("invalid length", func(t *testing.T) { prog := testSignatureContract() prog = append(prog, 0) @@ -110,15 +102,9 @@ func TestIsMultiSigContract(t *testing.T) { assert.False(t, IsMultiSigContract(prog)) }) - t.Run("no PUSHNULL", func(t *testing.T) { - prog := testMultisigContract(t, 2, 2) - prog[len(prog)-6] ^= 0xFF - assert.False(t, IsMultiSigContract(prog)) - }) - t.Run("invalid keys number", func(t *testing.T) { prog := testMultisigContract(t, 2, 2) - prog[len(prog)-7] = byte(opcode.PUSH3) + prog[len(prog)-6] = byte(opcode.PUSH3) assert.False(t, IsMultiSigContract(prog)) }) diff --git a/pkg/vm/interop.go b/pkg/vm/interop.go index 129357886..d4e153c0a 100644 --- a/pkg/vm/interop.go +++ b/pkg/vm/interop.go @@ -19,10 +19,6 @@ type interopIDFuncPrice struct { } var defaultVMInterops = []interopIDFuncPrice{ - {ID: interopnames.ToID([]byte(interopnames.SystemBinaryDeserialize)), - Func: RuntimeDeserialize, Price: 1 << 14}, - {ID: interopnames.ToID([]byte(interopnames.SystemBinarySerialize)), - Func: RuntimeSerialize, Price: 1 << 12}, {ID: interopnames.ToID([]byte(interopnames.SystemRuntimeLog)), Func: runtimeLog, Price: 1 << 15, RequiredFlags: callflag.AllowNotify}, {ID: interopnames.ToID([]byte(interopnames.SystemRuntimeNotify)), @@ -68,35 +64,6 @@ func runtimeNotify(vm *VM) error { return nil } -// RuntimeSerialize handles System.Binary.Serialize syscall. -func RuntimeSerialize(vm *VM) error { - item := vm.Estack().Pop() - data, err := stackitem.SerializeItem(item.value) - if err != nil { - return err - } else if len(data) > stackitem.MaxSize { - return errors.New("too big item") - } - - vm.Estack().PushVal(data) - - return nil -} - -// RuntimeDeserialize handles System.Binary.Deserialize syscall. -func RuntimeDeserialize(vm *VM) error { - data := vm.Estack().Pop().Bytes() - - item, err := stackitem.DeserializeItem(data) - if err != nil { - return err - } - - vm.Estack().Push(&Element{value: item}) - - return nil -} - // init sorts the global defaultVMInterops value. func init() { sort.Slice(defaultVMInterops, func(i, j int) bool { diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 937dca127..b937a9282 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -3,7 +3,6 @@ package vm import ( "bytes" "encoding/binary" - "encoding/hex" "errors" "fmt" "math" @@ -531,168 +530,6 @@ func getSyscallProg(name string) (prog []byte) { return buf.Bytes() } -func getSerializeProg() (prog []byte) { - prog = append(prog, getSyscallProg(interopnames.SystemBinarySerialize)...) - prog = append(prog, getSyscallProg(interopnames.SystemBinaryDeserialize)...) - prog = append(prog, byte(opcode.RET)) - - return -} - -func testSerialize(t *testing.T, vm *VM) { - err := vm.Step() - require.NoError(t, err) - require.Equal(t, 1, vm.estack.Len()) - require.IsType(t, (*stackitem.ByteArray)(nil), vm.estack.Top().value) - - err = vm.Step() - require.NoError(t, err) - require.Equal(t, 1, vm.estack.Len()) -} - -func TestSerializeBool(t *testing.T) { - vm := load(getSerializeProg()) - vm.estack.PushVal(true) - - testSerialize(t, vm) - - require.IsType(t, (*stackitem.Bool)(nil), vm.estack.Top().value) - require.Equal(t, true, vm.estack.Top().Bool()) -} - -func TestSerializeByteArray(t *testing.T) { - vm := load(getSerializeProg()) - value := []byte{1, 2, 3} - vm.estack.PushVal(value) - - testSerialize(t, vm) - - require.IsType(t, (*stackitem.ByteArray)(nil), vm.estack.Top().value) - require.Equal(t, value, vm.estack.Top().Bytes()) -} - -func TestSerializeInteger(t *testing.T) { - vm := load(getSerializeProg()) - value := int64(123) - vm.estack.PushVal(value) - - testSerialize(t, vm) - - require.IsType(t, (*stackitem.BigInteger)(nil), vm.estack.Top().value) - require.Equal(t, value, vm.estack.Top().BigInt().Int64()) -} - -func TestSerializeArray(t *testing.T) { - vm := load(getSerializeProg()) - item := stackitem.NewArray([]stackitem.Item{ - stackitem.Make(true), - stackitem.Make(123), - stackitem.NewMap(), - }) - - vm.estack.Push(&Element{value: item}) - - testSerialize(t, vm) - - require.IsType(t, (*stackitem.Array)(nil), vm.estack.Top().value) - require.Equal(t, item.Value().([]stackitem.Item), vm.estack.Top().Array()) -} - -func TestSerializeArrayBad(t *testing.T) { - vm := load(getSerializeProg()) - item := stackitem.NewArray(makeArrayOfType(2, stackitem.BooleanT)) - item.Value().([]stackitem.Item)[1] = item - - vm.estack.Push(&Element{value: item}) - - err := vm.Step() - require.Error(t, err) - require.True(t, vm.HasFailed()) -} - -func TestSerializeDupInteger(t *testing.T) { - prog := makeProgram( - opcode.PUSH0, opcode.NEWARRAY, opcode.INITSSLOT, 1, - opcode.DUP, opcode.PUSH2, opcode.DUP, opcode.STSFLD0, opcode.APPEND, - opcode.DUP, opcode.LDSFLD0, opcode.APPEND, - ) - vm := load(append(prog, getSerializeProg()...)) - - runVM(t, vm) -} - -func TestSerializeStruct(t *testing.T) { - vm := load(getSerializeProg()) - item := stackitem.NewStruct([]stackitem.Item{ - stackitem.Make(true), - stackitem.Make(123), - stackitem.NewMap(), - }) - - vm.estack.Push(&Element{value: item}) - - testSerialize(t, vm) - - require.IsType(t, (*stackitem.Struct)(nil), vm.estack.Top().value) - require.Equal(t, item.Value().([]stackitem.Item), vm.estack.Top().Array()) -} - -func TestDeserializeUnknown(t *testing.T) { - prog := append(getSyscallProg(interopnames.SystemBinaryDeserialize), byte(opcode.RET)) - - data, err := stackitem.SerializeItem(stackitem.NewBigInteger(big.NewInt(123))) - require.NoError(t, err) - - data[0] = 0xFF - - runWithArgs(t, prog, nil, data) -} - -func TestSerializeMap(t *testing.T) { - vm := load(getSerializeProg()) - item := stackitem.NewMap() - item.Add(stackitem.Make(true), stackitem.Make([]byte{1, 2, 3})) - item.Add(stackitem.Make([]byte{0}), stackitem.Make(false)) - - vm.estack.Push(&Element{value: item}) - - testSerialize(t, vm) - - require.IsType(t, (*stackitem.Map)(nil), vm.estack.Top().value) - require.Equal(t, item.Value(), vm.estack.Top().value.(*stackitem.Map).Value()) -} - -func TestSerializeMapCompat(t *testing.T) { - resHex := "480128036b6579280576616c7565" - res, err := hex.DecodeString(resHex) - require.NoError(t, err) - - // Create a map, push key and value, add KV to map, serialize. - buf := io.NewBufBinWriter() - emit.Opcodes(buf.BinWriter, opcode.NEWMAP) - emit.Opcodes(buf.BinWriter, opcode.DUP) - emit.Bytes(buf.BinWriter, []byte("key")) - emit.Bytes(buf.BinWriter, []byte("value")) - emit.Opcodes(buf.BinWriter, opcode.SETITEM) - emit.Syscall(buf.BinWriter, interopnames.SystemBinarySerialize) - require.NoError(t, buf.Err) - - vm := load(buf.Bytes()) - runVM(t, vm) - assert.Equal(t, res, vm.estack.Pop().Bytes()) -} - -func TestSerializeInterop(t *testing.T) { - vm := load(getSerializeProg()) - item := stackitem.NewInterop("kek") - - vm.estack.Push(&Element{value: item}) - - err := vm.Step() - require.Error(t, err) - require.True(t, vm.HasFailed()) -} - func getTestCallFlagsFunc(syscall []byte, flags callflag.CallFlag, result interface{}) func(t *testing.T) { return func(t *testing.T) { script := append([]byte{byte(opcode.SYSCALL)}, syscall...) diff --git a/pkg/wallet/account_test.go b/pkg/wallet/account_test.go index f44eeb6ef..cb26d34db 100644 --- a/pkg/wallet/account_test.go +++ b/pkg/wallet/account_test.go @@ -113,13 +113,13 @@ func TestAccount_ConvertMultisig(t *testing.T) { t.Run("1/1 multisig", func(t *testing.T) { pubs := convertPubs(t, hexs[:1]) require.NoError(t, a.ConvertMultisig(1, pubs)) - require.Equal(t, "NVNvVRW5Q5naSx2k2iZm7xRgtRNGuZppAK", a.Address) + require.Equal(t, "NNudMSGzEoktFzdYGYoNb3bzHzbmM1genF", a.Address) }) t.Run("3/4 multisig", func(t *testing.T) { pubs := convertPubs(t, hexs) require.NoError(t, a.ConvertMultisig(3, pubs)) - require.Equal(t, "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY", a.Address) + require.Equal(t, "NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6", a.Address) }) } @@ -135,11 +135,11 @@ func convertPubs(t *testing.T, hexKeys []string) []*keys.PublicKey { func compareFields(t *testing.T, tk keytestcases.Ktype, acc *Account) { want, have := tk.Address, acc.Address - require.Equalf(t, want, have, "expected %s got %s", want, have) + require.Equalf(t, want, have, "expected address %s got %s", want, have) want, have = tk.Wif, acc.wif - require.Equalf(t, want, have, "expected %s got %s", want, have) + require.Equalf(t, want, have, "expected wif %s got %s", want, have) want, have = tk.PublicKey, hex.EncodeToString(acc.publicKey) - require.Equalf(t, want, have, "expected %s got %s", want, have) + require.Equalf(t, want, have, "expected pub key %s got %s", want, have) want, have = tk.PrivateKey, acc.privateKey.String() - require.Equalf(t, want, have, "expected %s got %s", want, have) + require.Equalf(t, want, have, "expected priv key %s got %s", want, have) } diff --git a/pkg/wallet/testdata/wallet1.json b/pkg/wallet/testdata/wallet1.json index 27a48a3ed..1ade05d76 100644 --- a/pkg/wallet/testdata/wallet1.json +++ b/pkg/wallet/testdata/wallet1.json @@ -2,11 +2,11 @@ "version": "3.0", "accounts": [ { - "address": "NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc", - "key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux", + "address": "NTh9TnZTstvAePEYWDGLLxidBikJE24uTo", + "key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL", "label": "", "contract": { - "script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcILQZVEDXg=", + "script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcJBdHR2qg==", "parameters": [ { "name": "parameter0", @@ -19,11 +19,11 @@ "isdefault": false }, { - "address": "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY", - "key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux", + "address": "NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6", + "key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL", "label": "", "contract": { - "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw==", + "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEF7zmyl", "parameters": [ { "name": "parameter0", diff --git a/pkg/wallet/testdata/wallet2.json b/pkg/wallet/testdata/wallet2.json index d5030380c..ea840d39d 100644 --- a/pkg/wallet/testdata/wallet2.json +++ b/pkg/wallet/testdata/wallet2.json @@ -2,11 +2,11 @@ "version": "3.0", "accounts": [ { - "address": "NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc", - "key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux", + "address": "NTh9TnZTstvAePEYWDGLLxidBikJE24uTo", + "key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL", "label": "", "contract": { - "script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcILQZVEDXg=", + "script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcJBdHR2qg==", "parameters": [ { "name": "parameter0", @@ -19,11 +19,11 @@ "isdefault": false }, { - "address": "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY", - "key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux", + "address": "NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6", + "key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL", "label": "", "contract": { - "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw==", + "script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEF7zmyl", "parameters": [ { "name": "parameter0", @@ -44,11 +44,11 @@ "isdefault": false }, { - "address": "NWvKSwutC8D6VKmmPxAEgFKx2NLvFhn8q5", - "key": "6PYKEHagXJ3mDLdga1FoyTGRtPdJgPz6Gb8sjEFwZvRu7ncD9PVZfHtMzL", + "address": "NUREbqw2kfbPgDeEz8Dac2QxntGGqqFMm7", + "key": "6PYXADog3RQCwKRhqQsobwZEFopdcCJuMfPosM9pXPaDWSguKvznLdpADk", "label": "", "contract": { - "script": "DCECEDp/fdAWVYWX95YNJ8UWpDlP2Wi55lFV60sBPkBAQG4LQZVEDXg=", + "script": "DCECEDp/fdAWVYWX95YNJ8UWpDlP2Wi55lFV60sBPkBAQG5BdHR2qg==", "parameters": [ { "name": "parameter0", diff --git a/pkg/wallet/wallet_test.go b/pkg/wallet/wallet_test.go index 1811062df..4f3854fc9 100644 --- a/pkg/wallet/wallet_test.go +++ b/pkg/wallet/wallet_test.go @@ -186,14 +186,14 @@ func TestWalletGetChangeAddress(t *testing.T) { require.NoError(t, err) sh := w1.GetChangeAddress() // No default address, the first one is used. - expected, err := address.StringToUint160("NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc") + expected, err := address.StringToUint160("NTh9TnZTstvAePEYWDGLLxidBikJE24uTo") require.NoError(t, err) require.Equal(t, expected, sh) w2, err := NewWalletFromFile("testdata/wallet2.json") require.NoError(t, err) sh = w2.GetChangeAddress() // Default address. - expected, err = address.StringToUint160("NWvKSwutC8D6VKmmPxAEgFKx2NLvFhn8q5") + expected, err = address.StringToUint160("NUREbqw2kfbPgDeEz8Dac2QxntGGqqFMm7") require.NoError(t, err) require.Equal(t, expected, sh) } diff --git a/scripts/gendump/main.go b/scripts/gendump/main.go index bf6abc9be..40581d2ae 100644 --- a/scripts/gendump/main.go +++ b/scripts/gendump/main.go @@ -75,7 +75,7 @@ func main() { handleError("can't tranfser GAS", err) lastBlock = addBlock(bc, lastBlock, valScript, txMoveNeo, txMoveGas) - tx, contractHash, err := testchain.NewDeployTx(bc, "DumpContract", h, strings.NewReader(contract)) + tx, contractHash, _, err := testchain.NewDeployTx(bc, "DumpContract", h, strings.NewReader(contract)) handleError("can't create deploy tx", err) tx.NetworkFee = 10_000_000 tx.ValidUntilBlock = bc.BlockHeight() + 1