Compare commits
11 commits
master
...
useless-br
Author | SHA1 | Date | |
---|---|---|---|
|
2d9f2d40e7 | ||
|
fdc392323e | ||
|
fe292f3f39 | ||
|
fb16891e2a | ||
|
34ee294086 | ||
|
e9cdf3ea59 | ||
|
0345d8cca0 | ||
|
b299205f92 | ||
|
d3b244ccc9 | ||
|
9ef71b9226 | ||
|
2617e3b4f9 |
39 changed files with 1105 additions and 93 deletions
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/engine
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035 h1:21aZjC6ctecuJ4FH3/pBUVaJB+5Mf0Wc3YgsdqqykNE=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/events
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035 h1:21aZjC6ctecuJ4FH3/pBUVaJB+5Mf0Wc3YgsdqqykNE=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/iterator
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035 h1:21aZjC6ctecuJ4FH3/pBUVaJB+5Mf0Wc3YgsdqqykNE=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/nft
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035 h1:21aZjC6ctecuJ4FH3/pBUVaJB+5Mf0Wc3YgsdqqykNE=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -4,7 +4,7 @@ go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/nspcc-dev/neo-go v0.102.1-0.20231020181554-d89c8801d689
|
github.com/nspcc-dev/neo-go v0.102.1-0.20231020181554-d89c8801d689
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -200,8 +200,8 @@ github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22/go.mod h
|
||||||
github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y=
|
github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y=
|
||||||
github.com/nspcc-dev/neo-go v0.102.1-0.20231020181554-d89c8801d689 h1:WnEdGAQwaW0C8wnNnQZ+rM/JfFKZDSTOqwm8cS0TOdk=
|
github.com/nspcc-dev/neo-go v0.102.1-0.20231020181554-d89c8801d689 h1:WnEdGAQwaW0C8wnNnQZ+rM/JfFKZDSTOqwm8cS0TOdk=
|
||||||
github.com/nspcc-dev/neo-go v0.102.1-0.20231020181554-d89c8801d689/go.mod h1:x+wmcYqpZYJwLp1l/pHZrqNp3RSWlkMymWGDij3/OPo=
|
github.com/nspcc-dev/neo-go v0.102.1-0.20231020181554-d89c8801d689/go.mod h1:x+wmcYqpZYJwLp1l/pHZrqNp3RSWlkMymWGDij3/OPo=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035 h1:21aZjC6ctecuJ4FH3/pBUVaJB+5Mf0Wc3YgsdqqykNE=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
github.com/nspcc-dev/neofs-api-go/v2 v2.14.0 h1:jhuN8Ldqz7WApvUJRFY0bjRXE1R3iCkboMX5QVZhHVk=
|
github.com/nspcc-dev/neofs-api-go/v2 v2.14.0 h1:jhuN8Ldqz7WApvUJRFY0bjRXE1R3iCkboMX5QVZhHVk=
|
||||||
github.com/nspcc-dev/neofs-crypto v0.4.0 h1:5LlrUAM5O0k1+sH/sktBtrgfWtq1pgpDs09fZo+KYi4=
|
github.com/nspcc-dev/neofs-crypto v0.4.0 h1:5LlrUAM5O0k1+sH/sktBtrgfWtq1pgpDs09fZo+KYi4=
|
||||||
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11 h1:QOc8ZRN5DXlAeRPh5QG9u8rMLgoeRNiZF5/vL7QupWg=
|
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11 h1:QOc8ZRN5DXlAeRPh5QG9u8rMLgoeRNiZF5/vL7QupWg=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/nft-nd
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035 h1:21aZjC6ctecuJ4FH3/pBUVaJB+5Mf0Wc3YgsdqqykNE=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/oracle
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035 h1:21aZjC6ctecuJ4FH3/pBUVaJB+5Mf0Wc3YgsdqqykNE=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/runtime
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035 h1:21aZjC6ctecuJ4FH3/pBUVaJB+5Mf0Wc3YgsdqqykNE=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/storage
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035 h1:21aZjC6ctecuJ4FH3/pBUVaJB+5Mf0Wc3YgsdqqykNE=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/timer
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035 h1:21aZjC6ctecuJ4FH3/pBUVaJB+5Mf0Wc3YgsdqqykNE=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/token
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035 h1:21aZjC6ctecuJ4FH3/pBUVaJB+5Mf0Wc3YgsdqqykNE=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -33,7 +33,7 @@ require (
|
||||||
github.com/mmcloughlin/addchain v0.4.0 // indirect
|
github.com/mmcloughlin/addchain v0.4.0 // indirect
|
||||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20231123160306-3374ff1e7a3c // indirect
|
github.com/nspcc-dev/go-ordered-json v0.0.0-20231123160306-3374ff1e7a3c // indirect
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 // indirect
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035 // indirect
|
||||||
github.com/nspcc-dev/rfc6979 v0.2.0 // indirect
|
github.com/nspcc-dev/rfc6979 v0.2.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_golang v1.13.0 // indirect
|
github.com/prometheus/client_golang v1.13.0 // indirect
|
||||||
|
|
|
@ -216,8 +216,8 @@ github.com/nspcc-dev/go-ordered-json v0.0.0-20231123160306-3374ff1e7a3c/go.mod h
|
||||||
github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y=
|
github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y=
|
||||||
github.com/nspcc-dev/neo-go v0.103.1 h1:BfRBceHUu8jSc1KQy7CzmQ/pa+xzAmgcyteGf0/IGgM=
|
github.com/nspcc-dev/neo-go v0.103.1 h1:BfRBceHUu8jSc1KQy7CzmQ/pa+xzAmgcyteGf0/IGgM=
|
||||||
github.com/nspcc-dev/neo-go v0.103.1/go.mod h1:MD7MPiyshUwrE5n1/LzxeandbItaa/iLW/bJb6gNs/U=
|
github.com/nspcc-dev/neo-go v0.103.1/go.mod h1:MD7MPiyshUwrE5n1/LzxeandbItaa/iLW/bJb6gNs/U=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035 h1:21aZjC6ctecuJ4FH3/pBUVaJB+5Mf0Wc3YgsdqqykNE=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
github.com/nspcc-dev/neofs-api-go/v2 v2.14.0 h1:jhuN8Ldqz7WApvUJRFY0bjRXE1R3iCkboMX5QVZhHVk=
|
github.com/nspcc-dev/neofs-api-go/v2 v2.14.0 h1:jhuN8Ldqz7WApvUJRFY0bjRXE1R3iCkboMX5QVZhHVk=
|
||||||
github.com/nspcc-dev/neofs-crypto v0.4.0 h1:5LlrUAM5O0k1+sH/sktBtrgfWtq1pgpDs09fZo+KYi4=
|
github.com/nspcc-dev/neofs-crypto v0.4.0 h1:5LlrUAM5O0k1+sH/sktBtrgfWtq1pgpDs09fZo+KYi4=
|
||||||
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11 h1:QOc8ZRN5DXlAeRPh5QG9u8rMLgoeRNiZF5/vL7QupWg=
|
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11 h1:QOc8ZRN5DXlAeRPh5QG9u8rMLgoeRNiZF5/vL7QupWg=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/examples/zkp/xor
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035 h1:21aZjC6ctecuJ4FH3/pBUVaJB+5Mf0Wc3YgsdqqykNE=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -16,7 +16,7 @@ require (
|
||||||
github.com/mr-tron/base58 v1.2.0
|
github.com/mr-tron/base58 v1.2.0
|
||||||
github.com/nspcc-dev/dbft v0.2.0
|
github.com/nspcc-dev/dbft v0.2.0
|
||||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2
|
github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035
|
||||||
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11
|
github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11
|
||||||
github.com/nspcc-dev/rfc6979 v0.2.1
|
github.com/nspcc-dev/rfc6979 v0.2.1
|
||||||
github.com/pierrec/lz4 v2.6.1+incompatible
|
github.com/pierrec/lz4 v2.6.1+incompatible
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -94,8 +94,8 @@ github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2 h1:mD9hU
|
||||||
github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2/go.mod h1:U5VfmPNM88P4RORFb6KSUVBdJBDhlqggJZYGXGPxOcc=
|
github.com/nspcc-dev/go-ordered-json v0.0.0-20240301084351-0246b013f8b2/go.mod h1:U5VfmPNM88P4RORFb6KSUVBdJBDhlqggJZYGXGPxOcc=
|
||||||
github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y=
|
github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y=
|
||||||
github.com/nspcc-dev/hrw v1.0.9/go.mod h1:l/W2vx83vMQo6aStyx2AuZrJ+07lGv2JQGlVkPG06MU=
|
github.com/nspcc-dev/hrw v1.0.9/go.mod h1:l/W2vx83vMQo6aStyx2AuZrJ+07lGv2JQGlVkPG06MU=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035 h1:21aZjC6ctecuJ4FH3/pBUVaJB+5Mf0Wc3YgsdqqykNE=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
github.com/nspcc-dev/neofs-api-go/v2 v2.14.0 h1:jhuN8Ldqz7WApvUJRFY0bjRXE1R3iCkboMX5QVZhHVk=
|
github.com/nspcc-dev/neofs-api-go/v2 v2.14.0 h1:jhuN8Ldqz7WApvUJRFY0bjRXE1R3iCkboMX5QVZhHVk=
|
||||||
github.com/nspcc-dev/neofs-api-go/v2 v2.14.0/go.mod h1:DRIr0Ic1s+6QgdqmNFNLIqMqd7lNMJfYwkczlm1hDtM=
|
github.com/nspcc-dev/neofs-api-go/v2 v2.14.0/go.mod h1:DRIr0Ic1s+6QgdqmNFNLIqMqd7lNMJfYwkczlm1hDtM=
|
||||||
github.com/nspcc-dev/neofs-crypto v0.4.0 h1:5LlrUAM5O0k1+sH/sktBtrgfWtq1pgpDs09fZo+KYi4=
|
github.com/nspcc-dev/neofs-crypto v0.4.0 h1:5LlrUAM5O0k1+sH/sktBtrgfWtq1pgpDs09fZo+KYi4=
|
||||||
|
|
|
@ -2,4 +2,4 @@ module github.com/nspcc-dev/neo-go/internal/examples/oracle
|
||||||
|
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7
|
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7 h1:wULMfaNToxlinz9cYWyZA4kAy6CTUBtJ1yFHjCSgs10=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035 h1:21aZjC6ctecuJ4FH3/pBUVaJB+5Mf0Wc3YgsdqqykNE=
|
||||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240322141543-1840c057bdd7/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20240504105341-947aafa1a035/go.mod h1:/vrbWSHc7YS1KSYhVOyyeucXW/e+1DkVBOgnBEXUCeY=
|
||||||
|
|
|
@ -74,8 +74,10 @@ func TestRoleManagementRole(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCryptoLibNamedCurve(t *testing.T) {
|
func TestCryptoLibNamedCurve(t *testing.T) {
|
||||||
require.EqualValues(t, native.Secp256k1, crypto.Secp256k1)
|
require.EqualValues(t, native.Secp256k1Sha256, crypto.Secp256k1Sha256)
|
||||||
require.EqualValues(t, native.Secp256r1, crypto.Secp256r1)
|
require.EqualValues(t, native.Secp256r1Sha256, crypto.Secp256r1Sha256)
|
||||||
|
require.EqualValues(t, native.Secp256k1Keccak256, crypto.Secp256k1Keccak256)
|
||||||
|
require.EqualValues(t, native.Secp256r1Keccak256, crypto.Secp256r1Keccak256)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOracleContractValues(t *testing.T) {
|
func TestOracleContractValues(t *testing.T) {
|
||||||
|
@ -233,7 +235,7 @@ func TestNativeHelpersCompile(t *testing.T) {
|
||||||
{"sha256", []string{"[]byte{1, 2, 3}"}},
|
{"sha256", []string{"[]byte{1, 2, 3}"}},
|
||||||
{"ripemd160", []string{"[]byte{1, 2, 3}"}},
|
{"ripemd160", []string{"[]byte{1, 2, 3}"}},
|
||||||
{"murmur32", []string{"[]byte{1, 2, 3}", "123"}},
|
{"murmur32", []string{"[]byte{1, 2, 3}", "123"}},
|
||||||
{"verifyWithECDsa", []string{"[]byte{1, 2, 3}", pub, sig, "crypto.Secp256k1"}},
|
{"verifyWithECDsa", []string{"[]byte{1, 2, 3}", pub, sig, "crypto.Secp256k1Sha256"}},
|
||||||
{"bls12381Serialize", []string{"crypto.Bls12381Point{}"}},
|
{"bls12381Serialize", []string{"crypto.Bls12381Point{}"}},
|
||||||
{"bls12381Deserialize", []string{"[]byte{1, 2, 3}"}},
|
{"bls12381Deserialize", []string{"[]byte{1, 2, 3}"}},
|
||||||
{"bls12381Equal", []string{"crypto.Bls12381Point{}", "crypto.Bls12381Point{}"}},
|
{"bls12381Equal", []string{"crypto.Bls12381Point{}", "crypto.Bls12381Point{}"}},
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"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/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/twmb/murmur3"
|
"github.com/twmb/murmur3"
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
|
@ -28,13 +29,18 @@ type Crypto struct {
|
||||||
interop.ContractMD
|
interop.ContractMD
|
||||||
}
|
}
|
||||||
|
|
||||||
// NamedCurve identifies named elliptic curves.
|
// HashFunc is a delegate representing a hasher function with 256 bytes output length.
|
||||||
type NamedCurve byte
|
type HashFunc func([]byte) util.Uint256
|
||||||
|
|
||||||
// Various named elliptic curves.
|
// NamedCurveHash identifies a pair of named elliptic curve and hash function.
|
||||||
|
type NamedCurveHash byte
|
||||||
|
|
||||||
|
// Various pairs of named elliptic curves and hash functions.
|
||||||
const (
|
const (
|
||||||
Secp256k1 NamedCurve = 22
|
Secp256k1Sha256 NamedCurveHash = 22
|
||||||
Secp256r1 NamedCurve = 23
|
Secp256r1Sha256 NamedCurveHash = 23
|
||||||
|
Secp256k1Keccak256 NamedCurveHash = 24
|
||||||
|
Secp256r1Keccak256 NamedCurveHash = 25
|
||||||
)
|
)
|
||||||
|
|
||||||
const cryptoContractID = -3
|
const cryptoContractID = -3
|
||||||
|
@ -63,7 +69,7 @@ func newCrypto() *Crypto {
|
||||||
manifest.NewParameter("message", smartcontract.ByteArrayType),
|
manifest.NewParameter("message", smartcontract.ByteArrayType),
|
||||||
manifest.NewParameter("pubkey", smartcontract.ByteArrayType),
|
manifest.NewParameter("pubkey", smartcontract.ByteArrayType),
|
||||||
manifest.NewParameter("signature", smartcontract.ByteArrayType),
|
manifest.NewParameter("signature", smartcontract.ByteArrayType),
|
||||||
manifest.NewParameter("curve", smartcontract.IntegerType))
|
manifest.NewParameter("curveHash", smartcontract.IntegerType))
|
||||||
md = newMethodAndPrice(c.verifyWithECDsa, 1<<15, callflag.NoneFlag)
|
md = newMethodAndPrice(c.verifyWithECDsa, 1<<15, callflag.NoneFlag)
|
||||||
c.AddMethod(md, desc)
|
c.AddMethod(md, desc)
|
||||||
|
|
||||||
|
@ -142,7 +148,6 @@ func (c *Crypto) verifyWithECDsa(_ *interop.Context, args []stackitem.Item) stac
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("invalid message stackitem: %w", err))
|
panic(fmt.Errorf("invalid message stackitem: %w", err))
|
||||||
}
|
}
|
||||||
hashToCheck := hash.Sha256(msg)
|
|
||||||
pubkey, err := args[1].TryBytes()
|
pubkey, err := args[1].TryBytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("invalid pubkey stackitem: %w", err))
|
panic(fmt.Errorf("invalid pubkey stackitem: %w", err))
|
||||||
|
@ -151,10 +156,11 @@ func (c *Crypto) verifyWithECDsa(_ *interop.Context, args []stackitem.Item) stac
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("invalid signature stackitem: %w", err))
|
panic(fmt.Errorf("invalid signature stackitem: %w", err))
|
||||||
}
|
}
|
||||||
curve, err := curveFromStackitem(args[3])
|
curve, hasher, err := curveHasherFromStackitem(args[3])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("invalid curve stackitem: %w", err))
|
panic(fmt.Errorf("invalid curveHash stackitem: %w", err))
|
||||||
}
|
}
|
||||||
|
hashToCheck := hasher(msg)
|
||||||
pkey, err := keys.NewPublicKeyFromBytes(pubkey, curve)
|
pkey, err := keys.NewPublicKeyFromBytes(pubkey, curve)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("failed to decode pubkey: %w", err))
|
panic(fmt.Errorf("failed to decode pubkey: %w", err))
|
||||||
|
@ -163,22 +169,26 @@ func (c *Crypto) verifyWithECDsa(_ *interop.Context, args []stackitem.Item) stac
|
||||||
return stackitem.NewBool(res)
|
return stackitem.NewBool(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func curveFromStackitem(si stackitem.Item) (elliptic.Curve, error) {
|
func curveHasherFromStackitem(si stackitem.Item) (elliptic.Curve, HashFunc, error) {
|
||||||
curve, err := si.TryInteger()
|
curve, err := si.TryInteger()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if !curve.IsInt64() {
|
if !curve.IsInt64() {
|
||||||
return nil, errors.New("not an int64")
|
return nil, nil, errors.New("not an int64")
|
||||||
}
|
}
|
||||||
c := curve.Int64()
|
c := curve.Int64()
|
||||||
switch c {
|
switch c {
|
||||||
case int64(Secp256k1):
|
case int64(Secp256k1Sha256):
|
||||||
return secp256k1.S256(), nil
|
return secp256k1.S256(), hash.Sha256, nil
|
||||||
case int64(Secp256r1):
|
case int64(Secp256r1Sha256):
|
||||||
return elliptic.P256(), nil
|
return elliptic.P256(), hash.Sha256, nil
|
||||||
|
case int64(Secp256k1Keccak256):
|
||||||
|
return secp256k1.S256(), Keccak256, nil
|
||||||
|
case int64(Secp256r1Keccak256):
|
||||||
|
return elliptic.P256(), Keccak256, nil
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("unsupported curve type")
|
return nil, nil, errors.New("unsupported curve/hash type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,13 +305,7 @@ func (c *Crypto) keccak256(_ *interop.Context, args []stackitem.Item) stackitem.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
return stackitem.NewByteArray(Keccak256(bs).BytesBE())
|
||||||
digest := sha3.NewLegacyKeccak256()
|
|
||||||
_, err = digest.Write(bs)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return stackitem.NewByteArray(digest.Sum(nil))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metadata implements the Contract interface.
|
// Metadata implements the Contract interface.
|
||||||
|
@ -333,3 +337,14 @@ func (c *Crypto) PostPersist(ic *interop.Context) error {
|
||||||
func (c *Crypto) ActiveIn() *config.Hardfork {
|
func (c *Crypto) ActiveIn() *config.Hardfork {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keccak256 hashes the incoming byte slice using the
|
||||||
|
// keccak256 algorithm.
|
||||||
|
func Keccak256(data []byte) util.Uint256 {
|
||||||
|
var hash util.Uint256
|
||||||
|
hasher := sha3.NewLegacyKeccak256()
|
||||||
|
_, _ = hasher.Write(data)
|
||||||
|
|
||||||
|
hasher.Sum(hash[:0])
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
|
"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/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
@ -118,29 +119,44 @@ func TestMurmur32(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCryptoLibVerifyWithECDsa(t *testing.T) {
|
func TestCryptoLibVerifyWithECDsa(t *testing.T) {
|
||||||
t.Run("R1", func(t *testing.T) {
|
t.Run("R1 sha256", func(t *testing.T) {
|
||||||
testECDSAVerify(t, Secp256r1)
|
testECDSAVerify(t, Secp256r1Sha256)
|
||||||
})
|
})
|
||||||
t.Run("K1", func(t *testing.T) {
|
t.Run("K1 sha256", func(t *testing.T) {
|
||||||
testECDSAVerify(t, Secp256k1)
|
testECDSAVerify(t, Secp256k1Sha256)
|
||||||
|
})
|
||||||
|
t.Run("R1 keccak256", func(t *testing.T) {
|
||||||
|
testECDSAVerify(t, Secp256r1Keccak256)
|
||||||
|
})
|
||||||
|
t.Run("K1 keccak256", func(t *testing.T) {
|
||||||
|
testECDSAVerify(t, Secp256k1Keccak256)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testECDSAVerify(t *testing.T, curve NamedCurve) {
|
func testECDSAVerify(t *testing.T, curve NamedCurveHash) {
|
||||||
var (
|
var (
|
||||||
priv *keys.PrivateKey
|
priv *keys.PrivateKey
|
||||||
err error
|
err error
|
||||||
c = newCrypto()
|
c = newCrypto()
|
||||||
ic = &interop.Context{VM: vm.New()}
|
ic = &interop.Context{VM: vm.New()}
|
||||||
actual stackitem.Item
|
actual stackitem.Item
|
||||||
|
hasher HashFunc
|
||||||
)
|
)
|
||||||
switch curve {
|
switch curve {
|
||||||
case Secp256k1:
|
case Secp256k1Sha256:
|
||||||
priv, err = keys.NewSecp256k1PrivateKey()
|
priv, err = keys.NewSecp256k1PrivateKey()
|
||||||
case Secp256r1:
|
hasher = hash.Sha256
|
||||||
|
case Secp256r1Sha256:
|
||||||
priv, err = keys.NewPrivateKey()
|
priv, err = keys.NewPrivateKey()
|
||||||
|
hasher = hash.Sha256
|
||||||
|
case Secp256k1Keccak256:
|
||||||
|
priv, err = keys.NewSecp256k1PrivateKey()
|
||||||
|
hasher = Keccak256
|
||||||
|
case Secp256r1Keccak256:
|
||||||
|
priv, err = keys.NewPrivateKey()
|
||||||
|
hasher = Keccak256
|
||||||
default:
|
default:
|
||||||
t.Fatal("unknown curve")
|
t.Fatal("unknown curve/hash")
|
||||||
}
|
}
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -162,7 +178,7 @@ func testECDSAVerify(t *testing.T, curve NamedCurve) {
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := []byte("test message")
|
msg := []byte("test message")
|
||||||
sign := priv.Sign(msg)
|
sign := priv.SignHash(hasher(msg))
|
||||||
|
|
||||||
t.Run("bad message item", func(t *testing.T) {
|
t.Run("bad message item", func(t *testing.T) {
|
||||||
runCase(t, true, false, stackitem.NewInterop("cheburek"), priv.PublicKey().Bytes(), sign, int64(curve))
|
runCase(t, true, false, stackitem.NewInterop("cheburek"), priv.PublicKey().Bytes(), sign, int64(curve))
|
||||||
|
@ -254,3 +270,13 @@ func TestCryptolib_ScalarFromBytes_Compat(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestKeccak256(t *testing.T) {
|
||||||
|
input := []byte("hello")
|
||||||
|
data := Keccak256(input)
|
||||||
|
|
||||||
|
expected := "1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"
|
||||||
|
actual := hex.EncodeToString(data.BytesBE())
|
||||||
|
|
||||||
|
require.Equal(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
922
pkg/core/native/native_test/cryptolib_verification_test.go
Normal file
922
pkg/core/native/native_test/cryptolib_verification_test.go
Normal file
|
@ -0,0 +1,922 @@
|
||||||
|
package native_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/elliptic"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"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/core/native/nativenames"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
|
"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/io"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/neotest"
|
||||||
|
"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"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestCryptoLib_KoblitzVerificationScript builds transaction with custom witness that contains
|
||||||
|
// the Koblitz tx signature bytes and Koblitz signature verification script.
|
||||||
|
// This test ensures that transaction signed by Koblitz key passes verification and can
|
||||||
|
// be successfully accepted to the chain.
|
||||||
|
func TestCryptoLib_KoblitzVerificationScript(t *testing.T) {
|
||||||
|
check := func(
|
||||||
|
t *testing.T,
|
||||||
|
buildVerificationScript func(t *testing.T, pub *keys.PublicKey) []byte,
|
||||||
|
constructMsg func(t *testing.T, magic uint32, tx hash.Hashable) []byte,
|
||||||
|
) {
|
||||||
|
c := newGasClient(t)
|
||||||
|
gasInvoker := c.WithSigners(c.Committee)
|
||||||
|
e := c.Executor
|
||||||
|
|
||||||
|
// Consider the user that is able to sign txs only with Secp256k1 private key.
|
||||||
|
// Let this user build, sign and push a GAS transfer transaction from its account
|
||||||
|
// to some other account.
|
||||||
|
pk, err := keys.NewSecp256k1PrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Firstly, we need to build the N3 user's account address based on the user's public key.
|
||||||
|
// The address itself is Hash160 from the verification script corresponding to the user's public key.
|
||||||
|
// Since user's private key belongs to Koblitz curve, we can't use System.Crypto.CheckSig interop
|
||||||
|
// in the verification script. Likely, we have a 'verifyWithECDsa' method in native CriptoLib contract
|
||||||
|
// that is able to check Koblitz signature. So let's build custom verification script based on this call.
|
||||||
|
// The script should call 'verifyWithECDsa' method of native CriptoLib contract with Koblitz curve identifier
|
||||||
|
// and check the provided message signature against the user's Koblitz public key.
|
||||||
|
vrfBytes := buildVerificationScript(t, pk.PublicKey())
|
||||||
|
|
||||||
|
// Construct the user's account script hash. It's effectively a verification script hash.
|
||||||
|
from := hash.Hash160(vrfBytes)
|
||||||
|
|
||||||
|
// Supply this account with some initial balance so that the user is able to pay for his transactions.
|
||||||
|
gasInvoker.Invoke(t, true, "transfer", c.Committee.ScriptHash(), from, 10000_0000_0000, nil)
|
||||||
|
|
||||||
|
// Construct transaction that transfers 5 GAS from the user's account to some other account.
|
||||||
|
to := util.Uint160{1, 2, 3}
|
||||||
|
amount := 5
|
||||||
|
tx := gasInvoker.PrepareInvokeNoSign(t, "transfer", from, to, amount, nil)
|
||||||
|
tx.Signers = []transaction.Signer{
|
||||||
|
{
|
||||||
|
Account: from,
|
||||||
|
Scopes: transaction.CalledByEntry,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
neotest.AddNetworkFee(t, e.Chain, tx)
|
||||||
|
neotest.AddSystemFee(e.Chain, tx, -1)
|
||||||
|
|
||||||
|
// Add some more network fee to pay for the witness verification. This value may be calculated precisely,
|
||||||
|
// but let's keep some inaccurate value for the test.
|
||||||
|
tx.NetworkFee += 540_0000
|
||||||
|
|
||||||
|
// This transaction (along with the network magic) should be signed by the user's Koblitz private key.
|
||||||
|
msg := constructMsg(t, uint32(e.Chain.GetConfig().Magic), tx)
|
||||||
|
|
||||||
|
// The user has to sign the hash of the message by his Koblitz key.
|
||||||
|
// Please, note that this Keccak256 hash may easily be replaced by sha256 hash if needed.
|
||||||
|
signature := pk.SignHash(native.Keccak256(msg))
|
||||||
|
|
||||||
|
// Ensure that signature verification passes. This line here is just for testing purposes,
|
||||||
|
// it won't be present in the real code.
|
||||||
|
require.True(t, pk.PublicKey().Verify(signature, native.Keccak256(msg).BytesBE()))
|
||||||
|
|
||||||
|
// Build invocation witness script for the user's account.
|
||||||
|
invBytes := buildKoblitzInvocationScript(t, [][]byte{signature})
|
||||||
|
|
||||||
|
// Construct witness for signer #0 (the user itself).
|
||||||
|
tx.Scripts = []transaction.Witness{
|
||||||
|
{
|
||||||
|
InvocationScript: invBytes,
|
||||||
|
VerificationScript: vrfBytes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add transaction to the chain. No error is expected on new block addition. Note, that this line performs
|
||||||
|
// all those checks that are executed during transaction acceptance in the real network.
|
||||||
|
e.AddNewBlock(t, tx)
|
||||||
|
|
||||||
|
// Double-check: ensure funds have been transferred.
|
||||||
|
e.CheckGASBalance(t, to, big.NewInt(int64(amount)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// The proposed preferable witness verification script
|
||||||
|
// (110 bytes, 2154270 GAS including Invocation script execution).
|
||||||
|
// The user has to sign the keccak256([4-bytes-network-magic-LE, txHash-bytes-BE]).
|
||||||
|
check(t, buildKoblitzVerificationScript, constructMessage)
|
||||||
|
|
||||||
|
// Below presented some variations of verification scripts that were also considered, but
|
||||||
|
// they are not as good as the first one.
|
||||||
|
|
||||||
|
// The simplest witness verification script with low length and low execution cost
|
||||||
|
// (98 bytes, 2092530 GAS including Invocation script execution).
|
||||||
|
// The user has to sign the keccak256([var-bytes-network-magic, txHash-bytes-BE]).
|
||||||
|
check(t, buildKoblitzVerificationScriptSimpleSingleHash, constructMessageNoHash)
|
||||||
|
|
||||||
|
// Even more simple witness verification script with low length and low execution cost
|
||||||
|
// (95 bytes, 2092320 GAS including Invocation script execution).
|
||||||
|
// The user has to sign the keccak256([var-bytes-network-magic, txHash-bytes-BE]).
|
||||||
|
// The difference is that network magic is a static value, thus, both verification script and
|
||||||
|
// user address are network-specific.
|
||||||
|
check(t, buildKoblitzVerificationScriptSimpleSingleHashStaticMagic, constructMessageNoHash)
|
||||||
|
|
||||||
|
// More complicated verification script with higher length and higher execution cost
|
||||||
|
// (136 bytes, 4120620 GAS including Invocation script execution).
|
||||||
|
// The user has to sign the keccak256(sha256([var-bytes-network-magic, txHash-bytes-BE])).
|
||||||
|
check(t, buildKoblitzVerificationScriptSimple, constructMessageSimple)
|
||||||
|
|
||||||
|
// Witness verification script that follows the existing standard CheckSig account generation rules
|
||||||
|
// and has larger length and higher execution cost.
|
||||||
|
// (186 bytes, 5116020 GAS including Invocation script execution).
|
||||||
|
// The user has to sign the keccak256(sha256([4-bytes-network-magic-LE, txHash-bytes-BE]))
|
||||||
|
check(t, buildKoblitzVerificationScriptCompat, constructMessageCompat)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildKoblitzVerificationScript builds witness verification script for Koblitz public key.
|
||||||
|
// This method checks
|
||||||
|
//
|
||||||
|
// keccak256([4-bytes-network-magic-LE, txHash-bytes-BE])
|
||||||
|
//
|
||||||
|
// instead of (comparing with N3)
|
||||||
|
//
|
||||||
|
// sha256([4-bytes-network-magic-LE, txHash-bytes-BE]).
|
||||||
|
func buildKoblitzVerificationScript(t *testing.T, pub *keys.PublicKey) []byte {
|
||||||
|
criptoLibH := state.CreateNativeContractHash(nativenames.CryptoLib)
|
||||||
|
|
||||||
|
// vrf is witness verification script corresponding to the pub.
|
||||||
|
vrf := io.NewBufBinWriter()
|
||||||
|
emit.Int(vrf.BinWriter, int64(native.Secp256k1Keccak256)) // push Koblitz curve identifier.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.SWAP) // swap curve identifier with the signature.
|
||||||
|
emit.Bytes(vrf.BinWriter, pub.Bytes()) // emit the caller's public key.
|
||||||
|
// Construct and push the signed message. The signed message is effectively the network-dependent transaction hash,
|
||||||
|
// i.e. msg = [4-network-magic-bytes-LE, tx-hash-BE]
|
||||||
|
// Firstly, retrieve network magic (it's uint32 wrapped into BigInteger and represented as Integer stackitem on stack).
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetNetwork) // push network magic (Integer stackitem), can have 0-5 bytes length serialized.
|
||||||
|
// Convert network magic to 4-bytes-length LE byte array representation.
|
||||||
|
emit.Int(vrf.BinWriter, 0x100000000)
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.ADD, // some new number that is 5 bytes at least when serialized, but first 4 bytes are intact network value (LE).
|
||||||
|
opcode.PUSH4, opcode.LEFT) // cut the first 4 bytes out of a number that is at least 5 bytes long, the result is 4-bytes-length LE network representation.
|
||||||
|
// Retrieve executing transaction hash.
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetScriptContainer) // push the script container (executing transaction, actually).
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.PICKITEM) // pick 0-th transaction item (the transaction hash).
|
||||||
|
// Concatenate network magic and transaction hash.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.CAT) // this instruction will convert network magic to bytes using BigInteger rules of conversion.
|
||||||
|
// Continue construction of 'verifyWithECDsa' call.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH4, opcode.PACK) // pack arguments for 'verifyWithECDsa' call.
|
||||||
|
emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "verifyWithECDsa", callflag.NoneFlag) // emit the call to 'verifyWithECDsa' itself.
|
||||||
|
require.NoError(t, vrf.Err)
|
||||||
|
|
||||||
|
return vrf.Bytes()
|
||||||
|
// Here's an example of the resulting witness verification script (110 bytes length, always constant length, with constant length of signed data):
|
||||||
|
// NEO-GO-VM > loadbase64 ABhQDCECoIi/qx5LS+3n1GJFcoYbQByyDDsU6QaHvYhiJypOYWZBxfug4AMAAAAAAQAAAJ4UjUEtUQgwEM6LFMAfDA92ZXJpZnlXaXRoRUNEc2EMFBv1dasRiWiEE2EKNaEohs3gtmxyQWJ9W1I=
|
||||||
|
// READY: loaded 110 instructions
|
||||||
|
// NEO-GO-VM 0 > ops
|
||||||
|
// INDEX OPCODE PARAMETER
|
||||||
|
// 0 PUSHINT8 24 (18) <<
|
||||||
|
// 2 SWAP
|
||||||
|
// 3 PUSHDATA1 02a088bfab1e4b4bede7d4624572861b401cb20c3b14e90687bd8862272a4e6166
|
||||||
|
// 38 SYSCALL System.Runtime.GetNetwork (c5fba0e0)
|
||||||
|
// 43 PUSHINT64 4294967296 (0000000001000000)
|
||||||
|
// 52 ADD
|
||||||
|
// 53 PUSH4
|
||||||
|
// 54 LEFT
|
||||||
|
// 55 SYSCALL System.Runtime.GetScriptContainer (2d510830)
|
||||||
|
// 60 PUSH0
|
||||||
|
// 61 PICKITEM
|
||||||
|
// 62 CAT
|
||||||
|
// 63 PUSH4
|
||||||
|
// 64 PACK
|
||||||
|
// 65 PUSH0
|
||||||
|
// 66 PUSHDATA1 766572696679576974684543447361 ("verifyWithECDsa")
|
||||||
|
// 83 PUSHDATA1 1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
|
||||||
|
// 105 SYSCALL System.Contract.Call (627d5b52)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildKoblitzVerificationScriptSimpleSingleHash builds witness verification script for Koblitz public key.
|
||||||
|
// This method differs from buildKoblitzVerificationScriptCompat in that it checks
|
||||||
|
//
|
||||||
|
// keccak256([var-bytes-network-magic, txHash-bytes-BE])
|
||||||
|
//
|
||||||
|
// instead of (comparing with N3)
|
||||||
|
//
|
||||||
|
// sha256([4-bytes-network-magic-LE, txHash-bytes-BE]).
|
||||||
|
func buildKoblitzVerificationScriptSimpleSingleHash(t *testing.T, pub *keys.PublicKey) []byte {
|
||||||
|
criptoLibH := state.CreateNativeContractHash(nativenames.CryptoLib)
|
||||||
|
|
||||||
|
// vrf is witness verification script corresponding to the pub.
|
||||||
|
// vrf is witness verification script corresponding to the pk.
|
||||||
|
vrf := io.NewBufBinWriter()
|
||||||
|
emit.Int(vrf.BinWriter, int64(native.Secp256k1Keccak256)) // push Koblitz curve identifier.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.SWAP) // swap curve identifier with the signature.
|
||||||
|
emit.Bytes(vrf.BinWriter, pub.Bytes()) // emit the caller's public key.
|
||||||
|
// Construct and push the signed message. The signed message is effectively the network-dependent transaction hash,
|
||||||
|
// i.e. msg = [network-magic-bytes, tx.Hash()]
|
||||||
|
// Firstly, retrieve network magic (it's uint32 wrapped into BigInteger and represented as Integer stackitem on stack).
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetNetwork) // push network magic.
|
||||||
|
// Retrieve executing transaction hash.
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetScriptContainer) // push the script container (executing transaction, actually).
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.PICKITEM) // pick 0-th transaction item (the transaction hash).
|
||||||
|
// Concatenate network magic and transaction hash.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.CAT) // this instruction will convert network magic to bytes using BigInteger rules of conversion.
|
||||||
|
// Continue construction of 'verifyWithECDsa' call.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH4, opcode.PACK) // pack arguments for 'verifyWithECDsa' call.
|
||||||
|
emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "verifyWithECDsa", callflag.NoneFlag) // emit the call to 'verifyWithECDsa' itself.
|
||||||
|
require.NoError(t, vrf.Err)
|
||||||
|
|
||||||
|
return vrf.Bytes()
|
||||||
|
// Here's an example of the resulting witness verification script (98 bytes length, always constant length, with variable length of signed data):
|
||||||
|
// NEO-GO-VM > loadbase64 ABZQDCEDY9ekgSWnbN6m4JjJ8SjoKSDtQo5ftMrx1/gcFsrQwgVBxfug4EEtUQgwEM6LFMAfDA92ZXJpZnlXaXRoRUNEc2EMFBv1dasRiWiEE2EKNaEohs3gtmxyQWJ9W1I=
|
||||||
|
// READY: loaded 98 instructions
|
||||||
|
// NEO-GO-VM 0 > ops
|
||||||
|
// INDEX OPCODE PARAMETER
|
||||||
|
// 0 PUSHINT8 24 (18) <<
|
||||||
|
// 2 SWAP
|
||||||
|
// 3 PUSHDATA1 0363d7a48125a76cdea6e098c9f128e82920ed428e5fb4caf1d7f81c16cad0c205
|
||||||
|
// 38 SYSCALL System.Runtime.GetNetwork (c5fba0e0)
|
||||||
|
// 43 SYSCALL System.Runtime.GetScriptContainer (2d510830)
|
||||||
|
// 48 PUSH0
|
||||||
|
// 49 PICKITEM
|
||||||
|
// 50 CAT
|
||||||
|
// 51 PUSH4
|
||||||
|
// 52 PACK
|
||||||
|
// 53 PUSH0
|
||||||
|
// 54 PUSHDATA1 766572696679576974684543447361 ("verifyWithECDsa")
|
||||||
|
// 71 PUSHDATA1 1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
|
||||||
|
// 93 SYSCALL System.Contract.Call (627d5b52)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildKoblitzVerificationScriptSimpleSingleHashStaticMagic builds witness verification script for Koblitz public key.
|
||||||
|
// This method differs from buildKoblitzVerificationScriptCompat in that it checks
|
||||||
|
//
|
||||||
|
// keccak256([var-bytes-network-magic, txHash-bytes-BE])
|
||||||
|
//
|
||||||
|
// instead of (comparing with N3)
|
||||||
|
//
|
||||||
|
// sha256([4-bytes-network-magic-LE, txHash-bytes-BE]).
|
||||||
|
//
|
||||||
|
// and it uses static magic value (simple PUSHINT* + magic, or PUSHDATA1 + magicBytes is also possible)
|
||||||
|
// which results in network-specific verification script and, consequently, network-specific user address.
|
||||||
|
func buildKoblitzVerificationScriptSimpleSingleHashStaticMagic(t *testing.T, pub *keys.PublicKey) []byte {
|
||||||
|
criptoLibH := state.CreateNativeContractHash(nativenames.CryptoLib)
|
||||||
|
|
||||||
|
// vrf is witness verification script corresponding to the pub.
|
||||||
|
// vrf is witness verification script corresponding to the pk.
|
||||||
|
vrf := io.NewBufBinWriter()
|
||||||
|
emit.Int(vrf.BinWriter, int64(native.Secp256k1Keccak256)) // push Koblitz curve identifier.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.SWAP) // swap curve identifier with the signature.
|
||||||
|
emit.Bytes(vrf.BinWriter, pub.Bytes()) // emit the caller's public key.
|
||||||
|
// Construct and push the signed message. The signed message is effectively the network-dependent transaction hash,
|
||||||
|
// i.e. msg = [network-magic-bytes, tx.Hash()]
|
||||||
|
// Firstly, push static network magic (it's 42 for unit test chain).
|
||||||
|
emit.Int(vrf.BinWriter, 42)
|
||||||
|
// Retrieve executing transaction hash.
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetScriptContainer) // push the script container (executing transaction, actually).
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.PICKITEM) // pick 0-th transaction item (the transaction hash).
|
||||||
|
// Concatenate network magic and transaction hash.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.CAT) // this instruction will convert network magic to bytes using BigInteger rules of conversion.
|
||||||
|
// Continue construction of 'verifyWithECDsa' call.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH4, opcode.PACK) // pack arguments for 'verifyWithECDsa' call.
|
||||||
|
emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "verifyWithECDsa", callflag.NoneFlag) // emit the call to 'verifyWithECDsa' itself.
|
||||||
|
require.NoError(t, vrf.Err)
|
||||||
|
|
||||||
|
return vrf.Bytes()
|
||||||
|
// Here's an example of the resulting witness verification script (95 bytes length, always constant length, with variable length of signed data):
|
||||||
|
// NEO-GO-VM > loadbase64 ABZQDCECluEwgK3pKiq3IjOMKiSe6Ng6FPZJxoMhZkFl8GvREL0AKkEtUQgwEM6LFMAfDA92ZXJpZnlXaXRoRUNEc2EMFBv1dasRiWiEE2EKNaEohs3gtmxyQWJ9W1I=
|
||||||
|
// READY: loaded 95 instructions
|
||||||
|
// NEO-GO-VM 0 > ops
|
||||||
|
// INDEX OPCODE PARAMETER
|
||||||
|
// 0 PUSHINT8 24 (18) <<
|
||||||
|
// 2 SWAP
|
||||||
|
// 3 PUSHDATA1 0296e13080ade92a2ab722338c2a249ee8d83a14f649c68321664165f06bd110bd
|
||||||
|
// 38 PUSHINT8 42 (2a)
|
||||||
|
// 40 SYSCALL System.Runtime.GetScriptContainer (2d510830)
|
||||||
|
// 45 PUSH0
|
||||||
|
// 46 PICKITEM
|
||||||
|
// 47 CAT
|
||||||
|
// 48 PUSH4
|
||||||
|
// 49 PACK
|
||||||
|
// 50 PUSH0
|
||||||
|
// 51 PUSHDATA1 766572696679576974684543447361 ("verifyWithECDsa")
|
||||||
|
// 68 PUSHDATA1 1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
|
||||||
|
// 90 SYSCALL System.Contract.Call (627d5b52)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildKoblitzVerificationScriptSimple builds witness verification script for Koblitz public key.
|
||||||
|
// This method differs from buildKoblitzVerificationScriptCompat in that it checks
|
||||||
|
//
|
||||||
|
// keccak256(sha256([var-bytes-network-magic, txHash-bytes-BE]))
|
||||||
|
//
|
||||||
|
// instead of (comparing with N3)
|
||||||
|
//
|
||||||
|
// sha256([4-bytes-network-magic-LE, txHash-bytes-BE]).
|
||||||
|
//
|
||||||
|
// It produces constant-length verification script (136 bytes) independently of the network parameters.
|
||||||
|
// However, the length of signed message is variable and depends on the network magic (since network
|
||||||
|
// magic Integer stackitem being converted to Buffer has the resulting byte slice length that depends on
|
||||||
|
// the magic).
|
||||||
|
func buildKoblitzVerificationScriptSimple(t *testing.T, pub *keys.PublicKey) []byte {
|
||||||
|
criptoLibH := state.CreateNativeContractHash(nativenames.CryptoLib)
|
||||||
|
|
||||||
|
// vrf is witness verification script corresponding to the pub.
|
||||||
|
// vrf is witness verification script corresponding to the pk.
|
||||||
|
vrf := io.NewBufBinWriter()
|
||||||
|
emit.Int(vrf.BinWriter, int64(native.Secp256k1Keccak256)) // push Koblitz curve identifier.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.SWAP) // swap curve identifier with the signature.
|
||||||
|
emit.Bytes(vrf.BinWriter, pub.Bytes()) // emit the caller's public key.
|
||||||
|
// Construct and push the signed message. The signed message is effectively the network-dependent transaction hash,
|
||||||
|
// i.e. msg = Sha256([network-magic-bytes, tx.Hash()])
|
||||||
|
// Firstly, retrieve network magic (it's uint32 wrapped into BigInteger and represented as Integer stackitem on stack).
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetNetwork) // push network magic.
|
||||||
|
// Retrieve executing transaction hash.
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetScriptContainer) // push the script container (executing transaction, actually).
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.PICKITEM, // pick 0-th transaction item (the transaction hash).
|
||||||
|
opcode.CAT, // concatenate network magic and transaction hash; this instruction will convert network magic to bytes using BigInteger rules of conversion.
|
||||||
|
opcode.PUSH1, // push 1 (the number of arguments of 'sha256' method of native CryptoLib).
|
||||||
|
opcode.PACK) // pack arguments for 'sha256' call.
|
||||||
|
emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "sha256", callflag.NoneFlag) // emit the call to 'sha256' itself.
|
||||||
|
// Continue construction of 'verifyWithECDsa' call.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH4, opcode.PACK) // pack arguments for 'verifyWithECDsa' call.
|
||||||
|
emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "verifyWithECDsa", callflag.NoneFlag) // emit the call to 'verifyWithECDsa' itself.
|
||||||
|
require.NoError(t, vrf.Err)
|
||||||
|
|
||||||
|
return vrf.Bytes()
|
||||||
|
// Here's an example of the resulting witness verification script (136 bytes length, always constant length, with variable length of signed data):
|
||||||
|
// NEO-GO-VM 0 > loadbase64 ABZQDCEDp38Tevu0to16RQqloo/jNfgExYmoCElLS2JuuYcH831Bxfug4EEtUQgwEM6LEcAfDAZzaGEyNTYMFBv1dasRiWiEE2EKNaEohs3gtmxyQWJ9W1IUwB8MD3ZlcmlmeVdpdGhFQ0RzYQwUG/V1qxGJaIQTYQo1oSiGzeC2bHJBYn1bUg==
|
||||||
|
// READY: loaded 136 instructions
|
||||||
|
// NEO-GO-VM 0 > ops
|
||||||
|
// INDEX OPCODE PARAMETER
|
||||||
|
// 0 PUSHINT8 24 (18) <<
|
||||||
|
// 2 SWAP
|
||||||
|
// 3 PUSHDATA1 03a77f137afbb4b68d7a450aa5a28fe335f804c589a808494b4b626eb98707f37d
|
||||||
|
// 38 SYSCALL System.Runtime.GetNetwork (c5fba0e0)
|
||||||
|
// 43 SYSCALL System.Runtime.GetScriptContainer (2d510830)
|
||||||
|
// 48 PUSH0
|
||||||
|
// 49 PICKITEM
|
||||||
|
// 50 CAT
|
||||||
|
// 51 PUSH1
|
||||||
|
// 52 PACK
|
||||||
|
// 53 PUSH0
|
||||||
|
// 54 PUSHDATA1 736861323536 ("sha256")
|
||||||
|
// 62 PUSHDATA1 1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
|
||||||
|
// 84 SYSCALL System.Contract.Call (627d5b52)
|
||||||
|
// 89 PUSH4
|
||||||
|
// 90 PACK
|
||||||
|
// 91 PUSH0
|
||||||
|
// 92 PUSHDATA1 766572696679576974684543447361 ("verifyWithECDsa")
|
||||||
|
// 109 PUSHDATA1 1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
|
||||||
|
// 131 SYSCALL System.Contract.Call (627d5b52)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildKoblitzVerificationScript builds custom verification script for the provided Koblitz public key.
|
||||||
|
// It checks that the following message is signed by the provided public key:
|
||||||
|
//
|
||||||
|
// keccak256(sha256([4-bytes-network-magic-LE, txHash-bytes-BE]))
|
||||||
|
//
|
||||||
|
// It produces constant-length verification script (186 bytes) independently of the network parameters.
|
||||||
|
func buildKoblitzVerificationScriptCompat(t *testing.T, pub *keys.PublicKey) []byte {
|
||||||
|
criptoLibH := state.CreateNativeContractHash(nativenames.CryptoLib)
|
||||||
|
|
||||||
|
// vrf is witness verification script corresponding to the pub.
|
||||||
|
vrf := io.NewBufBinWriter()
|
||||||
|
emit.Int(vrf.BinWriter, int64(native.Secp256k1Keccak256)) // push Koblitz curve identifier.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.SWAP) // swap curve identifier with the signature.
|
||||||
|
emit.Bytes(vrf.BinWriter, pub.Bytes()) // emit the caller's public key.
|
||||||
|
// Construct and push the signed message. The signed message is effectively the network-dependent transaction hash,
|
||||||
|
// i.e. msg = Sha256([4-bytes-network-magic-LE, tx.Hash()])
|
||||||
|
// Firstly, convert network magic (uint32) to LE buffer.
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetNetwork) // push network magic.
|
||||||
|
// First byte: n & 0xFF
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.DUP)
|
||||||
|
emit.Int(vrf.BinWriter, 0xFF) // TODO: this can be optimize in order not to allocate 0xFF every time, but need to compare execution price.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.AND,
|
||||||
|
opcode.SWAP, // Swap with the original network n.
|
||||||
|
opcode.PUSH8,
|
||||||
|
opcode.SHR)
|
||||||
|
// Second byte: n >> 8 & 0xFF
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.DUP)
|
||||||
|
emit.Int(vrf.BinWriter, 0xFF)
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.AND,
|
||||||
|
opcode.SWAP, // Swap with the n >> 8.
|
||||||
|
opcode.PUSH8,
|
||||||
|
opcode.SHR)
|
||||||
|
// Third byte: n >> 16 & 0xFF
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.DUP)
|
||||||
|
emit.Int(vrf.BinWriter, 0xFF)
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.AND,
|
||||||
|
opcode.SWAP, // Swap with the n >> 16.
|
||||||
|
opcode.PUSH8,
|
||||||
|
opcode.SHR)
|
||||||
|
// Fourth byte: n >> 24 & 0xFF
|
||||||
|
emit.Int(vrf.BinWriter, 0xFF) // no DUP is needed since it's the last shift.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.AND)
|
||||||
|
// Put these 4 bytes into buffer.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH4, opcode.NEWBUFFER) // allocate new 4-bytes-length buffer.
|
||||||
|
emit.Opcodes(vrf.BinWriter,
|
||||||
|
// Set fourth byte.
|
||||||
|
opcode.DUP, opcode.PUSH3,
|
||||||
|
opcode.PUSH3, opcode.ROLL,
|
||||||
|
opcode.SETITEM,
|
||||||
|
// Set third byte.
|
||||||
|
opcode.DUP, opcode.PUSH2,
|
||||||
|
opcode.PUSH3, opcode.ROLL,
|
||||||
|
opcode.SETITEM,
|
||||||
|
// Set second byte.
|
||||||
|
opcode.DUP, opcode.PUSH1,
|
||||||
|
opcode.PUSH3, opcode.ROLL,
|
||||||
|
opcode.SETITEM,
|
||||||
|
// Set first byte.
|
||||||
|
opcode.DUP, opcode.PUSH0,
|
||||||
|
opcode.PUSH3, opcode.ROLL,
|
||||||
|
opcode.SETITEM)
|
||||||
|
// Retrieve executing transaction hash.
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetScriptContainer) // push the script container (executing transaction, actually).
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.PICKITEM, // pick 0-th transaction item (the transaction hash).
|
||||||
|
opcode.CAT, // concatenate network magic and transaction hash.
|
||||||
|
opcode.PUSH1, // push 1 (the number of arguments of 'sha256' method of native CryptoLib).
|
||||||
|
opcode.PACK) // pack arguments for 'sha256' call.
|
||||||
|
emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "sha256", callflag.NoneFlag) // emit the call to 'sha256' itself.
|
||||||
|
// Continue construction of 'verifyWithECDsa' call.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH4, opcode.PACK) // pack arguments for 'verifyWithECDsa' call.
|
||||||
|
emit.AppCallNoArgs(vrf.BinWriter, criptoLibH, "verifyWithECDsa", callflag.NoneFlag) // emit the call to 'verifyWithECDsa' itself.
|
||||||
|
require.NoError(t, vrf.Err)
|
||||||
|
|
||||||
|
return vrf.Bytes()
|
||||||
|
// Here's an example of the resulting witness verification script (186 bytes length, always constant length, the length of signed data is also always constant):
|
||||||
|
// NEO-GO-VM 0 > loadbase64 ABZQDCECYn75w2MePMuPvExbbEnjjM7eWnmvseGwcI+7lYp4AtdBxfug4EoB/wCRUBipSgH/AJFQGKlKAf8AkVAYqQH/AJEUiEoTE1LQShITUtBKERNS0EoQE1LQQS1RCDAQzosRwB8MBnNoYTI1NgwUG/V1qxGJaIQTYQo1oSiGzeC2bHJBYn1bUhTAHwwPdmVyaWZ5V2l0aEVDRHNhDBQb9XWrEYlohBNhCjWhKIbN4LZsckFifVtS
|
||||||
|
// READY: loaded 186 instructions
|
||||||
|
// NEO-GO-VM 0 > ops
|
||||||
|
// INDEX OPCODE PARAMETER
|
||||||
|
// 0 PUSHINT8 24 (18) <<
|
||||||
|
// 2 SWAP
|
||||||
|
// 3 PUSHDATA1 02627ef9c3631e3ccb8fbc4c5b6c49e38ccede5a79afb1e1b0708fbb958a7802d7
|
||||||
|
// 38 SYSCALL System.Runtime.GetNetwork (c5fba0e0)
|
||||||
|
// 43 DUP
|
||||||
|
// 44 PUSHINT16 255 (ff00)
|
||||||
|
// 47 AND
|
||||||
|
// 48 SWAP
|
||||||
|
// 49 PUSH8
|
||||||
|
// 50 SHR
|
||||||
|
// 51 DUP
|
||||||
|
// 52 PUSHINT16 255 (ff00)
|
||||||
|
// 55 AND
|
||||||
|
// 56 SWAP
|
||||||
|
// 57 PUSH8
|
||||||
|
// 58 SHR
|
||||||
|
// 59 DUP
|
||||||
|
// 60 PUSHINT16 255 (ff00)
|
||||||
|
// 63 AND
|
||||||
|
// 64 SWAP
|
||||||
|
// 65 PUSH8
|
||||||
|
// 66 SHR
|
||||||
|
// 67 PUSHINT16 255 (ff00)
|
||||||
|
// 70 AND
|
||||||
|
// 71 PUSH4
|
||||||
|
// 72 NEWBUFFER
|
||||||
|
// 73 DUP
|
||||||
|
// 74 PUSH3
|
||||||
|
// 75 PUSH3
|
||||||
|
// 76 ROLL
|
||||||
|
// 77 SETITEM
|
||||||
|
// 78 DUP
|
||||||
|
// 79 PUSH2
|
||||||
|
// 80 PUSH3
|
||||||
|
// 81 ROLL
|
||||||
|
// 82 SETITEM
|
||||||
|
// 83 DUP
|
||||||
|
// 84 PUSH1
|
||||||
|
// 85 PUSH3
|
||||||
|
// 86 ROLL
|
||||||
|
// 87 SETITEM
|
||||||
|
// 88 DUP
|
||||||
|
// 89 PUSH0
|
||||||
|
// 90 PUSH3
|
||||||
|
// 91 ROLL
|
||||||
|
// 92 SETITEM
|
||||||
|
// 93 SYSCALL System.Runtime.GetScriptContainer (2d510830)
|
||||||
|
// 98 PUSH0
|
||||||
|
// 99 PICKITEM
|
||||||
|
// 100 CAT
|
||||||
|
// 101 PUSH1
|
||||||
|
// 102 PACK
|
||||||
|
// 103 PUSH0
|
||||||
|
// 104 PUSHDATA1 736861323536 ("sha256")
|
||||||
|
// 112 PUSHDATA1 1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
|
||||||
|
// 134 SYSCALL System.Contract.Call (627d5b52)
|
||||||
|
// 139 PUSH4
|
||||||
|
// 140 PACK
|
||||||
|
// 141 PUSH0
|
||||||
|
// 142 PUSHDATA1 766572696679576974684543447361 ("verifyWithECDsa")
|
||||||
|
// 159 PUSHDATA1 1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
|
||||||
|
// 181 SYSCALL System.Contract.Call (627d5b52)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildKoblitzInvocationScript builds witness invocation script for the transaction signatures. The signature
|
||||||
|
// itself may be produced by public key over any curve (not required Koblitz, the algorithm is the same).
|
||||||
|
// The signatures expected to be sorted by public key (if multiple signatures are provided).
|
||||||
|
func buildKoblitzInvocationScript(t *testing.T, signatures [][]byte) []byte {
|
||||||
|
//Exactly like during standard
|
||||||
|
// signature verification, the resulting script pushes Koblitz signature bytes onto stack.
|
||||||
|
inv := io.NewBufBinWriter()
|
||||||
|
for _, sig := range signatures {
|
||||||
|
emit.Bytes(inv.BinWriter, sig) // message signature bytes.
|
||||||
|
}
|
||||||
|
require.NoError(t, inv.Err)
|
||||||
|
|
||||||
|
return inv.Bytes()
|
||||||
|
// Here's an example of the resulting single witness invocation script (66 bytes length, always constant length):
|
||||||
|
// NEO-GO-VM > loadbase64 DEBMGKU/MdSizlzaVNDUUbd1zMZQJ43eTaZ4vBCpmkJ/wVh1TYrAWEbFyHhkqq+aYxPCUS43NKJdJTXavcjB8sTP
|
||||||
|
// READY: loaded 66 instructions
|
||||||
|
// NEO-GO-VM 0 > ops
|
||||||
|
// INDEX OPCODE PARAMETER
|
||||||
|
// 0 PUSHDATA1 4c18a53f31d4a2ce5cda54d0d451b775ccc650278dde4da678bc10a99a427fc158754d8ac05846c5c87864aaaf9a6313c2512e3734a25d2535dabdc8c1f2c4cf <<
|
||||||
|
//
|
||||||
|
// Here's an example of the 3 out of 4 multisignature invocation script (66 * m bytes length, always constant length):
|
||||||
|
// NEO-GO-VM > loadbase64 DEBsPMY3+7sWyZf0gCVcqPzwZ79p+KpeylgtbYIrXp4Tdi6E/8q3DIrEgK7DdVe3YdbfE+VPrpwym/ufBb8MRTB6DED5B9OZDGWdJApRfuy9LeUTa2mLsXP7mBRa181g0Jo7beylWzVgDqHHF2PilECMcLmRbFRknmQm4KgiGkDE+O6ZDEAYt61O2dMfasJHiQD95M5b4mR6NBnDsMTo2e59H3y4YguroVLiUxnQSc4qu9LWvEIKr4/ytjCCuANXOkJmSw8C
|
||||||
|
// READY: loaded 198 instructions
|
||||||
|
// NEO-GO-VM 0 > ops
|
||||||
|
// INDEX OPCODE PARAMETER
|
||||||
|
// 0 PUSHDATA1 6c3cc637fbbb16c997f480255ca8fcf067bf69f8aa5eca582d6d822b5e9e13762e84ffcab70c8ac480aec37557b761d6df13e54fae9c329bfb9f05bf0c45307a <<
|
||||||
|
// 66 PUSHDATA1 f907d3990c659d240a517eecbd2de5136b698bb173fb98145ad7cd60d09a3b6deca55b35600ea1c71763e294408c70b9916c54649e6426e0a8221a40c4f8ee99
|
||||||
|
// 132 PUSHDATA1 18b7ad4ed9d31f6ac2478900fde4ce5be2647a3419c3b0c4e8d9ee7d1f7cb8620baba152e25319d049ce2abbd2d6bc420aaf8ff2b63082b803573a42664b0f02
|
||||||
|
}
|
||||||
|
|
||||||
|
// constructMessage constructs message for signing that consists of the
|
||||||
|
// unhashed constant 4-bytes length LE magic and transaction hash bytes:
|
||||||
|
//
|
||||||
|
// [4-bytes-network-magic-LE, txHash-bytes-BE]
|
||||||
|
func constructMessage(t *testing.T, magic uint32, tx hash.Hashable) []byte {
|
||||||
|
return hash.GetSignedData(magic, tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// constructMessageNoHash constructs message for signing that consists of the
|
||||||
|
// unhashed magic and transaction hash bytes:
|
||||||
|
//
|
||||||
|
// [var-bytes-network-magic, txHash-bytes-BE]
|
||||||
|
func constructMessageNoHash(t *testing.T, magic uint32, tx hash.Hashable) []byte {
|
||||||
|
m := big.NewInt(int64(magic))
|
||||||
|
return append(m.Bytes(), tx.Hash().BytesBE()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// constructMessageCompat constructs message for signing that does not follow N3 rules,
|
||||||
|
// but entails smaller verification script size and smaller verification price:
|
||||||
|
//
|
||||||
|
// sha256([var-bytes-network-magic, txHash-bytes-BE])
|
||||||
|
func constructMessageSimple(t *testing.T, magic uint32, tx hash.Hashable) []byte {
|
||||||
|
m := big.NewInt(int64(magic))
|
||||||
|
return hash.Sha256(append(m.Bytes(), tx.Hash().BytesBE()...)).BytesBE()
|
||||||
|
}
|
||||||
|
|
||||||
|
// constructMessageCompat constructs message for signing following the N3 rules:
|
||||||
|
//
|
||||||
|
// sha256([4-bytes-network-magic-LE, txHash-bytes-BE])
|
||||||
|
func constructMessageCompat(t *testing.T, magic uint32, tx hash.Hashable) []byte {
|
||||||
|
return hash.NetSha256(magic, tx).BytesBE()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPubFromBytes(t *testing.T) {
|
||||||
|
priv, err := keys.NewSecp256k1PrivateKey() // key on Koblitz
|
||||||
|
require.NoError(t, err)
|
||||||
|
fmt.Println(hex.EncodeToString(priv.Bytes()))
|
||||||
|
fmt.Println(hex.EncodeToString(priv.PublicKey().UncompressedBytes()))
|
||||||
|
resGo, err := keys.NewPublicKeyFromBytes(priv.PublicKey().Bytes(), elliptic.P256()) // restore with Secp256r1
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, resGo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCryptoLib_KoblitzMultisigVerificationScript builds transaction with custom witness that contains
|
||||||
|
// the Koblitz tx multisignature bytes and Koblitz multisignature verification script.
|
||||||
|
// This test ensures that transaction signed by m out of n Koblitz keys passes verification and can
|
||||||
|
// be successfully accepted to the chain.
|
||||||
|
func TestCryptoLib_KoblitzMultisigVerificationScript(t *testing.T) {
|
||||||
|
check := func(
|
||||||
|
t *testing.T,
|
||||||
|
buildVerificationScript func(t *testing.T, m int, pub keys.PublicKeys) []byte,
|
||||||
|
constructMsg func(t *testing.T, magic uint32, tx hash.Hashable) []byte,
|
||||||
|
) {
|
||||||
|
c := newGasClient(t)
|
||||||
|
gasInvoker := c.WithSigners(c.Committee)
|
||||||
|
e := c.Executor
|
||||||
|
|
||||||
|
// Consider 4 users willing to sign 3/4 multisignature transaction Secp256k1 private keys.
|
||||||
|
const (
|
||||||
|
n = 1
|
||||||
|
m = 1
|
||||||
|
)
|
||||||
|
pks := make([]*keys.PrivateKey, n)
|
||||||
|
// Bad key:
|
||||||
|
//11d82a8a4dd5a1636f8357f4d9e8c19ec41be24c951ee8413c7512d9f53a5676
|
||||||
|
//04535528c9a786d15bfb6c95a0699a5dbf3c991bed97a505caa89313bdb00a289e92904f29f1b41753629ae64c59d2d248b53ad7a2801121fe79baaf1c99b8355f
|
||||||
|
privBytes, err := hex.DecodeString("11d82a8a4dd5a1636f8357f4d9e8c19ec41be24c951ee8413c7512d9f53a5676")
|
||||||
|
require.NoError(t, err)
|
||||||
|
pks[0], err = keys.NewPrivateKoblitzKeyFromBytes(privBytes)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, hex.EncodeToString(pks[0].PublicKey().Bytes()), "04535528c9a786d15bfb6c95a0699a5dbf3c991bed97a505caa89313bdb00a289e92904f29f1b41753629ae64c59d2d248b53ad7a2801121fe79baaf1c99b8355f")
|
||||||
|
/*for i := range pks {
|
||||||
|
var err error
|
||||||
|
pks[i], err = keys.NewSecp256k1PrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// Sort private keys by their public keys.
|
||||||
|
sort.Slice(pks, func(i, j int) bool {
|
||||||
|
return pks[i].PublicKey().Cmp(pks[j].PublicKey()) < 0
|
||||||
|
})
|
||||||
|
|
||||||
|
// Firstly, we need to build the N3 multisig account address based on the users' public keys.
|
||||||
|
// Pubs must be sorted, exactly like for the standard CheckMultisig.
|
||||||
|
pubs := make(keys.PublicKeys, n)
|
||||||
|
for i := range pks {
|
||||||
|
pubs[i] = pks[i].PublicKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
//vrfBytes := buildVerificationScript(t, m, pubs)
|
||||||
|
vrfBytes, err := smartcontract.CreateMultiSigRedeemScript(1, pubs)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Construct the user's account script hash. It's effectively a verification script hash.
|
||||||
|
from := hash.Hash160(vrfBytes)
|
||||||
|
|
||||||
|
// Supply this account with some initial balance so that the user is able to pay for his transactions.
|
||||||
|
gasInvoker.Invoke(t, true, "transfer", c.Committee.ScriptHash(), from, 10000_0000_0000, nil)
|
||||||
|
|
||||||
|
// Construct transaction that transfers 5 GAS from the user's account to some other account.
|
||||||
|
to := util.Uint160{1, 2, 3}
|
||||||
|
amount := 5
|
||||||
|
tx := gasInvoker.PrepareInvokeNoSign(t, "transfer", from, to, amount, nil)
|
||||||
|
tx.Signers = []transaction.Signer{
|
||||||
|
{
|
||||||
|
Account: from,
|
||||||
|
Scopes: transaction.CalledByEntry,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
neotest.AddNetworkFee(t, e.Chain, tx)
|
||||||
|
neotest.AddSystemFee(e.Chain, tx, -1)
|
||||||
|
|
||||||
|
// Add some more network fee to pay for the witness verification. This value may be calculated precisely,
|
||||||
|
// but let's keep some inaccurate value for the test.
|
||||||
|
tx.NetworkFee = 8995470
|
||||||
|
|
||||||
|
// This transaction (along with the network magic) should be signed by the user's Koblitz private key.
|
||||||
|
msg := constructMsg(t, uint32(e.Chain.GetConfig().Magic), tx)
|
||||||
|
|
||||||
|
// The users have to sign the hash of the message by their Koblitz key. Collect m signatures from first m keys.
|
||||||
|
// Signatures must be sorted by public key.
|
||||||
|
sigs := make([][]byte, m)
|
||||||
|
for i := range sigs {
|
||||||
|
j := i
|
||||||
|
if i > 0 {
|
||||||
|
j++ // Add some shift to ensure that verification script works correctly.
|
||||||
|
}
|
||||||
|
if i > 3 {
|
||||||
|
j++ // Add more shift for large number of public keys for the same purpose.
|
||||||
|
}
|
||||||
|
sigs[i] = pks[j].SignHash(native.Keccak256(msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build invocation witness script for the signatures.
|
||||||
|
invBytes := buildKoblitzInvocationScript(t, sigs)
|
||||||
|
|
||||||
|
// Construct witness for signer #0 (the multisig account itself).
|
||||||
|
tx.Scripts = []transaction.Witness{
|
||||||
|
{
|
||||||
|
InvocationScript: invBytes,
|
||||||
|
VerificationScript: vrfBytes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(base64.StdEncoding.EncodeToString(vrfBytes))
|
||||||
|
|
||||||
|
// Add transaction to the chain. No error is expected on new block addition. Note, that this line performs
|
||||||
|
// all those checks that are executed during transaction acceptance in the real network.
|
||||||
|
e.AddNewBlock(t, tx)
|
||||||
|
|
||||||
|
// Double-check: ensure funds have been transferred.
|
||||||
|
e.CheckGASBalance(t, to, big.NewInt(int64(amount)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// The proposed multisig verification script.
|
||||||
|
// (264 bytes, 8390070 GAS including Invocation script execution for 3/4 multisig).
|
||||||
|
// The user has to sign the keccak256([4-bytes-network-magic-LE, txHash-bytes-BE]).
|
||||||
|
check(t, buildKoblitzMultisigVerificationScript, constructMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildKoblitzMultisigVerificationScript builds witness verification script for m signatures out of n Koblitz public keys.
|
||||||
|
// Public keys must be sorted. Signatures (pushed by witness Invocation script) must be sorted by public keys.
|
||||||
|
// It checks m out of n multisignature of the following message:
|
||||||
|
//
|
||||||
|
// keccak256([4-bytes-network-magic-LE, txHash-bytes-BE])
|
||||||
|
func buildKoblitzMultisigVerificationScript(t *testing.T, m int, pubs keys.PublicKeys) []byte {
|
||||||
|
if len(pubs) == 0 {
|
||||||
|
t.Fatalf("empty pubs list")
|
||||||
|
}
|
||||||
|
if m > len(pubs) {
|
||||||
|
t.Fatalf("m must be not greater than the number of public keys")
|
||||||
|
}
|
||||||
|
|
||||||
|
n := len(pubs) // public keys must be sorted.
|
||||||
|
cryptoLibH := state.CreateNativeContractHash(nativenames.CryptoLib)
|
||||||
|
|
||||||
|
// In fact, the following algorithm is implemented via NeoVM instructions:
|
||||||
|
//
|
||||||
|
// func Check(sigs []interop.Signature) bool {
|
||||||
|
// if m != len(sigs) {
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
// var pubs []interop.PublicKey = []interop.PublicKey{...}
|
||||||
|
// msg := append(convert.ToBytes(runtime.GetNetwork()), runtime.GetScriptContainer().Hash...)
|
||||||
|
// var sigCnt = 0
|
||||||
|
// var pubCnt = 0
|
||||||
|
// for ; sigCnt < m && pubCnt < n; { // sigs must be sorted by pub
|
||||||
|
// sigCnt += crypto.VerifyWithECDsa(msg, pubs[pubCnt], sigs[sigCnt], crypto.Secp256k1Keccak256)
|
||||||
|
// pubCnt++
|
||||||
|
// }
|
||||||
|
// return sigCnt == m
|
||||||
|
// }
|
||||||
|
vrf := io.NewBufBinWriter()
|
||||||
|
|
||||||
|
// Start the same way as regular multisig script.
|
||||||
|
emit.Int(vrf.BinWriter, int64(m)) // push m.
|
||||||
|
for _, pub := range pubs {
|
||||||
|
emit.Bytes(vrf.BinWriter, pub.Bytes()) // push public keys in compressed form.
|
||||||
|
}
|
||||||
|
emit.Int(vrf.BinWriter, int64(n)) // push n.
|
||||||
|
|
||||||
|
// Initialize slots for local variables. Locals slot scheme:
|
||||||
|
// LOC0 -> sigs
|
||||||
|
// LOC1 -> pubs
|
||||||
|
// LOC2 -> msg (ByteString)
|
||||||
|
// LOC3 -> sigCnt (Integer)
|
||||||
|
// LOC4 -> pubCnt (Integer)
|
||||||
|
// LOC5 -> n
|
||||||
|
// LOC6 -> m
|
||||||
|
emit.InitSlot(vrf.BinWriter, 7, 0)
|
||||||
|
|
||||||
|
// Store n.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.STLOC5)
|
||||||
|
|
||||||
|
// Pack public keys and store at LOC1.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.LDLOC5)
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PACK, opcode.STLOC1)
|
||||||
|
|
||||||
|
// Store m.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.STLOC6)
|
||||||
|
|
||||||
|
// Check the number of signatures is m. Abort the execution if not.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.DEPTH) // push the number of signatures onto stack.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.LDLOC6) // load m.
|
||||||
|
emit.Instruction(vrf.BinWriter, opcode.JMPEQ, []byte{0}) // here and below short jumps are sufficient.
|
||||||
|
sigsLenCheckEndOffset := vrf.Len() // offset of the signatures count check.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.ABORT) // abort execution if length of the signatures not equal to m.
|
||||||
|
|
||||||
|
// Start the check.
|
||||||
|
checkStartOffset := vrf.Len()
|
||||||
|
|
||||||
|
// Pack signatures and store at LOC0.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.LDLOC6) // load m.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PACK, opcode.STLOC0)
|
||||||
|
|
||||||
|
// Get message and store it at LOC2.
|
||||||
|
// msg = [4-network-magic-bytes-LE, tx-hash-BE]
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetNetwork) // push network magic (Integer stackitem), can have 0-5 bytes length serialized.
|
||||||
|
// Convert network magic to 4-bytes-length LE byte array representation.
|
||||||
|
emit.Int(vrf.BinWriter, 0x100000000)
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.ADD, // some new number that is 5 bytes at least when serialized, but first 4 bytes are intact network value (LE).
|
||||||
|
opcode.PUSH4, opcode.LEFT) // cut the first 4 bytes out of a number that is at least 5 bytes long, the result is 4-bytes-length LE network representation.
|
||||||
|
// Retrieve executing transaction hash.
|
||||||
|
emit.Syscall(vrf.BinWriter, interopnames.SystemRuntimeGetScriptContainer) // push the script container (executing transaction, actually).
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.PICKITEM) // pick 0-th transaction item (the transaction hash).
|
||||||
|
// Concatenate network magic and transaction hash.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.CAT) // this instruction will convert network magic to bytes using BigInteger rules of conversion.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.STLOC2) // store msg as a local variable #2.
|
||||||
|
|
||||||
|
// Initialize local variables: sigCnt, pubCnt.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.PUSH0, opcode.STLOC3, // initialize sigCnt.
|
||||||
|
opcode.PUSH0, opcode.STLOC4) // initialize pubCnt.
|
||||||
|
|
||||||
|
// Loop condition check.
|
||||||
|
loopStartOffset := vrf.Len()
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.LDLOC3) // load sigCnt.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.LDLOC6) // push m.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.GE, // sigCnt >= m
|
||||||
|
opcode.LDLOC4) // load pubCnt
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.LDLOC5) // push n.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.GE, // pubCnt >= n
|
||||||
|
opcode.OR) // sigCnt >= m || pubCnt >= n
|
||||||
|
emit.Instruction(vrf.BinWriter, opcode.JMPIF, []byte{0}) // jump to the end of the script if (sigCnt >= m || pubCnt >= n).
|
||||||
|
loopConditionOffset := vrf.Len()
|
||||||
|
|
||||||
|
// Loop start. Prepare arguments and call CryptoLib's verifyWithECDsa.
|
||||||
|
emit.Int(vrf.BinWriter, int64(native.Secp256k1Keccak256)) // push Koblitz curve identifier.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.LDLOC0, // load signatures.
|
||||||
|
opcode.LDLOC3, // load sigCnt.
|
||||||
|
opcode.PICKITEM, // pick signature at index sigCnt.
|
||||||
|
opcode.LDLOC1, // load pubs.
|
||||||
|
opcode.LDLOC4, // load pubCnt.
|
||||||
|
opcode.PICKITEM, // pick pub at index pubCnt.
|
||||||
|
opcode.LDLOC2, // load msg.
|
||||||
|
opcode.PUSH4, opcode.PACK) // pack 4 arguments for 'verifyWithECDsa' call.
|
||||||
|
emit.AppCallNoArgs(vrf.BinWriter, cryptoLibH, "verifyWithECDsa", callflag.NoneFlag) // emit the call to 'verifyWithECDsa' itself.
|
||||||
|
|
||||||
|
// Update loop variables.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.LDLOC3, opcode.ADD, opcode.STLOC3, // increment sigCnt if signature is valid.
|
||||||
|
opcode.LDLOC4, opcode.INC, opcode.STLOC4) // increment pubCnt.
|
||||||
|
|
||||||
|
// End of the loop.
|
||||||
|
emit.Instruction(vrf.BinWriter, opcode.JMP, []byte{0}) // jump to the start of cycle.
|
||||||
|
loopEndOffset := vrf.Len()
|
||||||
|
|
||||||
|
// Return condition: the number of valid signatures should be equal to m.
|
||||||
|
progRetOffset := vrf.Len()
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.LDLOC3) // load sigCnt.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.LDLOC6) // push m.
|
||||||
|
emit.Opcodes(vrf.BinWriter, opcode.NUMEQUAL) // push m == sigCnt.
|
||||||
|
|
||||||
|
require.NoError(t, vrf.Err)
|
||||||
|
script := vrf.Bytes()
|
||||||
|
|
||||||
|
// Set JMP* instructions offsets. "-1" is for short JMP parameter offset. JMP parameters
|
||||||
|
// are relative offsets.
|
||||||
|
script[sigsLenCheckEndOffset-1] = byte(checkStartOffset - sigsLenCheckEndOffset + 2)
|
||||||
|
script[loopEndOffset-1] = byte(loopStartOffset - loopEndOffset + 2)
|
||||||
|
script[loopConditionOffset-1] = byte(progRetOffset - loopConditionOffset + 2)
|
||||||
|
|
||||||
|
return script
|
||||||
|
// Here's an example of the resulting single witness invocation script (264 bytes length, the length may vary depending on m/n):
|
||||||
|
// NEO-GO-VM > loadbase64 EwwhAg1khs9yqTuG8R7dEj8/GhCqKwkL+6shSOczeaHENFo8DCECibz2wVNY1zRkRCbn+Qr87lQFjStnrQrwv1CSoea/91sMIQPiiV+wNGl5g5SVULR+BM/G2n6WO0WrGIsq+GBRqQHYwAwhAuwZz40NwnerrmSusUUgNqsZiv0WFj3KQE1BYd7lU7mDFFcHAHVtwHF2Q24oAzhuwHBBxfug4AMAAAAAAQAAAJ4UjUEtUQgwEM6LchBzEHRrbrhsbbiSJEIAGGhrzmlszmoUwB8MD3ZlcmlmeVdpdGhFQ0RzYQwUG/V1qxGJaIQTYQo1oSiGzeC2bHJBYn1bUmuec2ycdCK5a26z
|
||||||
|
// READY: loaded 264 instructions
|
||||||
|
// NEO-GO-VM 0 > ops
|
||||||
|
// INDEX OPCODE PARAMETER
|
||||||
|
// 0 PUSH3 <<
|
||||||
|
// 1 PUSHDATA1 020d6486cf72a93b86f11edd123f3f1a10aa2b090bfbab2148e73379a1c4345a3c
|
||||||
|
// 36 PUSHDATA1 0289bcf6c15358d734644426e7f90afcee54058d2b67ad0af0bf5092a1e6bff75b
|
||||||
|
// 71 PUSHDATA1 03e2895fb034697983949550b47e04cfc6da7e963b45ab188b2af86051a901d8c0
|
||||||
|
// 106 PUSHDATA1 02ec19cf8d0dc277abae64aeb1452036ab198afd16163dca404d4161dee553b983
|
||||||
|
// 141 PUSH4
|
||||||
|
// 142 INITSLOT 7 local, 0 arg
|
||||||
|
// 145 STLOC5
|
||||||
|
// 146 LDLOC5
|
||||||
|
// 147 PACK
|
||||||
|
// 148 STLOC1
|
||||||
|
// 149 STLOC6
|
||||||
|
// 150 DEPTH
|
||||||
|
// 151 LDLOC6
|
||||||
|
// 152 JMPEQ 155 (3/03)
|
||||||
|
// 154 ABORT
|
||||||
|
// 155 LDLOC6
|
||||||
|
// 156 PACK
|
||||||
|
// 157 STLOC0
|
||||||
|
// 158 SYSCALL System.Runtime.GetNetwork (c5fba0e0)
|
||||||
|
// 163 PUSHINT64 4294967296 (0000000001000000)
|
||||||
|
// 172 ADD
|
||||||
|
// 173 PUSH4
|
||||||
|
// 174 LEFT
|
||||||
|
// 175 SYSCALL System.Runtime.GetScriptContainer (2d510830)
|
||||||
|
// 180 PUSH0
|
||||||
|
// 181 PICKITEM
|
||||||
|
// 182 CAT
|
||||||
|
// 183 STLOC2
|
||||||
|
// 184 PUSH0
|
||||||
|
// 185 STLOC3
|
||||||
|
// 186 PUSH0
|
||||||
|
// 187 STLOC4
|
||||||
|
// 188 LDLOC3
|
||||||
|
// 189 LDLOC6
|
||||||
|
// 190 GE
|
||||||
|
// 191 LDLOC4
|
||||||
|
// 192 LDLOC5
|
||||||
|
// 193 GE
|
||||||
|
// 194 OR
|
||||||
|
// 195 JMPIF 261 (66/42)
|
||||||
|
// 197 PUSHINT8 24 (18)
|
||||||
|
// 199 LDLOC0
|
||||||
|
// 200 LDLOC3
|
||||||
|
// 201 PICKITEM
|
||||||
|
// 202 LDLOC1
|
||||||
|
// 203 LDLOC4
|
||||||
|
// 204 PICKITEM
|
||||||
|
// 205 LDLOC2
|
||||||
|
// 206 PUSH4
|
||||||
|
// 207 PACK
|
||||||
|
// 208 PUSH0
|
||||||
|
// 209 PUSHDATA1 766572696679576974684543447361 ("verifyWithECDsa")
|
||||||
|
// 226 PUSHDATA1 1bf575ab1189688413610a35a12886cde0b66c72 ("NNToUmdQBe5n8o53BTzjTFAnSEcpouyy3B", "0x726cb6e0cd8628a1350a611384688911ab75f51b")
|
||||||
|
// 248 SYSCALL System.Contract.Call (627d5b52)
|
||||||
|
// 253 LDLOC3
|
||||||
|
// 254 ADD
|
||||||
|
// 255 STLOC3
|
||||||
|
// 256 LDLOC4
|
||||||
|
// 257 INC
|
||||||
|
// 258 STLOC4
|
||||||
|
// 259 JMP 188 (-71/b9)
|
||||||
|
// 261 LDLOC3
|
||||||
|
// 262 LDLOC6
|
||||||
|
// 263 NUMEQUAL
|
||||||
|
}
|
|
@ -16,7 +16,10 @@ type Hashable interface {
|
||||||
Hash() util.Uint256
|
Hash() util.Uint256
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSignedData(net uint32, hh Hashable) []byte {
|
// GetSignedData returns the concatenated byte slice containing of the network
|
||||||
|
// magic in constant-length 4-bytes LE representation and hashable item hash in BE
|
||||||
|
// representation.
|
||||||
|
func GetSignedData(net uint32, hh Hashable) []byte {
|
||||||
var b = make([]byte, 4+util.Uint256Size)
|
var b = make([]byte, 4+util.Uint256Size)
|
||||||
binary.LittleEndian.PutUint32(b, net)
|
binary.LittleEndian.PutUint32(b, net)
|
||||||
h := hh.Hash()
|
h := hh.Hash()
|
||||||
|
@ -27,7 +30,7 @@ func getSignedData(net uint32, hh Hashable) []byte {
|
||||||
// NetSha256 calculates a network-specific hash of the Hashable item that can then
|
// NetSha256 calculates a network-specific hash of the Hashable item that can then
|
||||||
// be signed/verified.
|
// be signed/verified.
|
||||||
func NetSha256(net uint32, hh Hashable) util.Uint256 {
|
func NetSha256(net uint32, hh Hashable) util.Uint256 {
|
||||||
return Sha256(getSignedData(net, hh))
|
return Sha256(GetSignedData(net, hh))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sha256 hashes the incoming byte slice
|
// Sha256 hashes the incoming byte slice
|
||||||
|
|
|
@ -80,6 +80,33 @@ func NewPrivateKeyFromBytes(b []byte) (*PrivateKey, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewPrivateKeyFromBytes returns a NEO Secp256r1 PrivateKey from the given
|
||||||
|
// byte slice.
|
||||||
|
func NewPrivateKoblitzKeyFromBytes(b []byte) (*PrivateKey, error) {
|
||||||
|
if len(b) != 32 {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"invalid byte length: expected %d bytes got %d", 32, len(b),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
c = secp256k1.S256()
|
||||||
|
d = new(big.Int).SetBytes(b)
|
||||||
|
)
|
||||||
|
|
||||||
|
x, y := c.ScalarBaseMult(b)
|
||||||
|
|
||||||
|
return &PrivateKey{
|
||||||
|
ecdsa.PrivateKey{
|
||||||
|
PublicKey: ecdsa.PublicKey{
|
||||||
|
Curve: c,
|
||||||
|
X: x,
|
||||||
|
Y: y,
|
||||||
|
},
|
||||||
|
D: d,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// NewPrivateKeyFromASN1 returns a NEO Secp256k1 PrivateKey from the ASN.1
|
// NewPrivateKeyFromASN1 returns a NEO Secp256k1 PrivateKey from the ASN.1
|
||||||
// serialized key.
|
// serialized key.
|
||||||
func NewPrivateKeyFromASN1(b []byte) (*PrivateKey, error) {
|
func NewPrivateKeyFromASN1(b []byte) (*PrivateKey, error) {
|
||||||
|
|
|
@ -13,13 +13,15 @@ import (
|
||||||
// Hash represents CryptoLib contract hash.
|
// 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"
|
const Hash = "\x1b\xf5\x75\xab\x11\x89\x68\x84\x13\x61\x0a\x35\xa1\x28\x86\xcd\xe0\xb6\x6c\x72"
|
||||||
|
|
||||||
// NamedCurve represents a named elliptic curve.
|
// NamedCurveHash represents a pair of named elliptic curve and hash function.
|
||||||
type NamedCurve byte
|
type NamedCurveHash byte
|
||||||
|
|
||||||
// Various named elliptic curves.
|
// Various pairs of named elliptic curves and hash functions.
|
||||||
const (
|
const (
|
||||||
Secp256k1 NamedCurve = 22
|
Secp256k1Sha256 NamedCurveHash = 22
|
||||||
Secp256r1 NamedCurve = 23
|
Secp256r1Sha256 NamedCurveHash = 23
|
||||||
|
Secp256k1Keccak256 NamedCurveHash = 24
|
||||||
|
Secp256r1Keccak256 NamedCurveHash = 25
|
||||||
)
|
)
|
||||||
|
|
||||||
// Sha256 calls `sha256` method of native CryptoLib contract and computes SHA256 hash of b.
|
// Sha256 calls `sha256` method of native CryptoLib contract and computes SHA256 hash of b.
|
||||||
|
@ -40,8 +42,8 @@ func Murmur32(b []byte, seed int) []byte {
|
||||||
|
|
||||||
// VerifyWithECDsa calls `verifyWithECDsa` method of native CryptoLib contract and checks that sig is
|
// VerifyWithECDsa calls `verifyWithECDsa` method of native CryptoLib contract and checks that sig is
|
||||||
// a correct msg's signature for the given pub (serialized public key on the given curve).
|
// a correct msg's signature for the given pub (serialized public key on the given curve).
|
||||||
func VerifyWithECDsa(msg []byte, pub interop.PublicKey, sig interop.Signature, curve NamedCurve) bool {
|
func VerifyWithECDsa(msg []byte, pub interop.PublicKey, sig interop.Signature, curveHash NamedCurveHash) bool {
|
||||||
return neogointernal.CallWithToken(Hash, "verifyWithECDsa", int(contract.NoneFlag), msg, pub, sig, curve).(bool)
|
return neogointernal.CallWithToken(Hash, "verifyWithECDsa", int(contract.NoneFlag), msg, pub, sig, curveHash).(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bls12381Point represents BLS12-381 curve point (G1 or G2 in the Affine or
|
// Bls12381Point represents BLS12-381 curve point (G1 or G2 in the Affine or
|
||||||
|
|
|
@ -89,7 +89,7 @@ const (
|
||||||
faultedTxHashLE = "82279bfe9bada282ca0f8cb8e0bb124b921af36f00c69a518320322c6f4fef60"
|
faultedTxHashLE = "82279bfe9bada282ca0f8cb8e0bb124b921af36f00c69a518320322c6f4fef60"
|
||||||
faultedTxBlock uint32 = 23
|
faultedTxBlock uint32 = 23
|
||||||
invokescriptContractAVM = "VwIADBQBDAMOBQYMDQIODw0DDgcJAAAAAErZMCQE2zBwaEH4J+yMqiYEEUAMFA0PAwIJAAIBAwcDBAUCAQAOBgwJStkwJATbMHFpQfgn7IyqJgQSQBNA"
|
invokescriptContractAVM = "VwIADBQBDAMOBQYMDQIODw0DDgcJAAAAAErZMCQE2zBwaEH4J+yMqiYEEUAMFA0PAwIJAAIBAwcDBAUCAQAOBgwJStkwJATbMHFpQfgn7IyqJgQSQBNA"
|
||||||
block20StateRootLE = "637aac452ef781dee7ac5e898a1edf4d3c5b6420288ea5232dad620f39d2152a"
|
block20StateRootLE = "c187c5a3272054dadbf8a1c896435462bb8c79c8a09595d5ebd96dbc7fd7129d"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -3152,10 +3152,13 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
||||||
body := doRPCCall(`{"jsonrpc": "2.0", "id": 1, "method": "calculatenetworkfee", "params": ["bm90IGEgdHJhbnNhY3Rpb24K"]}"`, httpSrv.URL, t)
|
body := doRPCCall(`{"jsonrpc": "2.0", "id": 1, "method": "calculatenetworkfee", "params": ["bm90IGEgdHJhbnNhY3Rpb24K"]}"`, httpSrv.URL, t)
|
||||||
_ = checkErrGetResult(t, body, true, neorpc.InvalidParamsCode, "Invalid params")
|
_ = checkErrGetResult(t, body, true, neorpc.InvalidParamsCode, "Invalid params")
|
||||||
})
|
})
|
||||||
calcReq := func(t *testing.T, tx *transaction.Transaction) []byte {
|
calcReqExactly := func(t *testing.T, tx string) []byte {
|
||||||
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "calculatenetworkfee", "params": ["%s"]}"`, base64.StdEncoding.EncodeToString(tx.Bytes()))
|
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "calculatenetworkfee", "params": ["%s"]}"`, tx)
|
||||||
return doRPCCall(rpc, httpSrv.URL, t)
|
return doRPCCall(rpc, httpSrv.URL, t)
|
||||||
}
|
}
|
||||||
|
calcReq := func(t *testing.T, tx *transaction.Transaction) []byte {
|
||||||
|
return calcReqExactly(t, base64.StdEncoding.EncodeToString(tx.Bytes()))
|
||||||
|
}
|
||||||
t.Run("non-contract with zero verification", func(t *testing.T) {
|
t.Run("non-contract with zero verification", func(t *testing.T) {
|
||||||
tx := &transaction.Transaction{
|
tx := &transaction.Transaction{
|
||||||
Script: []byte{byte(opcode.RET)},
|
Script: []byte{byte(opcode.RET)},
|
||||||
|
@ -3235,6 +3238,13 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
||||||
}
|
}
|
||||||
checkCalc(t, tx, 2315100) // Perfectly matches FeeIsMultiSigContract() C# test.
|
checkCalc(t, tx, 2315100) // Perfectly matches FeeIsMultiSigContract() C# test.
|
||||||
})
|
})
|
||||||
|
t.Run("Koblitz custom multisignature witness", func(t *testing.T) {
|
||||||
|
tx := "AAIAAACWP5gAAAAAAIBniwAAAAAAAgAAAAFSPSnnAijsThGazYipphHw5ljbTgEAVgsVDBQBAgMAAAAAAAAAAAAAAAAAAAAAAAwUUj0p5wIo7E4Rms2IqaYR8OZY204UwB8MCHRyYW5zZmVyDBTPduKL0AYsSkeO41VhARMZ88+k0kFifVtSAcwMQgxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxCDEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADEIMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD9BgFXBQBDEygFSQlAE8BwDCEDL5dNUWO0ZANmSz16KYRBbhmdzE7u8DguVYmWcRLdiF4MIQNFg/P7jBdGcTXcxgjfua/nv6taGb4rLqQXYGQ3LtfuLQwhA0hxJZ8CzRWOQpvE/60SPZ7bVYGr6Kl1MiZBLnvtDpsdDCECkTKVLY6bY2bFkIbD+dViOp7w2eZ2ZWr+1kH4qFNKkP8UwHFBxfug4AMAAAAAAQAAAJ4UjUEtUQgwEM6LchBzEHRrE7hsFLiSJEIAGGhrzmlszmoUwB8MD3ZlcmlmeVdpdGhFQ0RzYQwUG/V1qxGJaIQTYQo1oSiGzeC2bHJBYn1bUmuec2ycdCK5axOz"
|
||||||
|
resp := checkErrGetResult(t, calcReqExactly(t, tx), false, 0)
|
||||||
|
res := new(result.NetworkFee)
|
||||||
|
require.NoError(t, json.Unmarshal(resp, res))
|
||||||
|
require.Equal(t, int64(8995470), res.Value)
|
||||||
|
})
|
||||||
checkContract := func(t *testing.T, verAcc util.Uint160, invoc []byte, fee int64) {
|
checkContract := func(t *testing.T, verAcc util.Uint160, invoc []byte, fee int64) {
|
||||||
txScript, err := smartcontract.CreateCallWithAssertScript(chain.UtilityTokenHash(), "transfer",
|
txScript, err := smartcontract.CreateCallWithAssertScript(chain.UtilityTokenHash(), "transfer",
|
||||||
verAcc, verAcc, 1, nil)
|
verAcc, verAcc, 1, nil)
|
||||||
|
|
|
@ -29,6 +29,11 @@ func Opcodes(w *io.BinWriter, ops ...opcode.Opcode) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitSlot emits INITSLOT instruction with the specified size of locals/args slots.
|
||||||
|
func InitSlot(w *io.BinWriter, locals, args uint8) {
|
||||||
|
Instruction(w, opcode.INITSLOT, []byte{locals, args})
|
||||||
|
}
|
||||||
|
|
||||||
// Bool emits a bool type to the given buffer.
|
// Bool emits a bool type to the given buffer.
|
||||||
func Bool(w *io.BinWriter, ok bool) {
|
func Bool(w *io.BinWriter, ok bool) {
|
||||||
var opVal = opcode.PUSHT
|
var opVal = opcode.PUSHT
|
||||||
|
|
Loading…
Reference in a new issue