-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathbitcoinscript-interpreter.test.ts
More file actions
181 lines (150 loc) · 6.54 KB
/
bitcoinscript-interpreter.test.ts
File metadata and controls
181 lines (150 loc) · 6.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import { expect } from "chai";
import {
BitcoinScriptInterpreter,
OpCode,
hash160,
opCodeFunctions,
sha256,
} from "../src/bitcoinscript-interpreter";
import { ec as EC } from "elliptic";
const ec = new EC("secp256k1");
describe("#BitcoinScriptInterpreter Hands-on Session", () => {
describe("OpCode Functions", () => {
describe("OP_DUP", () => {
it("should duplicate the top stack element", () => {
const stack: Buffer[] = [Buffer.from("test")];
opCodeFunctions[OpCode.OP_DUP](stack);
expect(stack).to.have.lengthOf(2);
expect(stack[0]).to.deep.equal(stack[1]);
});
it("should throw an error if the stack is empty", () => {
const stack: Buffer[] = [];
expect(() => opCodeFunctions[OpCode.OP_DUP](stack)).to.throw("Stack underflow");
});
});
describe("OP_HASH160", () => {
it("should hash the top stack element", () => {
const stack: Buffer[] = [Buffer.from("hello")];
opCodeFunctions[OpCode.OP_HASH160](stack);
expect(stack).to.have.lengthOf(1);
expect(stack[0]).to.have.lengthOf(20);
});
it("should throw an error if the stack is empty", () => {
const stack: Buffer[] = [];
expect(() => opCodeFunctions[OpCode.OP_HASH160](stack)).to.throw("Stack underflow");
});
});
describe("OP_EQUALVERIFY", () => {
it("should verify equality of the top two stack elements", () => {
const stack: Buffer[] = [Buffer.from("value"), Buffer.from("value")];
expect(() => opCodeFunctions[OpCode.OP_EQUALVERIFY](stack)).to.not.throw();
expect(stack).to.have.lengthOf(0);
});
it("should throw an error if the stack has less than two elements", () => {
const stack: Buffer[] = [Buffer.from("value")];
expect(() => opCodeFunctions[OpCode.OP_EQUALVERIFY](stack)).to.throw("Stack underflow");
});
it("should throw an error if the elements are not equal", () => {
const stack: Buffer[] = [Buffer.from("value1"), Buffer.from("value2")];
expect(() => opCodeFunctions[OpCode.OP_EQUALVERIFY](stack)).to.throw("Verification failed");
});
});
describe("OP_CHECKSIG", () => {
it("should verify a valid signature", () => {
const keyPair = ec.genKeyPair();
const publicKey = Buffer.from(keyPair.getPublic("hex"), "hex");
const message = Buffer.from("Transaction data to be signed");
const msgHash = sha256(message);
const signature = Buffer.from(keyPair.sign(msgHash).toDER());
const stack: Buffer[] = [signature, publicKey];
opCodeFunctions[OpCode.OP_CHECKSIG](stack);
expect(stack).to.have.lengthOf(1);
expect(stack[0][0]).to.equal(1);
});
it("should fail to verify an invalid signature", () => {
const keyPair = ec.genKeyPair();
const publicKey = Buffer.from(keyPair.getPublic("hex"), "hex");
const invalidSignature = Buffer.from("invalid_signature");
const stack: Buffer[] = [invalidSignature, publicKey];
expect(() => opCodeFunctions[OpCode.OP_CHECKSIG](stack)).to.throw();
});
it("should throw an error if the stack has less than two elements", () => {
const stack: Buffer[] = [Buffer.from("signature")];
expect(() => opCodeFunctions[OpCode.OP_CHECKSIG](stack)).to.throw("Stack underflow");
});
});
});
describe("BitcoinScriptInterpreter", () => {
it("should correctly execute a valid script", () => {
const keyPair = ec.genKeyPair();
const publicKey = Buffer.from(keyPair.getPublic("hex"), "hex");
const message = Buffer.from("Transaction data to be signed");
const msgHash = sha256(message);
const signature = Buffer.from(keyPair.sign(msgHash).toDER());
const pubKeyHash = hash160(publicKey);
const unlockingScript: (OpCode | Buffer)[] = [signature, publicKey];
const lockingScript: (OpCode | Buffer)[] = [
OpCode.OP_DUP,
OpCode.OP_HASH160,
pubKeyHash,
OpCode.OP_EQUALVERIFY,
OpCode.OP_CHECKSIG,
];
const script = [...unlockingScript, ...lockingScript];
const interpreter = new BitcoinScriptInterpreter();
const isValid = interpreter.executeScript(script);
expect(isValid).to.be.true;
});
it("should fail on an invalid script", () => {
const keyPair = ec.genKeyPair();
const publicKey = Buffer.from(keyPair.getPublic("hex"), "hex");
const message = Buffer.from("Invalid transaction data");
const msgHash = sha256(message);
const signature = Buffer.from(keyPair.sign(msgHash).toDER());
const pubKeyHash = hash160(publicKey);
const unlockingScript: (OpCode | Buffer)[] = [signature, publicKey];
const lockingScript: (OpCode | Buffer)[] = [
OpCode.OP_DUP,
OpCode.OP_HASH160,
pubKeyHash,
OpCode.OP_EQUALVERIFY,
OpCode.OP_CHECKSIG,
];
const script = [...unlockingScript, ...lockingScript];
const interpreter = new BitcoinScriptInterpreter();
const isValid = interpreter.executeScript(script);
expect(isValid).to.be.false;
});
it("should throw an error if the script result is not 0 or 1", () => {
const interpreter = new BitcoinScriptInterpreter();
const invalidScript: (OpCode | Buffer)[] = [Buffer.from("arbitrary_data")];
expect(() => interpreter.executeScript(invalidScript)).to.throw("Invalid script result");
});
it("should correctly execute a P2SH script", () => {
const keyPair = ec.genKeyPair();
const publicKey = Buffer.from(keyPair.getPublic("hex"), "hex");
const pubKeyHash = hash160(publicKey);
const redeemScript: (OpCode | Buffer)[] = [
OpCode.OP_DUP,
OpCode.OP_HASH160,
pubKeyHash,
OpCode.OP_EQUALVERIFY,
OpCode.OP_CHECKSIG,
];
const scriptHash = hash160(BitcoinScriptInterpreter.serializeScript(redeemScript));
const lockingScript: (OpCode | Buffer)[] = [OpCode.OP_HASH160, scriptHash, OpCode.OP_EQUAL];
const message = Buffer.from("Transaction data to be signed");
const msgHash = sha256(message);
const signature = Buffer.from(keyPair.sign(msgHash).toDER());
const unlockingScript: (OpCode | Buffer)[] = [
signature,
publicKey,
BitcoinScriptInterpreter.serializeScript(redeemScript),
];
const script = [...unlockingScript, ...lockingScript];
const interpreter = new BitcoinScriptInterpreter();
const isValid = interpreter.executeScript(script);
expect(isValid).to.be.true;
});
});
});