Completed
Pull Request — master (#248)
by thomas
58:32 queued 33:01
created

ScriptCreator   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 166
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 72.72%

Importance

Changes 3
Bugs 0 Features 2
Metric Value
wmc 22
c 3
b 0
f 2
lcom 1
cbo 9
dl 0
loc 166
ccs 48
cts 66
cp 0.7272
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 2
B sequence() 0 20 5
A op() 0 6 1
B push() 0 30 4
B int() 0 12 5
A pushSerializable() 0 5 1
A pushSerializableArray() 0 8 2
A concat() 0 5 1
A getScript() 0 4 1
1
<?php
2
3
namespace BitWasp\Bitcoin\Script\Factory;
4
5
use BitWasp\Bitcoin\Math\Math;
6
use BitWasp\Bitcoin\Script\Interpreter\Number;
7
use BitWasp\Bitcoin\Script\Opcodes;
8
use BitWasp\Bitcoin\Script\Script;
9
use BitWasp\Bitcoin\Script\ScriptInterface;
10
use BitWasp\Bitcoin\Serializable;
11
use BitWasp\Buffertools\Buffer;
12
use BitWasp\Buffertools\BufferInterface;
13
use BitWasp\Buffertools\Buffertools;
14
use BitWasp\Buffertools\Parser;
15
16
class ScriptCreator
17
{
18
    /**
19
     * @var string
20
     */
21
    private $script = '';
22
23
    /**
24
     * @var Opcodes
25
     */
26
    private $opcodes;
27
28
    /**
29
     * @var Math
30
     */
31
    private $math;
32
33
    /**
34
     * @param Math $math
35
     * @param Opcodes $opcodes
36
     * @param BufferInterface|null $buffer
37
     */
38 1857
    public function __construct(Math $math, Opcodes $opcodes, BufferInterface $buffer = null)
39
    {
40 1857
        if ($buffer !== null) {
41 1428
            $this->script = $buffer->getBinary();
42 1428
        }
43
44 1857
        $this->math = $math;
45 1857
        $this->opcodes = $opcodes;
46 1857
    }
47
48
    /**
49
     * @param array $sequence
50
     * @return $this
51
     */
52 117
    public function sequence(array $sequence)
53
    {
54 117
        $new = new self($this->math, $this->opcodes, null);
55 117
        foreach ($sequence as $operation) {
56 117
            if (is_int($operation)) {
57 54
                if (!$this->opcodes->offsetExists($operation)) {
58
                    throw new \RuntimeException('Unknown opcode');
59
                }
60
61 54
                $new->script .= chr($operation);
62 117
            } elseif ($operation instanceof BufferInterface) {
63 117
                $new->push($operation);
64 117
            } else {
65
                throw new \RuntimeException('Input was neither an opcode or BufferInterfacecc');
66
            }
67 117
        }
68
69 117
        $this->concat($new->getScript());
70 117
        return $this;
71
    }
72
73
    /**
74
     * Add an opcode to the script
75
     *
76
     * @param string $name
77
     * @return $this
78
     */
79 399
    public function op($name)
80
    {
81 399
        $code = $this->opcodes->getOpByName($name);
82 393
        $this->script .= chr($code);
83 393
        return $this;
84
    }
85
86
    /**
87
     * Push data into the stack.
88
     *
89
     * @param $data
90
     * @return $this
91
     * @throws \Exception
92
     */
93 435
    public function push(BufferInterface $data)
94
    {
95 435
        $length = $data->getSize();
96 435
        $parsed = new Parser('', $this->math);
97
98
        /** Note that larger integers are serialized without flipping bits - Big endian */
99
100 435
        if ($length < $this->opcodes->getOpByName('OP_PUSHDATA1')) {
101 417
            $varInt = Buffertools::numToVarInt($length);
102 417
            $data = new Buffer($varInt->getBinary() . $data->getBinary(), null, $this->math);
103 417
            $parsed->writeBytes($data->getSize(), $data);
104 417
        } else {
105 36
            if ($length <= 0xff) {
106 24
                $lengthSize = 1;
107 36
            } elseif ($length <= 0xffff) {
108 6
                $lengthSize = 2;
109 6
            } else {
110 6
                $lengthSize = 4;
111
            }
112
113 36
            $op = $this->opcodes->getOpByName('OP_PUSHDATA' . $lengthSize);
114
            $parsed
115 36
                ->writeBytes(1, Buffer::int($op))
116 36
                ->writeBytes($lengthSize, Buffer::int($length), true)
117 36
                ->writeBytes($length, $data);
118
        }
119
120 435
        $this->script .= $parsed->getBuffer()->getBinary();
121 435
        return $this;
122
    }
123
124
    /**
125
     * @param int $n
126
     * @return $this
127
     */
128
    public function int($n)
129
    {
130
        if ($n === 0) {
131
            $this->script .= chr(Opcodes::OP_0);
132
        } else if ($n === -1 || ($n >= 1 && $n <= 16)) {
133
            $this->script .= chr($n + (Opcodes::OP_1 - 1));
134
        } else {
135
            $this->script .= Number::int($n)->getBinary();
136
        }
137
138
        return $this;
139
    }
140
141
    /**
142
     * @param Serializable $object
143
     * @return $this
144
     */
145
    public function pushSerializable(Serializable $object)
146
    {
147
        $this->push($object->getBuffer());
148
        return $this;
149
    }
150
151
    /**
152
     * @param Serializable[] $serializable
153
     * @return $this
154
     */
155
    public function pushSerializableArray(array $serializable)
156
    {
157
        foreach ($serializable as $object) {
158
            $this->pushSerializable($object);
159
        }
160
161
        return $this;
162
    }
163
164
    /**
165
     * @param ScriptInterface $script
166
     * @return $this
167
     */
168 123
    public function concat(ScriptInterface $script)
169
    {
170 123
        $this->script .= $script->getBinary();
171 123
        return $this;
172
    }
173
174
    /**
175
     * @return ScriptInterface
176
     */
177 1851
    public function getScript()
178
    {
179 1851
        return new Script(new Buffer($this->script, null, $this->math), $this->opcodes);
180
    }
181
}
182