core: sort hashes and witnesses in VerifyWitnesses()

Fixes failure to verify some multi-witnesses transactions in Testnet chain. C#
code contains similar logic.
This commit is contained in:
Roman Khimov 2019-09-30 17:39:42 +03:00
parent dd0dd2d031
commit 8537700b7b
3 changed files with 33 additions and 1 deletions

View file

@ -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

View file

@ -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

View file

@ -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"