Completed
Pull Request — master (#348)
by thomas
70:36
created

PaymentVerifier::checkTransactions()   C

Complexity

Conditions 8
Paths 12

Size

Total Lines 44
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 27
CRAP Score 8

Importance

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