From 8537700b7b006ee317ce3a25c4ce4831d18eee48 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 30 Sep 2019 17:39:42 +0300 Subject: [PATCH] core: sort hashes and witnesses in VerifyWitnesses() Fixes failure to verify some multi-witnesses transactions in Testnet chain. C# code contains similar logic. --- pkg/core/blockchain.go | 7 ++++++- pkg/util/uint160.go | 12 ++++++++++++ pkg/util/uint160_test.go | 15 +++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 88bf4e5d8..fb7db4eaf 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "math" + "sort" "sync/atomic" "time" @@ -965,7 +966,9 @@ func (bc *Blockchain) GetScriptHashesForVerifying(t *transaction.Transaction) ([ } -// VerifyWitnesses verify the scripts (witnesses) that come with a transactions. +// VerifyWitnesses verify the scripts (witnesses) that come with a given +// transaction. It can reorder them by ScriptHash, because that's required to +// match a slice of script hashes from the Blockchain. // Golang implementation of VerifyWitnesses method in C# (https://github.com/neo-project/neo/blob/master/neo/SmartContract/Helper.cs#L87). // Unfortunately the IVerifiable interface could not be implemented because we can't move the References method in blockchain.go to the transaction.go file func (bc *Blockchain) VerifyWitnesses(t *transaction.Transaction) error { @@ -978,6 +981,8 @@ func (bc *Blockchain) VerifyWitnesses(t *transaction.Transaction) error { if len(hashes) != len(witnesses) { return errors.Errorf("expected len(hashes) == len(witnesses). got: %d != %d", len(hashes), len(witnesses)) } + sort.Slice(hashes, func(i, j int) bool { return hashes[i].Less(hashes[j]) }) + sort.Slice(witnesses, func(i, j int) bool { return witnesses[i].ScriptHash().Less(witnesses[j].ScriptHash()) }) for i := 0; i < len(hashes); i++ { verification := witnesses[i].VerificationScript diff --git a/pkg/util/uint160.go b/pkg/util/uint160.go index 8c9202935..f2cd6535f 100644 --- a/pkg/util/uint160.go +++ b/pkg/util/uint160.go @@ -59,6 +59,18 @@ func (u Uint160) Equals(other Uint160) bool { return u == other } +// Less returns true if this value is less than given Uint160 value. It's +// primarily intended to be used for sorting purposes. +func (u Uint160) Less(other Uint160) bool { + for k := range u { + if u[k] == other[k] { + continue + } + return u[k] < other[k] + } + return false +} + // UnmarshalJSON implements the json unmarshaller interface. func (u *Uint160) UnmarshalJSON(data []byte) (err error) { var js string diff --git a/pkg/util/uint160_test.go b/pkg/util/uint160_test.go index 0d64a5623..06db92ea3 100644 --- a/pkg/util/uint160_test.go +++ b/pkg/util/uint160_test.go @@ -76,6 +76,21 @@ func TestUInt160Equals(t *testing.T) { } } +func TestUInt160Less(t *testing.T) { + a := "2d3b96ae1bcc5a585e075e3b81920210dec16302" + b := "2d3b96ae1bcc5a585e075e3b81920210dec16303" + + ua, err := Uint160DecodeString(a) + assert.Nil(t, err) + ua2, err := Uint160DecodeString(a) + assert.Nil(t, err) + ub, err := Uint160DecodeString(b) + assert.Nil(t, err) + assert.Equal(t, true, ua.Less(ub)) + assert.Equal(t, false, ua.Less(ua2)) + assert.Equal(t, false, ub.Less(ua)) +} + func TestUInt160String(t *testing.T) { hexStr := "b28427088a3729b2536d10122960394e8be6721f" hexRevStr := "1f72e68b4e39602912106d53b229378a082784b2"