Completed
Pull Request — master (#301)
by thomas
69:53
created

PaymentVerifier   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 96
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 96
rs 10
c 1
b 0
f 0
wmc 12
lcom 1
cbo 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getTransactions() 0 11 1
D checkTransactions() 0 44 9
A checkPayment() 0 5 1
1
<?php
2
3
namespace BitWasp\Bitcoin\PaymentProtocol;
4
5
use BitWasp\Bitcoin\Collection\Transaction\TransactionCollection;
6
use BitWasp\Bitcoin\Math\Math;
7
use BitWasp\Bitcoin\PaymentProtocol\Protobufs\Payment;
8
use BitWasp\Bitcoin\PaymentProtocol\Protobufs\PaymentDetails;
9
use BitWasp\Bitcoin\PaymentProtocol\Protobufs\PaymentRequest;
10
use BitWasp\Bitcoin\Transaction\TransactionFactory;
11
12
class PaymentVerifier
13
{
14
    /**
15
     * @var Math
16
     */
17
    private $math;
18
19
    /**
20
     * PaymentVerifier constructor.
21
     * @param Math $math
22
     */
23
    public function __construct(Math $math)
24
    {
25
        $this->math = $math;
26
    }
27
28
    /**
29
     * @param Payment $payment
30
     * @return TransactionCollection
31
     */
32
    public function getTransactions(Payment $payment)
33
    {
34
        return new TransactionCollection(
35
            array_map(
36
                function ($binTx) {
37
                    return TransactionFactory::fromHex(bin2hex($binTx));
38
                },
39
                $payment->getTransactionsList()
40
            )
41
        );
42
    }
43
44
    /**
45
     * @param PaymentRequest $request
46
     * @param TransactionCollection $collection
47
     * @return bool
48
     */
49
    public function checkTransactions(PaymentRequest $request, TransactionCollection $collection)
50
    {
51
        // Add up cumulative amounts for each destination
52
        $scriptAmount = [];
53
        foreach ($collection as $tx) {
54
            foreach ($tx->getOutputs() as $output) {
55
                $scriptBin = $output->getScript()->getBinary();
56
                if (array_key_exists($scriptBin, $scriptAmount)) {
57
                    $scriptAmount[$scriptBin] = $this->math->add($output->getValue(), $scriptAmount[$scriptBin]);
58
                } else {
59
                    $scriptAmount[$scriptBin] = $output->getValue();
60
                }
61
            }
62
        }
63
64
        // Do the same for our PaymentDetails
65
        $details = new PaymentDetails();
66
        $details->parse($request->getSerializedPaymentDetails());
67
68
        $requiredAmounts = [];
69
        foreach ($details->getOutputsList() as $out) {
70
            $scriptBin = $out->getScript();
71
            if (array_key_exists($scriptBin, $requiredAmounts)) {
72
                $requiredAmounts[$scriptBin] = $this->math->add($out->getAmount(), $requiredAmounts[$scriptBin]);
73
            } else {
74
                $requiredAmounts[$scriptBin] = $out->getAmount();
75
            }
76
        }
77
78
        // Check required amounts against user transaction
79
        foreach ($requiredAmounts as $script => $value) {
80
            // Script not funded
81
            if (!array_key_exists($script, $scriptAmount)) {
82
                return false;
83
            }
84
85
            // Script not paid enough
86
            if ($this->math->cmp($scriptAmount[$script], $value) < 0) {
87
                return false;
88
            }
89
        }
90
91
        return true;
92
    }
93
94
    /**
95
     * Verifies that outputs of transactions in Payment
96
     * satisfy the amounts required by the PaymentRequest.
97
     *
98
     * @param Payment $payment
99
     * @param PaymentRequest $request
100
     * @return bool
101
     */
102
    public function checkPayment(PaymentRequest $request, Payment $payment)
103
    {
104
        $transactions = $this->getTransactions($payment);
105
        return $this->checkTransactions($request, $transactions);
106
    }
107
}
108