Completed
Pull Request — master (#241)
by thomas
133:23 queued 63:06
created

ScriptCreator::sequence()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 20
rs 8.8571
ccs 7
cts 7
cp 1
cc 5
eloc 13
nc 5
nop 1
crap 5
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 1138
    public function __construct(Math $math, Opcodes $opcodes, BufferInterface $buffer = null)
39
    {
40 1138
        if ($buffer !== null) {
41 744
            $this->script = $buffer->getBinary();
42 744
        }
43
44 1138
        $this->math = $math;
45 1138
        $this->opcodes = $opcodes;
46 1138
    }
47
48
    /**
49
     * @param array $sequence
50
     * @return $this
51
     */
52
    public function sequence(array $sequence)
53
    {
54 340
        $new = new self($this->math, $this->opcodes, null);
55
        foreach ($sequence as $operation) {
56 340
            if (is_int($operation)) {
57 334
                if (!$this->opcodes->offsetExists($operation)) {
58 334
                    throw new \RuntimeException('Unknown opcode');
59
                }
60
61
                $new->script .= chr($operation);
62
            } elseif ($operation instanceof BufferInterface) {
63
                $new->push($operation);
64
            } else {
65
                throw new \RuntimeException('Input was neither an opcode or BufferInterfacecc');
66
            }
67
        }
68 400
69
        $this->concat($new->getScript());
70 400
        return $this;
71 400
    }
72
73
    /**
74
     * Add an opcode to the script
75 400
     *
76 382
     * @param string $name
77 382
     * @return $this
78 382
     */
79 382
    public function op($name)
80 36
    {
81 24
        $code = $this->opcodes->getOpByName($name);
82 36
        $this->script .= chr($code);
83 6
        return $this;
84 6
    }
85 6
86
    /**
87
     * Push data into the stack.
88 36
     *
89
     * @param $data
90 36
     * @return $this
91 36
     * @throws \Exception
92 36
     */
93
    public function push(BufferInterface $data)
94
    {
95 400
        $length = $data->getSize();
96 400
        $parsed = new Parser('', $this->math);
97
98
        /** Note that larger integers are serialized without flipping bits - Big endian */
99
100
        if ($length < $this->opcodes->getOpByName('OP_PUSHDATA1')) {
101
            $varInt = Buffertools::numToVarInt($length);
102
            $data = new Buffer($varInt->getBinary() . $data->getBinary(), null, $this->math);
103
            $parsed->writeBytes($data->getSize(), $data);
104
        } else {
105
            if ($length <= 0xff) {
106
                $lengthSize = 1;
107
            } elseif ($length <= 0xffff) {
108
                $lengthSize = 2;
109
            } else {
110
                $lengthSize = 4;
111
            }
112
113
            $op = $this->opcodes->getOpByName('OP_PUSHDATA' . $lengthSize);
114
            $parsed
115
                ->writeBytes(1, Buffer::int($op))
116
                ->writeBytes($lengthSize, Buffer::int($length), true)
117
                ->writeBytes($length, $data);
118
        }
119
120
        $this->script .= $parsed->getBuffer()->getBinary();
121
        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 6
     * @return $this
144
     */
145 6
    public function pushSerializable(Serializable $object)
146 6
    {
147
        $this->push($object->getBuffer());
148
        return $this;
149
    }
150
151
    /**
152 1132
     * @param Serializable[] $serializable
153
     * @return $this
154 1132
     */
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
    public function concat(ScriptInterface $script)
169
    {
170
        $this->script .= $script->getBinary();
171
        return $this;
172
    }
173
174
    /**
175
     * @return ScriptInterface
176
     */
177
    public function getScript()
178
    {
179
        return new Script(new Buffer($this->script, null, $this->math), $this->opcodes);
180
    }
181
}
182