From 679e01bd0f435daa22efb68ebbf3a8e42514e6c7 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 23 Sep 2019 20:13:44 +0300 Subject: [PATCH] core: implement witness verification logic Fix #368. --- pkg/core/blockchain.go | 39 ++++++++++++++++++----------- pkg/core/transaction/transaction.go | 17 +++++++++++-- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 068401b03..24f98ffcb 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -1,6 +1,7 @@ package core import ( + "bytes" "context" "fmt" "math" @@ -12,6 +13,7 @@ import ( "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" + "github.com/CityOfZion/neo-go/pkg/vm" "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) @@ -902,28 +904,35 @@ func (bc *Blockchain) VerifyWitnesses(t *transaction.Transaction) error { verification := witnesses[i].VerificationScript if len(verification) == 0 { - /*TODO: replicate following C# code: - using (ScriptBuilder sb = new ScriptBuilder()) - { - sb.EmitAppCall(hashes[i].ToArray()); - verification = sb.ToArray(); + bb := new(bytes.Buffer) + err = vm.EmitAppCall(bb, hashes[i], false) + if err != nil { + return err } - */ - + verification = bb.Bytes() } else { if h := witnesses[i].ScriptHash(); hashes[i] != h { return errors.Errorf("hash mismatch for script #%d", i) } } - /*TODO: replicate following C# code: - using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, verifiable, snapshot, Fixed8.Zero)) - { - engine.LoadScript(verification); - engine.LoadScript(verifiable.Witnesses[i].InvocationScript); - if (!engine.Execute()) return false; - if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().GetBoolean()) return false; - }*/ + vm := vm.New(vm.ModeMute) + vm.SetCheckedHash(t.VerificationHash().Bytes()) + vm.LoadScript(verification) + vm.LoadScript(witnesses[i].InvocationScript) + vm.Run() + if vm.HasFailed() { + return errors.Errorf("vm failed to execute the script") + } + res := vm.PopResult() + switch res.(type) { + case bool: + if !(res.(bool)) { + return errors.Errorf("signature check failed") + } + default: + return errors.Errorf("vm returned non-boolean result") + } } return nil diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index 13bdca753..12e90e7dd 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -39,9 +39,12 @@ type Transaction struct { // and invocation script. Scripts []*Witness `json:"scripts"` - // hash of the transaction + // Hash of the transaction (double SHA256). hash util.Uint256 + // Hash of the transaction used to verify it (single SHA256). + verificationHash util.Uint256 + // Trimmed indicates this is a transaction from trimmed // data. Trimmed bool `json:"-"` @@ -64,6 +67,14 @@ func (t *Transaction) Hash() util.Uint256 { return t.hash } +// VerificationHash returns the hash of the transaction used to verify it. +func (t *Transaction) VerificationHash() util.Uint256 { + if t.verificationHash.Equals(util.Uint256{}) { + t.createHash() + } + return t.verificationHash +} + // AddOutput adds the given output to the transaction outputs. func (t *Transaction) AddOutput(out *Output) { t.Outputs = append(t.Outputs, out) @@ -196,7 +207,9 @@ func (t *Transaction) createHash() error { return buf.Err } - t.hash = hash.DoubleSha256(buf.Bytes()) + b := buf.Bytes() + t.hash = hash.DoubleSha256(b) + t.verificationHash = hash.Sha256(b) return nil }