Completed
Pull Request — master (#526)
by thomas
30:16
created

ParsedScript::hasMultipleBranches()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace BitWasp\Bitcoin\Script\Path;
4
5
use BitWasp\Bitcoin\Script\Parser\Operation;
6
use BitWasp\Bitcoin\Script\ScriptFactory;
7
use BitWasp\Bitcoin\Script\ScriptInterface;
8
9
class ParsedScript
10
{
11
    /**
12
     * @var ScriptInterface
13
     */
14
    private $script;
15
16
    /**
17
     * @var LogicOpNode
18
     */
19
    private $ast;
20
21
    /**
22
     * @var array
23
     */
24
    private $branchMap;
25
26
    /**
27
     * @var array
28
     */
29
    private $descriptorMap;
30
31
    /**
32
     * ParsedScript constructor.
33
     * @param ScriptInterface $script
34
     * @param LogicOpNode $ast
35
     * @param ScriptBranch[] $branches
36
     */
37 154
    public function __construct(ScriptInterface $script, LogicOpNode $ast, array $branches)
38
    {
39 154
        if (!$ast->isRoot()) {
40
            throw new \RuntimeException("AST is for invalid node, wasn't root");
41
        }
42
43 154
        $descriptorIdx = 0;
44 154
        $keyedBranchMap = []; // idx => ScriptBranch
45 154
        $keyedIdxMap = []; // descriptor => ScriptBranch
46 154
        foreach ($branches as $branch) {
47 154
            $descriptor = $branch->getPath();
48 154
            $descriptorKey = json_encode($descriptor);
49 154
            if (array_key_exists($descriptorKey, $keyedBranchMap)) {
50
                throw new \RuntimeException("Duplicate branch descriptor, invalid ScriptBranch found");
51
            }
52
53 154
            $keyedBranchMap[] = $branch;
54 154
            $keyedIdxMap[$descriptorKey] = $descriptorIdx;
55 154
            $descriptorIdx++;
56
        }
57
58 154
        $this->branchMap = $keyedBranchMap;
59 154
        $this->descriptorMap = $keyedIdxMap;
60 154
        $this->script = $script;
61 154
        $this->ast = $ast;
62 154
    }
63
64
    /**
65
     * @return bool
66
     */
67 136
    public function hasMultipleBranches()
68
    {
69 136
        return $this->ast->hasChildren();
70
    }
71
72
    /**
73
     * Returns a list of paths for this script. This is not
74
     * always guaranteed to be in order, so take care that
75
     * you actually work out the paths in advance of signing,
76
     * and hard code them somehow.
77
     *
78
     * @return array[] - array of paths
79
     */
80
    public function getPaths()
81
    {
82 18
        return array_map(function (ScriptBranch $branch) {
83 18
            return $branch->getPath();
84 18
        }, $this->branchMap);
85
    }
86
87
    /**
88
     * Look up the branch idx by it's path
89
     *
90
     * @param array $branchDesc
91
     * @return int
92
     */
93 154
    private function getBranchIdx(array $branchDesc)
94
    {
95 154
        $descriptorKey = json_encode($branchDesc);
96
97 154
        if (array_key_exists($descriptorKey, $this->descriptorMap)) {
98 154
            return $this->descriptorMap[$descriptorKey];
99
        }
100
101
        throw new \RuntimeException("Unknown branch");
102
    }
103
104
    /**
105
     * Look up the branch by it's path
106
     *
107
     * @param array $branchDesc
108
     * @return bool|ScriptBranch
109
     */
110 154
    public function getBranchByPath(array $branchDesc)
111
    {
112 154
        $idx = $this->getBranchIdx($branchDesc);
113 154
        if (!array_key_exists($idx, $this->branchMap)) {
114
            throw new \RuntimeException("Coding error, missing entry in branch map for desc");
115
        }
116
117 154
        return $this->branchMap[$idx];
118
    }
119
120
    /**
121
     * @param $branch
122
     * @return ScriptInterface|bool
123
     */
124
    public function getMutuallyExclusiveOps($branch)
125
    {
126
        if (!($branch = $this->getBranchByPath($branch))) {
127
            return false;
128
        }
129
130
        $ops = [];
131
        foreach ($branch->getScriptSections() as $step) {
132
            /** @var Operation[] $step */
133
            if (count($step) === 1 && $step[0]->isLogical()) {
134
                continue;
135
            }
136
137
            $ops = array_merge($ops, $step);
138
        }
139
140
        return ScriptFactory::fromOperations($ops);
141
    }
142
}
143