Completed
Push — master ( b2abd4...891c12 )
by thomas
22:03
created

ScriptCreator::sequence()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 6.0702

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 15
nc 6
nop 1
dl 0
loc 22
ccs 14
cts 16
cp 0.875
crap 6.0702
rs 8.6737
c 1
b 0
f 0
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 772
    public function __construct(Math $math, Opcodes $opcodes, BufferInterface $buffer = null)
39
    {
40 772
        if ($buffer !== null) {
41 516
            $this->script = $buffer->getBinary();
42 387
        }
43
44 772
        $this->math = $math;
45 772
        $this->opcodes = $opcodes;
46 772
    }
47
48
    /**
49
     * @param int[]|\BitWasp\Bitcoin\Script\Interpreter\Number[]|BufferInterface[] $sequence
50
     * @return $this
51
     */
52 192
    public function sequence(array $sequence)
53
    {
54 192
        $new = new self($this->math, $this->opcodes, null);
55 192
        foreach ($sequence as $operation) {
56 192
            if (is_int($operation)) {
57 184
                if (!$this->opcodes->offsetExists($operation)) {
58
                    throw new \RuntimeException('Unknown opcode');
59
                }
60
61 184
                $new->script .= chr($operation);
62 162
            } elseif ($operation instanceof Number) {
63
                $new->push($operation->getBuffer());
64 144
            } elseif ($operation instanceof BufferInterface) {
65 192
                $new->push($operation);
66 144
            } else {
67 48
                throw new \RuntimeException('Input was neither an opcode or BufferInterfacecc');
68
            }
69 144
        }
70
71 192
        $this->concat($new->getScript());
72 192
        return $this;
73
    }
74
75
    /**
76
     * Add an opcode to the script
77
     *
78
     * @param string $name
79
     * @return $this
80
     */
81 116
    public function op($name)
82
    {
83 116
        $code = $this->opcodes->getOpByName($name);
84 112
        $this->script .= chr($code);
85 112
        return $this;
86
    }
87
88
    /**
89
     * Push data into the stack.
90
     *
91
     * @param $data
92
     * @return $this
93
     * @throws \Exception
94
     */
95 252
    public function push(BufferInterface $data)
96
    {
97 252
        $length = $data->getSize();
98 252
        $parsed = new Parser('', $this->math);
99
100
        /** Note that larger integers are serialized without flipping bits - Big endian */
101
102 252
        if ($length < $this->opcodes->getOpByName('OP_PUSHDATA1')) {
103 240
            $varInt = Buffertools::numToVarInt($length);
104 240
            $data = new Buffer($varInt->getBinary() . $data->getBinary(), null, $this->math);
105 240
            $parsed->writeBytes($data->getSize(), $data);
106 180
        } else {
107 16
            if ($length <= 0xff) {
108 8
                $lengthSize = 1;
109 14
            } elseif ($length <= 0xffff) {
110 4
                $lengthSize = 2;
111 3
            } else {
112 4
                $lengthSize = 4;
113
            }
114
115 16
            $op = $this->opcodes->getOpByName('OP_PUSHDATA' . $lengthSize);
116
            $parsed
117 16
                ->writeBytes(1, Buffer::int($op))
118 16
                ->writeBytes($lengthSize, Buffer::int($length), true)
119 16
                ->writeBytes($length, $data);
120
        }
121
122 252
        $this->script .= $parsed->getBuffer()->getBinary();
123 252
        return $this;
124
    }
125
126
    /**
127
     * @param int $n
128
     * @return $this
129
     */
130 52
    public function int($n)
131
    {
132 52
        if ($n === 0) {
133
            $this->script .= chr(Opcodes::OP_0);
134 52
        } else if ($n === -1 || ($n >= 1 && $n <= 16)) {
135 52
            $this->script .= chr(\BitWasp\Bitcoin\Script\encodeOpN($n));
136 39
        } else {
137
            $this->script .= Number::int($n)->getBinary();
138
        }
139
140 52
        return $this;
141
    }
142
143
    /**
144
     * @param Serializable $object
145
     * @return $this
146
     */
147
    public function pushSerializable(Serializable $object)
148
    {
149
        $this->push($object->getBuffer());
150
        return $this;
151
    }
152
153
    /**
154
     * @param Serializable[] $serializable
155
     * @return $this
156
     */
157
    public function pushSerializableArray(array $serializable)
158
    {
159
        foreach ($serializable as $object) {
160
            $this->pushSerializable($object);
161
        }
162
163
        return $this;
164
    }
165
166
    /**
167
     * @param ScriptInterface $script
168
     * @return $this
169
     */
170 192
    public function concat(ScriptInterface $script)
171
    {
172 192
        $this->script .= $script->getBinary();
173 192
        return $this;
174
    }
175
176
    /**
177
     * @return ScriptInterface
178
     */
179 768
    public function getScript()
180
    {
181 768
        return new Script(new Buffer($this->script, null, $this->math), $this->opcodes);
182
    }
183
}
184