From b67fbbd5ab511f1070a418459ec7f67ad39c3202 Mon Sep 17 00:00:00 2001 From: Don Gunn Date: Fri, 14 Dec 2018 23:13:44 -0800 Subject: [PATCH 01/17] The start of something good with dart --- lib/src/payments/p2ms.dart | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 lib/src/payments/p2ms.dart diff --git a/lib/src/payments/p2ms.dart b/lib/src/payments/p2ms.dart new file mode 100644 index 0000000..e69de29 From 9d3efcef1cb0bce990323d89b7a2d462fa643659 Mon Sep 17 00:00:00 2001 From: Don Gunn Date: Tue, 18 Dec 2018 00:30:29 -0800 Subject: [PATCH 02/17] Adding Functions --- lib/src/payments/p2ms.dart | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/src/payments/p2ms.dart b/lib/src/payments/p2ms.dart index e69de29..e6278df 100644 --- a/lib/src/payments/p2ms.dart +++ b/lib/src/payments/p2ms.dart @@ -0,0 +1,15 @@ + +import '../utils/constants/op.dart'; + +class P2MS { + + bool stacksEqual (a, b) { + if (a.length != b.length) return false; + for(int i=1;i<=a.length;i++) { + if (a[i]!=b[i]) { + return false; + } + } + return true; + } +} \ No newline at end of file From f9a2cb4c1567f3d39715349f27a8c1b2f430b2a3 Mon Sep 17 00:00:00 2001 From: Don Gunn Date: Tue, 18 Dec 2018 04:12:13 -0800 Subject: [PATCH 03/17] Added Data types and seperated classes --- lib/src/payments/p2ms.dart | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/lib/src/payments/p2ms.dart b/lib/src/payments/p2ms.dart index e6278df..356d76f 100644 --- a/lib/src/payments/p2ms.dart +++ b/lib/src/payments/p2ms.dart @@ -1,7 +1,13 @@ +import '../utils/constants/op.dart' show OP_RESERVED; +import 'package:meta/meta.dart'; -import '../utils/constants/op.dart'; class P2MS { + P2MSData data; + + P2MS({@required data}) { + this.data = data; + } bool stacksEqual (a, b) { if (a.length != b.length) return false; @@ -12,4 +18,29 @@ class P2MS { } return true; } -} \ No newline at end of file +} + + +class P2MSData { + int m; + int n; + String output; + String input; + List pubkeys; + List signatures; + Map options; + + P2MSData( + {this.m, + this.n, + this.output, + this.input, + this.pubkeys, + this.signatures}); + +/* @override + String toString() { + return 'P2MSData{address: $address, hash: $hash, output: $output, signature: $signature, pubkey: $pubkey, input: $input}'; + } */ + +} From ccb9980b441c121c2704f27549e6fe49edb4ed6e Mon Sep 17 00:00:00 2001 From: Don Gunn Date: Sat, 22 Dec 2018 02:27:26 -0800 Subject: [PATCH 04/17] Working on lazy eval --- lib/src/payments/p2ms.dart | 51 ++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/lib/src/payments/p2ms.dart b/lib/src/payments/p2ms.dart index 356d76f..61310ee 100644 --- a/lib/src/payments/p2ms.dart +++ b/lib/src/payments/p2ms.dart @@ -1,15 +1,42 @@ -import '../utils/constants/op.dart' show OP_RESERVED; +import '../utils/constants/op.dart'; import 'package:meta/meta.dart'; +import '../utils/script.dart' as bscript; +import '../models/networks.dart'; + + class P2MS { P2MSData data; + NetworkType network; + List _chunks; P2MS({@required data}) { this.data = data; + this.network = network ?? bitcoin; } - - bool stacksEqual (a, b) { + get _tempItem {1 + 2;} + void _enoughInformation(data) { + if ( + !data.input && + !data.output && + !(data.pubkeys && data.m != null) && + !data.signatures + ) throw FormatException('Not enough data'); + } + void _extraValidation(options){ + options['validate'] = true; + } + bool _isAcceptableSignature(signature,options) { + return bscript.isCanonicalScriptSignature(signature) || (options.allowIncomplete && (signature == OPS['OP_0'])); + } + void _decode(output) { + _chunks = bscript.decompile(output); + _tempItem.m = _chunks[0] - OPS['OP_INT_BASE']; + _tempItem.n = _chunks[_chunks.length - 2] - OPS['OP_INT_BASE']; + _tempItem.pubkeys = _chunks.sublist(1, -3); + } + bool _stacksEqual(a, b) { if (a.length != b.length) return false; for(int i=1;i<=a.length;i++) { if (a[i]!=b[i]) { @@ -18,9 +45,9 @@ class P2MS { } return true; } + } - class P2MSData { int m; int n; @@ -30,17 +57,21 @@ class P2MSData { List signatures; Map options; - P2MSData( + P2MSData( {this.m, this.n, this.output, this.input, this.pubkeys, - this.signatures}); - -/* @override + this.signatures, + this.options}); + @override String toString() { - return 'P2MSData{address: $address, hash: $hash, output: $output, signature: $signature, pubkey: $pubkey, input: $input}'; - } */ + return 'P2MSData{sigs: $m, neededSigs: $n, output: $output, input: $input, pubkeys: $pubkeys, sigs: $signatures, options: $signatures}'; + } } + + + + From 872d2b74fbedd4b29c6437c55baafbeffc6fd18d Mon Sep 17 00:00:00 2001 From: Don Gunn Date: Thu, 27 Dec 2018 23:56:48 -0800 Subject: [PATCH 05/17] Working on test --- lib/src/payments/p2ms.dart | 26 ++- test/fixtures/p2ms.json | 352 +++++++++++++++++++++++++++++++++++ test/payments/p2ms_test.dart | 65 +++++++ 3 files changed, 433 insertions(+), 10 deletions(-) create mode 100644 test/fixtures/p2ms.json create mode 100644 test/payments/p2ms_test.dart diff --git a/lib/src/payments/p2ms.dart b/lib/src/payments/p2ms.dart index 61310ee..1de3cb7 100644 --- a/lib/src/payments/p2ms.dart +++ b/lib/src/payments/p2ms.dart @@ -2,6 +2,7 @@ import '../utils/constants/op.dart'; import 'package:meta/meta.dart'; import '../utils/script.dart' as bscript; import '../models/networks.dart'; +import 'dart:typed_data'; @@ -14,15 +15,18 @@ class P2MS { P2MS({@required data}) { this.data = data; this.network = network ?? bitcoin; + _init(); + } + void _init() { + _enoughInformation(data); } - get _tempItem {1 + 2;} void _enoughInformation(data) { if ( !data.input && !data.output && !(data.pubkeys && data.m != null) && !data.signatures - ) throw FormatException('Not enough data'); + ) throw new ArgumentError('Not enough data'); } void _extraValidation(options){ options['validate'] = true; @@ -30,12 +34,12 @@ class P2MS { bool _isAcceptableSignature(signature,options) { return bscript.isCanonicalScriptSignature(signature) || (options.allowIncomplete && (signature == OPS['OP_0'])); } - void _decode(output) { +/* void _decode(output) { _chunks = bscript.decompile(output); _tempItem.m = _chunks[0] - OPS['OP_INT_BASE']; _tempItem.n = _chunks[_chunks.length - 2] - OPS['OP_INT_BASE']; _tempItem.pubkeys = _chunks.sublist(1, -3); - } + } */ bool _stacksEqual(a, b) { if (a.length != b.length) return false; for(int i=1;i<=a.length;i++) { @@ -51,12 +55,13 @@ class P2MS { class P2MSData { int m; int n; - String output; - String input; - List pubkeys; - List signatures; + Uint8List output; + Uint8List input; + List pubkeys; + List signatures; + Uint8List witness; Map options; - + P2MSData( {this.m, this.n, @@ -64,10 +69,11 @@ class P2MSData { this.input, this.pubkeys, this.signatures, + this.witness, this.options}); @override String toString() { - return 'P2MSData{sigs: $m, neededSigs: $n, output: $output, input: $input, pubkeys: $pubkeys, sigs: $signatures, options: $signatures}'; + return 'P2MSData{sigs: $m, neededSigs: $n, output: $output, input: $input, pubkeys: $pubkeys, sigs: $signatures, options: $signatures, witness: $witness}'; } } diff --git a/test/fixtures/p2ms.json b/test/fixtures/p2ms.json new file mode 100644 index 0000000..9ff2611 --- /dev/null +++ b/test/fixtures/p2ms.json @@ -0,0 +1,352 @@ +{ + "valid": [ + { + "description": "output from output", + "arguments": { + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG" + }, + "options": {}, + "expected": { + "m": 2, + "n": 2, + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002" + ], + "signatures": null, + "input": null, + "witness": null + } + }, + { + "description": "output from m/pubkeys", + "arguments": { + "m": 1, + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002" + ] + }, + "expected": { + "m": 1, + "n": 2, + "output": "OP_1 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002" + ], + "signatures": null, + "input": null, + "witness": null + } + }, + { + "description": "input/output from m/pubkeys/signatures", + "arguments": { + "m": 2, + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002", + "030000000000000000000000000000000000000000000000000000000000000003" + ], + "signatures": [ + "300602010002010001", + "300602010102010001" + ] + }, + "expected": { + "m": 2, + "n": 3, + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 OP_3 OP_CHECKMULTISIG", + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002", + "030000000000000000000000000000000000000000000000000000000000000003" + ], + "signatures": [ + "300602010002010001", + "300602010102010001" + ], + "input": "OP_0 300602010002010001 300602010102010001", + "witness": [] + } + }, + { + "description": "input/output from output/signatures", + "arguments": { + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 OP_3 OP_CHECKMULTISIG", + "signatures": [ + "300602010002010001", + "300602010102010001" + ] + }, + "expected": { + "m": 2, + "n": 3, + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 OP_3 OP_CHECKMULTISIG", + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002", + "030000000000000000000000000000000000000000000000000000000000000003" + ], + "signatures": [ + "300602010002010001", + "300602010102010001" + ], + "input": "OP_0 300602010002010001 300602010102010001", + "witness": [] + } + }, + { + "description": "input/output from input/output", + "arguments": { + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 OP_3 OP_CHECKMULTISIG", + "input": "OP_0 300602010002010001 300602010102010001" + }, + "expected": { + "m": 2, + "n": 3, + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 OP_3 OP_CHECKMULTISIG", + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002", + "030000000000000000000000000000000000000000000000000000000000000003" + ], + "signatures": [ + "300602010002010001", + "300602010102010001" + ], + "input": "OP_0 300602010002010001 300602010102010001", + "witness": [] + } + }, + { + "description": "input/output from input/output, even if incomplete", + "arguments": { + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", + "input": "OP_0 OP_0 300602010102010001" + }, + "options": { + "allowIncomplete": true + }, + "expected": { + "m": 2, + "n": 2, + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002" + ], + "signatures": [ + 0, + "300602010102010001" + ], + "input": "OP_0 OP_0 300602010102010001", + "witness": [] + } + }, + { + "description": "input/output from output/signatures, even if incomplete", + "arguments": { + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", + "signatures": [ + 0, + "300602010102010001" + ] + }, + "options": { + "allowIncomplete": true + }, + "expected": { + "m": 2, + "n": 2, + "output": "OP_2 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 OP_2 OP_CHECKMULTISIG", + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002" + ], + "signatures": [ + 0, + "300602010102010001" + ], + "input": "OP_0 OP_0 300602010102010001", + "witness": [] + } + } + ], + "invalid": [ + { + "exception": "Not enough data", + "arguments": {} + }, + { + "exception": "Not enough data", + "arguments": { + "m": 2 + } + }, + { + "exception": "Not enough data", + "arguments": { + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002" + ] + } + }, + { + "description": "Non OP_INT chunk (m)", + "exception": "Output is invalid", + "arguments": { + "output": "OP_RESERVED" + } + }, + { + "description": "Non OP_INT chunk (n)", + "exception": "Output is invalid", + "arguments": { + "output": "OP_1 OP_RESERVED" + } + }, + { + "description": "Missing OP_CHECKMULTISIG", + "exception": "Output is invalid", + "arguments": { + "output": "OP_1 OP_2 OP_RESERVED" + } + }, + { + "description": "m is 0", + "exception": "Output is invalid", + "arguments": { + "output": "OP_0 OP_2 OP_CHECKMULTISIG" + } + }, + { + "description": "n is 0 (m > n)", + "exception": "Output is invalid", + "arguments": { + "output": "OP_2 OP_0 OP_CHECKMULTISIG" + } + }, + { + "description": "m > n", + "exception": "Output is invalid", + "arguments": { + "output": "OP_3 OP_2 OP_CHECKMULTISIG" + } + }, + { + "description": "n !== output pubkeys", + "exception": "Output is invalid", + "options": {}, + "arguments": { + "output": "OP_1 030000000000000000000000000000000000000000000000000000000000000001 OP_2 OP_CHECKMULTISIG" + } + }, + { + "description": "Non-canonical output public key", + "exception": "Output is invalid", + "arguments": { + "output": "OP_1 ffff OP_1 OP_CHECKMULTISIG" + } + }, + { + "exception": "n mismatch", + "arguments": { + "n": 2, + "output": "OP_1 030000000000000000000000000000000000000000000000000000000000000001 OP_1 OP_CHECKMULTISIG" + } + }, + { + "exception": "m mismatch", + "arguments": { + "m": 2, + "output": "OP_1 030000000000000000000000000000000000000000000000000000000000000001 OP_1 OP_CHECKMULTISIG" + } + }, + { + "exception": "Pubkeys mismatch", + "options": {}, + "arguments": { + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001" + ], + "output": "OP_1 030000000000000000000000000000000000000000000000000000000000000002 OP_1 OP_CHECKMULTISIG" + } + }, + { + "exception": "Pubkey count mismatch", + "arguments": { + "m": 2, + "n": 3, + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000002" + ] + } + }, + { + "exception": "Pubkey count cannot be less than m", + "arguments": { + "m": 4, + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000001" + ] + } + }, + { + "exception": "Not enough signatures provided", + "arguments": { + "m": 2, + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000001" + ], + "signatures": [ + "300602010002010001" + ] + } + }, + { + "exception": "Too many signatures provided", + "arguments": { + "m": 2, + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000001" + ], + "signatures": [ + "300602010002010001", + "300602010002010001", + "300602010002010001" + ] + } + }, + { + "description": "Missing OP_0", + "exception": "Input is invalid", + "options": {}, + "arguments": { + "m": 2, + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001", + "030000000000000000000000000000000000000000000000000000000000000001" + ], + "input": "OP_RESERVED" + } + }, + { + "exception": "Input has invalid signature\\(s\\)", + "arguments": { + "m": 1, + "pubkeys": [ + "030000000000000000000000000000000000000000000000000000000000000001" + ], + "input": "OP_0 ffffffffffffffff" + } + } + ] + } \ No newline at end of file diff --git a/test/payments/p2ms_test.dart b/test/payments/p2ms_test.dart new file mode 100644 index 0000000..19c8447 --- /dev/null +++ b/test/payments/p2ms_test.dart @@ -0,0 +1,65 @@ +import 'package:bitcoin_flutter/src/payments/p2ms.dart'; +import 'package:test/test.dart'; +import 'package:bitcoin_flutter/src/utils/script.dart' as bscript; +import 'dart:io'; +import 'dart:convert'; +import 'package:hex/hex.dart'; +import 'dart:typed_data'; +main() { + final fixtures = json.decode(new File("./test/fixtures/p2ms.json").readAsStringSync(encoding: utf8)); + group('(valid case)', () { + (fixtures["valid"] as List).forEach((f) { + test(f['description'] + ' as expected', () { + final arguments = _preformP2MS(f['arguments']); + final p2ms = new P2MS(data: arguments); + + expect(p2ms.data.m, f['expected']['m']); + expect(_toString(p2ms.data.n), f['expected']['n']); + expect(_toString(p2ms.data.output), f['expected']['output']); + expect(_toString(p2ms.data.pubkeys), f['expected']['pubkeys']); + expect(_toString(p2ms.data.signatures), f['expected']['signatures']); + expect(_toString(p2ms.data.input), f['expected']['input']); + expect(_toString(p2ms.data.witness), f['expected']['witness']); + + }); + }); + }); + group('(invalid case)', () { + (fixtures["invalid"] as List).forEach((f) { + test('throws ' + f['exception'] + (f['description'] != null ? ('for ' + f['description']) : ''), () { + final arguments = _preformP2MS(f['arguments']); + try { + expect(new P2MS(data: arguments), isArgumentError); + } catch(err) { + expect((err as ArgumentError).message, f['exception']); + } + + }); + }); + }); +} +P2MSData _preformP2MS(dynamic x) { + final m = x['m']; + final n = x['n']; + final input = x['input'] != null ? bscript.fromASM(x['input']) : null; + final output = x['output'] != null ? bscript.fromASM(x['output']) : x['outputHex'] != null ? HEX.decode(x['outputHex']) : null; + + final pubkeys = x['pubkeys']; + final signatures = x['signatures']; + final witness = x['witness']; + final options = x['options']; + + return new P2MSData(m: m, n: n, input: input, output: output, pubkeys: pubkeys, signatures: signatures, witness: witness, options: options); +} +String _toString(dynamic x) { + if (x == null) { + return null; + } + if (x is Uint8List) { + return HEX.encode(x); + } + if (x is List) { + return bscript.toASM(x); + } + return ''; +} From 098730f208d27b8b6acab9039deca0f569a8a988 Mon Sep 17 00:00:00 2001 From: Don Gunn Date: Mon, 31 Dec 2018 01:16:31 -0800 Subject: [PATCH 06/17] Working 2 test cases --- lib/src/payments/p2ms.dart | 79 ++++++++++++++++++++++++------------ test/payments/p2ms_test.dart | 18 +++++--- 2 files changed, 67 insertions(+), 30 deletions(-) diff --git a/lib/src/payments/p2ms.dart b/lib/src/payments/p2ms.dart index 1de3cb7..528ad02 100644 --- a/lib/src/payments/p2ms.dart +++ b/lib/src/payments/p2ms.dart @@ -4,35 +4,70 @@ import '../utils/script.dart' as bscript; import '../models/networks.dart'; import 'dart:typed_data'; - - - class P2MS { P2MSData data; NetworkType network; List _chunks; + Map _temp; + bool _isDecoded; P2MS({@required data}) { this.data = data; this.network = network ?? bitcoin; + this._isDecoded = false; _init(); } void _init() { _enoughInformation(data); + _extraValidation(data.options); + _setNetwork(network); + _extendedValidation(); } + void _enoughInformation(data) { - if ( - !data.input && - !data.output && - !(data.pubkeys && data.m != null) && - !data.signatures - ) throw new ArgumentError('Not enough data'); + if (data.input == null && + data.output == null && + !((data.pubkeys != null) && (data.m != null)) && + data.signatures == null) { + throw new ArgumentError('Not enough data'); + } + } + void _setNetwork(network){ + _temp[network] == network; } - void _extraValidation(options){ + void _extraValidation(options) { options['validate'] = true; } - bool _isAcceptableSignature(signature,options) { - return bscript.isCanonicalScriptSignature(signature) || (options.allowIncomplete && (signature == OPS['OP_0'])); + + void _extendedValidation(){ + if(data.options['validate']==true){ + _checkDataOutput(); + + } + } + void _decode(){ + if(_isDecoded) {return;} + else{ + _isDecoded = true; + _chunks = bscript.decompile(data.input); + _temp['m'] = _chunks[0] - OPS['OP_INT_BASE'] ; + _temp['n'] = _chunks[_chunks.length - 2] - OPS['OP_INT_BASE']; + _temp['pubkeys'] = _chunks.sublist(1,_chunks.length-2); + } + } + void _checkDataOutput(){ + if (data.output != null){ + _decode(); + if (!typef.Number(chunks[0])) throw new TypeError('Output is invalid') + } + + } + + + + bool _isAcceptableSignature(signature, options) { + return bscript.isCanonicalScriptSignature(signature) || + (options.allowIncomplete && (signature == OPS['OP_0'])); } /* void _decode(output) { _chunks = bscript.decompile(output); @@ -42,14 +77,13 @@ class P2MS { } */ bool _stacksEqual(a, b) { if (a.length != b.length) return false; - for(int i=1;i<=a.length;i++) { - if (a[i]!=b[i]) { + for (int i = 1; i <= a.length; i++) { + if (a[i] != b[i]) { return false; } } return true; } - } class P2MSData { @@ -57,12 +91,12 @@ class P2MSData { int n; Uint8List output; Uint8List input; - List pubkeys; - List signatures; + List pubkeys; + List signatures; Uint8List witness; Map options; - - P2MSData( + + P2MSData( {this.m, this.n, this.output, @@ -74,10 +108,5 @@ class P2MSData { @override String toString() { return 'P2MSData{sigs: $m, neededSigs: $n, output: $output, input: $input, pubkeys: $pubkeys, sigs: $signatures, options: $signatures, witness: $witness}'; - } - + } } - - - - diff --git a/test/payments/p2ms_test.dart b/test/payments/p2ms_test.dart index 19c8447..59ef34a 100644 --- a/test/payments/p2ms_test.dart +++ b/test/payments/p2ms_test.dart @@ -7,7 +7,7 @@ import 'package:hex/hex.dart'; import 'dart:typed_data'; main() { final fixtures = json.decode(new File("./test/fixtures/p2ms.json").readAsStringSync(encoding: utf8)); - group('(valid case)', () { +/* group('(valid case)', () { (fixtures["valid"] as List).forEach((f) { test(f['description'] + ' as expected', () { final arguments = _preformP2MS(f['arguments']); @@ -23,7 +23,7 @@ main() { }); }); - }); + }); */ group('(invalid case)', () { (fixtures["invalid"] as List).forEach((f) { test('throws ' + f['exception'] + (f['description'] != null ? ('for ' + f['description']) : ''), () { @@ -31,6 +31,7 @@ main() { try { expect(new P2MS(data: arguments), isArgumentError); } catch(err) { + print(err); expect((err as ArgumentError).message, f['exception']); } @@ -39,18 +40,25 @@ main() { }); } P2MSData _preformP2MS(dynamic x) { - final m = x['m']; - final n = x['n']; + final m = x['m'] != null ? x['m'] : null; + final n = x['n'] != null ? x['n'] : null; final input = x['input'] != null ? bscript.fromASM(x['input']) : null; final output = x['output'] != null ? bscript.fromASM(x['output']) : x['outputHex'] != null ? HEX.decode(x['outputHex']) : null; + final pubkeys = x['pubkeys']!= null ? convertToList(x['pubkeys']) : null; - final pubkeys = x['pubkeys']; final signatures = x['signatures']; final witness = x['witness']; final options = x['options']; return new P2MSData(m: m, n: n, input: input, output: output, pubkeys: pubkeys, signatures: signatures, witness: witness, options: options); } +List convertToList(dynamic x){ + List properList = []; + for( var i = 0; i < x.length; i++ ) { + properList.add(HEX.decode(x[i])); + } + return properList;} + String _toString(dynamic x) { if (x == null) { return null; From 3367d86dbab1719b44eb0469cb02f47fd2d4c504 Mon Sep 17 00:00:00 2001 From: Don Gunn Date: Mon, 31 Dec 2018 16:59:36 -0800 Subject: [PATCH 07/17] All Fubctions created --- lib/src/payments/p2ms.dart | 111 +++++++++++++++++++++++++++++-------- 1 file changed, 89 insertions(+), 22 deletions(-) diff --git a/lib/src/payments/p2ms.dart b/lib/src/payments/p2ms.dart index 528ad02..b9cc009 100644 --- a/lib/src/payments/p2ms.dart +++ b/lib/src/payments/p2ms.dart @@ -3,6 +3,7 @@ import 'package:meta/meta.dart'; import '../utils/script.dart' as bscript; import '../models/networks.dart'; import 'dart:typed_data'; +import 'package:bip32/src/utils/ecurve.dart' show isPoint; class P2MS { P2MSData data; @@ -36,7 +37,7 @@ class P2MS { _temp[network] == network; } void _extraValidation(options) { - options['validate'] = true; + data.options['validate'] = true; } void _extendedValidation(){ @@ -45,45 +46,111 @@ class P2MS { } } - void _decode(){ + void _decode(output){ if(_isDecoded) {return;} else{ _isDecoded = true; - _chunks = bscript.decompile(data.input); + _chunks = bscript.decompile(output); _temp['m'] = _chunks[0] - OPS['OP_INT_BASE'] ; _temp['n'] = _chunks[_chunks.length - 2] - OPS['OP_INT_BASE']; _temp['pubkeys'] = _chunks.sublist(1,_chunks.length-2); } } - void _checkDataOutput(){ - if (data.output != null){ - _decode(); - if (!typef.Number(chunks[0])) throw new TypeError('Output is invalid') - } - + void _setOutput(){ + if (data.m == null){ return;} + if (data.m == null) {return;} + if (data.pubkeys == null) {return;} + _temp['output'] = bscript.compile([ + OPS['OP_INT_BASE']+ data.m, + data.pubkeys, + OPS['OP_INT_BASE']+ _temp['n'], + OPS['OP_CHECKMULTISIG'] + ]); + } + void _setSigs(){ + if (data.input == null) {return;} + var list = bscript.decompile(data.input); + _temp['signatures']=list.sublist(1,list.length); + } + void _setInput(){ + if (data.signatures == null) {return;} + _temp['input'] = bscript.compile( [OPS['OP_0'],data.signatures]); + } + void _setWitness(){ + if (_temp['input'] == null) {return;} + _temp['witness'] = []; } - - - bool _isAcceptableSignature(signature, options) { - return bscript.isCanonicalScriptSignature(signature) || - (options.allowIncomplete && (signature == OPS['OP_0'])); + void _setM(){ + _setOutput(); + if (_temp['output'] == null) return + _decode(_temp['output']); + } + void _setN(){ + _setPubkeys(); + if (_temp['output'] == null) return + _temp['n']= _temp['pubkeys'].length; + } + void _setPubkeys(){ + if (data.output == null) return + _decode(data.output); } -/* void _decode(output) { - _chunks = bscript.decompile(output); - _tempItem.m = _chunks[0] - OPS['OP_INT_BASE']; - _tempItem.n = _chunks[_chunks.length - 2] - OPS['OP_INT_BASE']; - _tempItem.pubkeys = _chunks.sublist(1, -3); - } */ - bool _stacksEqual(a, b) { + bool _stacksEqual(a, b) { if (a.length != b.length) return false; - for (int i = 1; i <= a.length; i++) { + for (int i = 0; i <= a.length-1; i++) { if (a[i] != b[i]) { return false; } } return true; } + bool _isAcceptableSignature(signature, options) { + return bscript.isCanonicalScriptSignature(signature) || + (options.allowIncomplete && (signature == OPS['OP_0'])); + } + void _checkDataOutput(){ + if (data.output != null){ + _decode(data.output); + if ((_chunks[0].runtimeType is! int)) throw new ArgumentError('Output is invalid'); + if (_chunks[_chunks.length - 2] is! int) throw new ArgumentError('Output is invalid'); + if (_chunks[_chunks.length - 1] != OPS['OP_CHECKMULTISIG']) throw new ArgumentError('Output is invalid'); + _setM(); + _setN(); + if(_temp['m'] <= 0 || + _temp['n'] > 16 || + _temp['m'] > _temp['n'] || + _temp['n'] != _chunks.length - 3) {throw new ArgumentError('Output is invalid');} + if (!_temp['pubkeys'].every((x) => isPoint(x))) {throw new ArgumentError('Output is invalid');} + if (data.m != null && data.m != _temp['m']) throw new ArgumentError('m mismatch'); + if (data.n != null && data.n != _temp['n']) throw new ArgumentError('n mismatch'); + if (data.pubkeys != null && !_stacksEqual(data.pubkeys, _temp['pubkeys'])) throw new ArgumentError('Pubkeys mismatch'); + } + if (data.pubkeys != null){ + if (data.n != null && data.n != data.pubkeys.length) throw new ArgumentError('Pubkey count mismatch'); + _temp['n'] = data.pubkeys.length; + _setM(); + if (_temp['n'] < _temp['m']) throw new ArgumentError('Pubkey count cannot be less than m'); + } + if (data.signatures != null) { + _setM(); + if (data.signatures.length < _temp['m']) throw new ArgumentError('Not enough signatures provided'); + if (data.signatures.length > _temp['m']) throw new ArgumentError('Too many signatures provided'); + } + if (data.input != null) { + if (data.input[0] != OPS['OPS.OP_0']) throw new ArgumentError('Input is invalid'); + if (_temp['signatures'].length == 0 || !_temp['signatures'].every((x) => _isAcceptableSignature(x,data.options))) + throw new ArgumentError('Input has invalid signature(s)'); + + if (data.signatures != null&& !_stacksEqual(data.signatures,_temp['signatures'])) throw new ArgumentError('Signature mismatch'); + if (data.m != null && data.m != data.signatures.length) throw new ArgumentError('Signature count mismatch'); + } + + } + + + + + } class P2MSData { From 3640412f2687eba3d5bcc10e3fcd4713d47efcd4 Mon Sep 17 00:00:00 2001 From: Don Gunn Date: Thu, 3 Jan 2019 04:49:42 -0800 Subject: [PATCH 08/17] Large Test Progress --- lib/src/payments/p2ms.dart | 93 ++++++++++++++++++++---------------- test/payments/p2ms_test.dart | 4 +- 2 files changed, 55 insertions(+), 42 deletions(-) diff --git a/lib/src/payments/p2ms.dart b/lib/src/payments/p2ms.dart index b9cc009..d3fad36 100644 --- a/lib/src/payments/p2ms.dart +++ b/lib/src/payments/p2ms.dart @@ -4,7 +4,8 @@ import '../utils/script.dart' as bscript; import '../models/networks.dart'; import 'dart:typed_data'; import 'package:bip32/src/utils/ecurve.dart' show isPoint; - +//Notes +//OP_Reserved list = [OPS['OP_RESERVED']+data.m]; + data.pubkeys.forEach((pubkey) => list.add(pubkey)); + list.add(OPS['OP_RESERVED'] + _temp['n']); + list.add(OPS['OP_CHECKMULTISIG']); + _temp['output'] = bscript.compile(list); } void _setSigs(){ if (data.input == null) {return;} @@ -82,21 +86,19 @@ class P2MS { } void _setM(){ - _setOutput(); - if (_temp['output'] == null) return + if (_temp['output'] == null) {return;} _decode(_temp['output']); } void _setN(){ - _setPubkeys(); - if (_temp['output'] == null) return + if (_temp['pubkeys'] == null) {return;} _temp['n']= _temp['pubkeys'].length; } void _setPubkeys(){ - if (data.output == null) return + if (data.output == null) {return;} _decode(data.output); } bool _stacksEqual(a, b) { - if (a.length != b.length) return false; + if (a.length != b.length) {return false;} for (int i = 0; i <= a.length-1; i++) { if (a[i] != b[i]) { return false; @@ -105,44 +107,54 @@ class P2MS { return true; } bool _isAcceptableSignature(signature, options) { - return bscript.isCanonicalScriptSignature(signature) || - (options.allowIncomplete && (signature == OPS['OP_0'])); + return (bscript.isCanonicalScriptSignature(signature) || + (options.allowIncomplete && (signature == OPS['OP_0']))); } - void _checkDataOutput(){ + void _check(){ + //_setPubkeys(); + // _setSigs(); + // _setInput(); + + // _setOutput(); + // _setM(); + + // _setN(); + // _setWitness(); + if (data.output != null){ + final temp = bscript.decompile(data.output); + if (temp[0] == null) {throw new ArgumentError('Output is invalid');} + if (temp.length < 2 ) {throw new ArgumentError('Output is invalid');} + if (temp[temp.length - 1] != OPS['OP_CHECKMULTISIG']) {throw new ArgumentError('Output is invalid');} _decode(data.output); - if ((_chunks[0].runtimeType is! int)) throw new ArgumentError('Output is invalid'); - if (_chunks[_chunks.length - 2] is! int) throw new ArgumentError('Output is invalid'); - if (_chunks[_chunks.length - 1] != OPS['OP_CHECKMULTISIG']) throw new ArgumentError('Output is invalid'); - _setM(); - _setN(); if(_temp['m'] <= 0 || _temp['n'] > 16 || _temp['m'] > _temp['n'] || _temp['n'] != _chunks.length - 3) {throw new ArgumentError('Output is invalid');} if (!_temp['pubkeys'].every((x) => isPoint(x))) {throw new ArgumentError('Output is invalid');} - if (data.m != null && data.m != _temp['m']) throw new ArgumentError('m mismatch'); - if (data.n != null && data.n != _temp['n']) throw new ArgumentError('n mismatch'); - if (data.pubkeys != null && !_stacksEqual(data.pubkeys, _temp['pubkeys'])) throw new ArgumentError('Pubkeys mismatch'); + if (data.m != null && data.m != _temp['m']) {throw new ArgumentError('m mismatch');} + if (data.n != null && data.n != _temp['n']) {throw new ArgumentError('n mismatch');} + if (data.pubkeys != null && !_stacksEqual(data.pubkeys, _temp['pubkeys'])) {throw new ArgumentError('Pubkeys mismatch');} } if (data.pubkeys != null){ - if (data.n != null && data.n != data.pubkeys.length) throw new ArgumentError('Pubkey count mismatch'); + if (data.n != null && data.n != data.pubkeys.length) {throw new ArgumentError('Pubkey count mismatch');} _temp['n'] = data.pubkeys.length; + _setOutput(); _setM(); - if (_temp['n'] < _temp['m']) throw new ArgumentError('Pubkey count cannot be less than m'); + if (_temp['n'] < _temp['m']) {throw new ArgumentError('Pubkey count cannot be less than m');} } if (data.signatures != null) { - _setM(); - if (data.signatures.length < _temp['m']) throw new ArgumentError('Not enough signatures provided'); - if (data.signatures.length > _temp['m']) throw new ArgumentError('Too many signatures provided'); + + if (data.signatures.length < _temp['m']) {throw new ArgumentError('Not enough signatures provided');} + if (data.signatures.length > _temp['m']) {throw new ArgumentError('Too many signatures provided');} } if (data.input != null) { - if (data.input[0] != OPS['OPS.OP_0']) throw new ArgumentError('Input is invalid'); + if (data.input[0] != OPS['OPS.OP_0']) {throw new ArgumentError('Input is invalid');} if (_temp['signatures'].length == 0 || !_temp['signatures'].every((x) => _isAcceptableSignature(x,data.options))) - throw new ArgumentError('Input has invalid signature(s)'); + {throw new ArgumentError('Input has invalid signature(s)');} - if (data.signatures != null&& !_stacksEqual(data.signatures,_temp['signatures'])) throw new ArgumentError('Signature mismatch'); - if (data.m != null && data.m != data.signatures.length) throw new ArgumentError('Signature count mismatch'); + if (data.signatures != null&& !_stacksEqual(data.signatures,_temp['signatures'])) {throw new ArgumentError('Signature mismatch');} + if (data.m != null && data.m != data.signatures.length) {throw new ArgumentError('Signature count mismatch');} } } @@ -153,6 +165,7 @@ class P2MS { } + class P2MSData { int m; int n; diff --git a/test/payments/p2ms_test.dart b/test/payments/p2ms_test.dart index 59ef34a..15a2401 100644 --- a/test/payments/p2ms_test.dart +++ b/test/payments/p2ms_test.dart @@ -30,8 +30,8 @@ main() { final arguments = _preformP2MS(f['arguments']); try { expect(new P2MS(data: arguments), isArgumentError); - } catch(err) { - print(err); + } catch(err,stacktrace) { + print(stacktrace); expect((err as ArgumentError).message, f['exception']); } From 2ea98f6e0fec657b160b4ad58354f306b8e0c1f5 Mon Sep 17 00:00:00 2001 From: Don Gunn Date: Tue, 8 Jan 2019 15:39:06 -0800 Subject: [PATCH 09/17] More test passing --- lib/src/payments/p2ms.dart | 10 ++++------ test/payments/p2ms_test.dart | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/src/payments/p2ms.dart b/lib/src/payments/p2ms.dart index d3fad36..4180084 100644 --- a/lib/src/payments/p2ms.dart +++ b/lib/src/payments/p2ms.dart @@ -144,12 +144,14 @@ class P2MS { if (_temp['n'] < _temp['m']) {throw new ArgumentError('Pubkey count cannot be less than m');} } if (data.signatures != null) { - + _setSigs(); if (data.signatures.length < _temp['m']) {throw new ArgumentError('Not enough signatures provided');} if (data.signatures.length > _temp['m']) {throw new ArgumentError('Too many signatures provided');} } + _setInput(); + _setSigs(); if (data.input != null) { - if (data.input[0] != OPS['OPS.OP_0']) {throw new ArgumentError('Input is invalid');} + if (data.input[0] != OPS['OP_0']) {throw new ArgumentError('Input is invalid');} if (_temp['signatures'].length == 0 || !_temp['signatures'].every((x) => _isAcceptableSignature(x,data.options))) {throw new ArgumentError('Input has invalid signature(s)');} @@ -159,10 +161,6 @@ class P2MS { } - - - - } diff --git a/test/payments/p2ms_test.dart b/test/payments/p2ms_test.dart index 15a2401..0d2f816 100644 --- a/test/payments/p2ms_test.dart +++ b/test/payments/p2ms_test.dart @@ -45,8 +45,8 @@ P2MSData _preformP2MS(dynamic x) { final input = x['input'] != null ? bscript.fromASM(x['input']) : null; final output = x['output'] != null ? bscript.fromASM(x['output']) : x['outputHex'] != null ? HEX.decode(x['outputHex']) : null; final pubkeys = x['pubkeys']!= null ? convertToList(x['pubkeys']) : null; - - final signatures = x['signatures']; + final signatures = x['signatures']!= null ? convertToList(x['signatures']) : null; + final witness = x['witness']; final options = x['options']; From c1e1842cc3117a7fe3ee04d31f8aa5c65de1e34c Mon Sep 17 00:00:00 2001 From: Don Gunn Date: Sun, 13 Jan 2019 18:44:02 -0800 Subject: [PATCH 10/17] Passed all exceptions --- lib/src/payments/p2ms.dart | 25 ++++++++++--------------- test/fixtures/p2ms.json | 2 +- test/payments/p2ms_test.dart | 34 +++++++++++++++++++--------------- 3 files changed, 30 insertions(+), 31 deletions(-) diff --git a/lib/src/payments/p2ms.dart b/lib/src/payments/p2ms.dart index 4180084..543feff 100644 --- a/lib/src/payments/p2ms.dart +++ b/lib/src/payments/p2ms.dart @@ -47,14 +47,13 @@ class P2MS { if(data.options['validate']==true){ _check(); } + _assignVariables(); } void _decode(output){ if(_isDecoded) {return;} else{ - _isDecoded = true; - _chunks = bscript.decompile(output); _temp['m'] = _chunks[0] - OPS['OP_RESERVED']; _temp['n'] = _chunks[_chunks.length - 2] - OPS['OP_RESERVED']; @@ -96,6 +95,12 @@ class P2MS { void _setPubkeys(){ if (data.output == null) {return;} _decode(data.output); + } + void _assignVariables(){ + if (_temp['m'] != null) {data.m = _temp['m'];} + if (_temp['n'] != null) {data.n = _temp['n'];} + if (_temp['output'] != null) {data.output = _temp['output'];} + if (_temp['pubkeys'] != null) {data.pubkeys = _temp['pubkeys'];} } bool _stacksEqual(a, b) { if (a.length != b.length) {return false;} @@ -108,18 +113,9 @@ class P2MS { } bool _isAcceptableSignature(signature, options) { return (bscript.isCanonicalScriptSignature(signature) || - (options.allowIncomplete && (signature == OPS['OP_0']))); + ((options['allowIncomplete'] == true) && (signature == OPS['OP_0']))); } void _check(){ - //_setPubkeys(); - // _setSigs(); - // _setInput(); - - // _setOutput(); - // _setM(); - - // _setN(); - // _setWitness(); if (data.output != null){ final temp = bscript.decompile(data.output); @@ -158,7 +154,6 @@ class P2MS { if (data.signatures != null&& !_stacksEqual(data.signatures,_temp['signatures'])) {throw new ArgumentError('Signature mismatch');} if (data.m != null && data.m != data.signatures.length) {throw new ArgumentError('Signature count mismatch');} } - } } @@ -169,7 +164,7 @@ class P2MSData { int n; Uint8List output; Uint8List input; - List pubkeys; + List pubkeys; List signatures; Uint8List witness; Map options; @@ -185,6 +180,6 @@ class P2MSData { this.options}); @override String toString() { - return 'P2MSData{sigs: $m, neededSigs: $n, output: $output, input: $input, pubkeys: $pubkeys, sigs: $signatures, options: $signatures, witness: $witness}'; + return 'P2MSData{m: $m, n: $n, output: $output, input: $input, pubkeys: $pubkeys, sigs: $signatures, options: $signatures, witness: $witness}'; } } diff --git a/test/fixtures/p2ms.json b/test/fixtures/p2ms.json index 9ff2611..5407548 100644 --- a/test/fixtures/p2ms.json +++ b/test/fixtures/p2ms.json @@ -339,7 +339,7 @@ } }, { - "exception": "Input has invalid signature\\(s\\)", + "exception": "Input has invalid signature(s)", "arguments": { "m": 1, "pubkeys": [ diff --git a/test/payments/p2ms_test.dart b/test/payments/p2ms_test.dart index 0d2f816..40bd896 100644 --- a/test/payments/p2ms_test.dart +++ b/test/payments/p2ms_test.dart @@ -7,31 +7,30 @@ import 'package:hex/hex.dart'; import 'dart:typed_data'; main() { final fixtures = json.decode(new File("./test/fixtures/p2ms.json").readAsStringSync(encoding: utf8)); -/* group('(valid case)', () { + group('(valid case)', () { (fixtures["valid"] as List).forEach((f) { test(f['description'] + ' as expected', () { final arguments = _preformP2MS(f['arguments']); final p2ms = new P2MS(data: arguments); - expect(p2ms.data.m, f['expected']['m']); - expect(_toString(p2ms.data.n), f['expected']['n']); - expect(_toString(p2ms.data.output), f['expected']['output']); - expect(_toString(p2ms.data.pubkeys), f['expected']['pubkeys']); - expect(_toString(p2ms.data.signatures), f['expected']['signatures']); - expect(_toString(p2ms.data.input), f['expected']['input']); - expect(_toString(p2ms.data.witness), f['expected']['witness']); + expect(p2ms.data.n, f['expected']['n']); + expect(_toDataForm(p2ms.data.output), f['expected']['output']); + expect(p2ms.data.pubkeys, (convertToList(f['expected']['pubkeys']))); + print('ran'); + expect(_toDataForm(p2ms.data.signatures), f['expected']['signatures']); + expect(_toDataForm(p2ms.data.input), f['expected']['input']); + expect(_toDataForm(p2ms.data.witness), f['expected']['witness']); }); }); - }); */ + }); group('(invalid case)', () { (fixtures["invalid"] as List).forEach((f) { test('throws ' + f['exception'] + (f['description'] != null ? ('for ' + f['description']) : ''), () { final arguments = _preformP2MS(f['arguments']); try { expect(new P2MS(data: arguments), isArgumentError); - } catch(err,stacktrace) { - print(stacktrace); + } catch(err) { expect((err as ArgumentError).message, f['exception']); } @@ -59,15 +58,20 @@ List convertToList(dynamic x){ } return properList;} -String _toString(dynamic x) { +dynamic _toDataForm(dynamic x) { if (x == null) { return null; } - if (x is Uint8List) { - return HEX.encode(x); + if (x is Uint8List) {//used + return bscript.toASM(x); } if (x is List) { - return bscript.toASM(x); + List temp = []; + for (var i = 0 ; i < x.length; i++ ) { + temp.add(x[0]); + } + + return temp; } return ''; } From 5ba20582bad124031742de84c77c27f28b587c7d Mon Sep 17 00:00:00 2001 From: Don Gunn Date: Tue, 15 Jan 2019 02:17:47 -0800 Subject: [PATCH 11/17] More testing progress --- lib/src/payments/p2ms.dart | 41 +++++++++++++++++++++++++++++------- test/payments/p2ms_test.dart | 17 +++++++++------ 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/lib/src/payments/p2ms.dart b/lib/src/payments/p2ms.dart index 543feff..ad4e026 100644 --- a/lib/src/payments/p2ms.dart +++ b/lib/src/payments/p2ms.dart @@ -73,15 +73,34 @@ class P2MS { void _setSigs(){ if (data.input == null) {return;} var list = bscript.decompile(data.input); - _temp['signatures']=list.sublist(1,list.length); + list.removeAt(0); + List uintList = []; + for (var i = 0; i < list.length; i++) { + uintList.add(list[i]); + } + + _temp['signatures'] = uintList; + + //print(bscript.toASM(_temp['signatures'])); } void _setInput(){ + print(data.signatures); + print('ran'); if (data.signatures == null) {return;} - _temp['input'] = bscript.compile( [OPS['OP_0'],data.signatures]); + String tempString = 'OP_0 '; + tempString = tempString+(bscript.toASM(data.signatures)); + //print(tempString); + Uint8List tempList = bscript.fromASM(tempString); + _temp['input'] = bscript.compile(tempList); + _setWitness(); } void _setWitness(){ + //print(_temp['input']); if (_temp['input'] == null) {return;} - _temp['witness'] = []; + List temp = []; + _temp['witness'] = temp; + + } void _setM(){ @@ -101,6 +120,9 @@ class P2MS { if (_temp['n'] != null) {data.n = _temp['n'];} if (_temp['output'] != null) {data.output = _temp['output'];} if (_temp['pubkeys'] != null) {data.pubkeys = _temp['pubkeys'];} + if (_temp['signatures'] != null) {data.signatures = _temp['signatures'];} + if (_temp['input'] != null) {data.input= _temp['input'];} + if (_temp['witness'] != null) {data.witness= _temp['witness'];} } bool _stacksEqual(a, b) { if (a.length != b.length) {return false;} @@ -116,7 +138,6 @@ class P2MS { ((options['allowIncomplete'] == true) && (signature == OPS['OP_0']))); } void _check(){ - if (data.output != null){ final temp = bscript.decompile(data.output); if (temp[0] == null) {throw new ArgumentError('Output is invalid');} @@ -141,20 +162,24 @@ class P2MS { } if (data.signatures != null) { _setSigs(); + _setInput(); if (data.signatures.length < _temp['m']) {throw new ArgumentError('Not enough signatures provided');} if (data.signatures.length > _temp['m']) {throw new ArgumentError('Too many signatures provided');} } - _setInput(); - _setSigs(); + + if (data.input != null) { if (data.input[0] != OPS['OP_0']) {throw new ArgumentError('Input is invalid');} + _setSigs(); if (_temp['signatures'].length == 0 || !_temp['signatures'].every((x) => _isAcceptableSignature(x,data.options))) {throw new ArgumentError('Input has invalid signature(s)');} - if (data.signatures != null&& !_stacksEqual(data.signatures,_temp['signatures'])) {throw new ArgumentError('Signature mismatch');} if (data.m != null && data.m != data.signatures.length) {throw new ArgumentError('Signature count mismatch');} } + _setInput(); + _setWitness(); } + } @@ -166,7 +191,7 @@ class P2MSData { Uint8List input; List pubkeys; List signatures; - Uint8List witness; + List witness; Map options; P2MSData( diff --git a/test/payments/p2ms_test.dart b/test/payments/p2ms_test.dart index 40bd896..42bc689 100644 --- a/test/payments/p2ms_test.dart +++ b/test/payments/p2ms_test.dart @@ -12,12 +12,12 @@ main() { test(f['description'] + ' as expected', () { final arguments = _preformP2MS(f['arguments']); final p2ms = new P2MS(data: arguments); + print(p2ms.data.signatures); expect(p2ms.data.m, f['expected']['m']); expect(p2ms.data.n, f['expected']['n']); expect(_toDataForm(p2ms.data.output), f['expected']['output']); - expect(p2ms.data.pubkeys, (convertToList(f['expected']['pubkeys']))); - print('ran'); - expect(_toDataForm(p2ms.data.signatures), f['expected']['signatures']); + expect(p2ms.data.pubkeys, (_convertToList(f['expected']['pubkeys']))); + expect(p2ms.data.signatures, _convertSigs(f['expected']['signatures'])); expect(_toDataForm(p2ms.data.input), f['expected']['input']); expect(_toDataForm(p2ms.data.witness), f['expected']['witness']); @@ -43,15 +43,18 @@ P2MSData _preformP2MS(dynamic x) { final n = x['n'] != null ? x['n'] : null; final input = x['input'] != null ? bscript.fromASM(x['input']) : null; final output = x['output'] != null ? bscript.fromASM(x['output']) : x['outputHex'] != null ? HEX.decode(x['outputHex']) : null; - final pubkeys = x['pubkeys']!= null ? convertToList(x['pubkeys']) : null; - final signatures = x['signatures']!= null ? convertToList(x['signatures']) : null; - + final pubkeys = x['pubkeys']!= null ? _convertToList(x['pubkeys']) : null; + final signatures = x['signatures']!= null ? _convertToList(x['signatures']) : null; final witness = x['witness']; final options = x['options']; return new P2MSData(m: m, n: n, input: input, output: output, pubkeys: pubkeys, signatures: signatures, witness: witness, options: options); } -List convertToList(dynamic x){ +dynamic _convertSigs(dynamic x){ + if (x == null){return null;} + else{ return (_convertToList(x));} +} +List _convertToList(dynamic x){ List properList = []; for( var i = 0; i < x.length; i++ ) { properList.add(HEX.decode(x[i])); From ecdd7dec92611ef475f9dd52759cb99032e78074 Mon Sep 17 00:00:00 2001 From: Don Gunn Date: Wed, 16 Jan 2019 02:23:01 -0800 Subject: [PATCH 12/17] Passed all test --- .vscode/launch.json | 9 +++++++++ lib/src/payments/p2ms.dart | 36 ++++++++++++++++++++--------------- test/payments/p2ms_test.dart | 37 +++++++++++++++++++++--------------- 3 files changed, 52 insertions(+), 30 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..5b41424 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,9 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + ] +} \ No newline at end of file diff --git a/lib/src/payments/p2ms.dart b/lib/src/payments/p2ms.dart index ad4e026..9827b16 100644 --- a/lib/src/payments/p2ms.dart +++ b/lib/src/payments/p2ms.dart @@ -75,32 +75,38 @@ class P2MS { var list = bscript.decompile(data.input); list.removeAt(0); List uintList = []; + for (var i = 0; i < list.length; i++) { - uintList.add(list[i]); + dynamic temp = list[i]; + if(list[i] is int ){ + List temp1 = []; + temp1.add(list[i]); + temp = Uint8List.fromList(temp1); + } + uintList.add(temp); } _temp['signatures'] = uintList; - - //print(bscript.toASM(_temp['signatures'])); } void _setInput(){ - print(data.signatures); - print('ran'); if (data.signatures == null) {return;} String tempString = 'OP_0 '; - tempString = tempString+(bscript.toASM(data.signatures)); - //print(tempString); + List tempsignatures = []; + for (var i = 0; i < data.signatures.length; i++) { + if(data.signatures[i].toString()=='[0]'){ + tempString = tempString + 'OP_0 '; + }else{ + tempsignatures.add(data.signatures[i]); + } + } + tempString = tempString+(bscript.toASM(tempsignatures)); Uint8List tempList = bscript.fromASM(tempString); - _temp['input'] = bscript.compile(tempList); - _setWitness(); + _temp['input'] = bscript.compile(tempList); } void _setWitness(){ - //print(_temp['input']); - if (_temp['input'] == null) {return;} + if (_temp['input'] == null && data.input == null) {return;} List temp = []; _temp['witness'] = temp; - - } void _setM(){ @@ -133,9 +139,9 @@ class P2MS { } return true; } - bool _isAcceptableSignature(signature, options) { + bool _isAcceptableSignature(signature, options) { return (bscript.isCanonicalScriptSignature(signature) || - ((options['allowIncomplete'] == true) && (signature == OPS['OP_0']))); + ((options['allowIncomplete'] == true) && (signature[0] == 0))); } void _check(){ if (data.output != null){ diff --git a/test/payments/p2ms_test.dart b/test/payments/p2ms_test.dart index 42bc689..93fec67 100644 --- a/test/payments/p2ms_test.dart +++ b/test/payments/p2ms_test.dart @@ -5,21 +5,21 @@ import 'dart:io'; import 'dart:convert'; import 'package:hex/hex.dart'; import 'dart:typed_data'; + main() { final fixtures = json.decode(new File("./test/fixtures/p2ms.json").readAsStringSync(encoding: utf8)); group('(valid case)', () { (fixtures["valid"] as List).forEach((f) { test(f['description'] + ' as expected', () { - final arguments = _preformP2MS(f['arguments']); + final arguments = _preformP2MS(f['arguments'], f['options']); final p2ms = new P2MS(data: arguments); - print(p2ms.data.signatures); - expect(p2ms.data.m, f['expected']['m']); - expect(p2ms.data.n, f['expected']['n']); - expect(_toDataForm(p2ms.data.output), f['expected']['output']); - expect(p2ms.data.pubkeys, (_convertToList(f['expected']['pubkeys']))); - expect(p2ms.data.signatures, _convertSigs(f['expected']['signatures'])); - expect(_toDataForm(p2ms.data.input), f['expected']['input']); - expect(_toDataForm(p2ms.data.witness), f['expected']['witness']); + expect(p2ms.data.m, f['expected']['m']); + expect(p2ms.data.n, f['expected']['n']); + expect(_toDataForm(p2ms.data.output), f['expected']['output']); + expect(p2ms.data.pubkeys, (_convertToList(f['expected']['pubkeys']))); + expect(p2ms.data.signatures, _convertSigs(f['expected']['signatures'])); + expect(_toDataForm(p2ms.data.input), f['expected']['input']); + expect(_toDataForm(p2ms.data.witness), f['expected']['witness']); }); }); @@ -27,7 +27,7 @@ main() { group('(invalid case)', () { (fixtures["invalid"] as List).forEach((f) { test('throws ' + f['exception'] + (f['description'] != null ? ('for ' + f['description']) : ''), () { - final arguments = _preformP2MS(f['arguments']); + final arguments = _preformP2MS(f['arguments'], f['options']); try { expect(new P2MS(data: arguments), isArgumentError); } catch(err) { @@ -38,26 +38,33 @@ main() { }); }); } -P2MSData _preformP2MS(dynamic x) { +P2MSData _preformP2MS(dynamic x, option) { final m = x['m'] != null ? x['m'] : null; final n = x['n'] != null ? x['n'] : null; final input = x['input'] != null ? bscript.fromASM(x['input']) : null; final output = x['output'] != null ? bscript.fromASM(x['output']) : x['outputHex'] != null ? HEX.decode(x['outputHex']) : null; final pubkeys = x['pubkeys']!= null ? _convertToList(x['pubkeys']) : null; - final signatures = x['signatures']!= null ? _convertToList(x['signatures']) : null; + final signatures = x['signatures']!= null ? _convertSigs(x['signatures']) : null; final witness = x['witness']; - final options = x['options']; + final options = option; return new P2MSData(m: m, n: n, input: input, output: output, pubkeys: pubkeys, signatures: signatures, witness: witness, options: options); } dynamic _convertSigs(dynamic x){ if (x == null){return null;} - else{ return (_convertToList(x));} + else{ List properList = []; + for( var i = 0; i < x.length; i++ ) { + if(x[i]==0){properList.add(HEX.decode('0'));} + else{ + properList.add(HEX.decode(x[i]));} + } + return properList;} } List _convertToList(dynamic x){ List properList = []; for( var i = 0; i < x.length; i++ ) { - properList.add(HEX.decode(x[i])); + var temp = x[i]; + properList.add(HEX.decode(temp)); } return properList;} From 87ce22be40dc0aeb3b7bf50479bcf97ff05c9dd6 Mon Sep 17 00:00:00 2001 From: Don Gunn Date: Wed, 16 Jan 2019 14:23:24 -0800 Subject: [PATCH 13/17] Ready for pr --- .vscode/launch.json | 9 --------- lib/src/payments/p2ms.dart | 5 +++-- test/payments/p2ms_test.dart | 2 +- 3 files changed, 4 insertions(+), 12 deletions(-) delete mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 5b41424..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - - ] -} \ No newline at end of file diff --git a/lib/src/payments/p2ms.dart b/lib/src/payments/p2ms.dart index 9827b16..b9c088a 100644 --- a/lib/src/payments/p2ms.dart +++ b/lib/src/payments/p2ms.dart @@ -4,8 +4,7 @@ import '../utils/script.dart' as bscript; import '../models/networks.dart'; import 'dart:typed_data'; import 'package:bip32/src/utils/ecurve.dart' show isPoint; -//Notes -//OP_Reserved) { From ee808d038e36f47fa8d8b8cb529cd81b9f5bd79f Mon Sep 17 00:00:00 2001 From: Don Gunn Date: Fri, 18 Jan 2019 01:55:59 -0800 Subject: [PATCH 14/17] Cleanup --- test/payments/p2ms_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/payments/p2ms_test.dart b/test/payments/p2ms_test.dart index 312db67..91aa4d8 100644 --- a/test/payments/p2ms_test.dart +++ b/test/payments/p2ms_test.dart @@ -16,7 +16,7 @@ main() { expect(p2ms.data.m, f['expected']['m']); expect(p2ms.data.n, f['expected']['n']); expect(_toDataForm(p2ms.data.output), f['expected']['output']); - expect(p2ms.data.pubkeys, (_convertToList(f['expected']['pubkeys']))); + expect(p2ms.data.pubkeys, _convertToList(f['expected']['pubkeys'])); expect(p2ms.data.signatures, _convertSigs(f['expected']['signatures'])); expect(_toDataForm(p2ms.data.input), f['expected']['input']); expect(_toDataForm(p2ms.data.witness), f['expected']['witness']); From 89c47055b9bda7f94bfd2228516e79e4aa9f1a0f Mon Sep 17 00:00:00 2001 From: Don Gunn Date: Sun, 20 Jan 2019 16:40:02 -0800 Subject: [PATCH 15/17] Used proper chunk name https://github.com/anicdh/bitcoin_flutter/pull/1#discussion_r249248553 --- lib/src/payments/p2ms.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/payments/p2ms.dart b/lib/src/payments/p2ms.dart index b9c088a..7153f71 100644 --- a/lib/src/payments/p2ms.dart +++ b/lib/src/payments/p2ms.dart @@ -73,7 +73,7 @@ class P2MS { if (data.input == null) {return;} var list = bscript.decompile(data.input); list.removeAt(0); - List uintList = []; + List _chunks = []; for (var i = 0; i < list.length; i++) { dynamic temp = list[i]; @@ -82,10 +82,10 @@ class P2MS { temp1.add(list[i]); temp = Uint8List.fromList(temp1); } - uintList.add(temp); + _chunks.add(temp); } - _temp['signatures'] = uintList; + _temp['signatures'] = _chunks; } void _setInput(){ if (data.signatures == null) {return;} From 014182049e91efab8145872b2def4b757ffc0308 Mon Sep 17 00:00:00 2001 From: Don Gunn Date: Sun, 20 Jan 2019 20:31:36 -0800 Subject: [PATCH 16/17] Removed _temp, but fails 3 test fails these 3 test starting at lines 146 --- lib/src/payments/p2ms.dart | 70 ++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 41 deletions(-) diff --git a/lib/src/payments/p2ms.dart b/lib/src/payments/p2ms.dart index 7153f71..ee60d54 100644 --- a/lib/src/payments/p2ms.dart +++ b/lib/src/payments/p2ms.dart @@ -9,14 +9,12 @@ class P2MS { P2MSData data; NetworkType network; List _chunks; - Map _temp; bool _isDecoded; P2MS({@required data}) { this.data = data; this.network = network ?? bitcoin; this._isDecoded = false; - this._temp = {}; _init(); } void _init() { @@ -35,7 +33,7 @@ class P2MS { } } void _setNetwork(network){ - _temp['network'] == network; + this.network == network; } void _extraValidation(options) { if(data.options == null){data.options = {};} @@ -46,7 +44,6 @@ class P2MS { if(data.options['validate']==true){ _check(); } - _assignVariables(); } void _decode(output){ @@ -54,20 +51,20 @@ class P2MS { else{ _isDecoded = true; _chunks = bscript.decompile(output); - _temp['m'] = _chunks[0] - OPS['OP_RESERVED']; - _temp['n'] = _chunks[_chunks.length - 2] - OPS['OP_RESERVED']; - _temp['pubkeys'] = _chunks.sublist(1,_chunks.length-2); + data.m = _chunks[0] - OPS['OP_RESERVED']; + data.n = _chunks[_chunks.length - 2] - OPS['OP_RESERVED']; + data.pubkeys = _chunks.sublist(1,_chunks.length-2); } } void _setOutput(){ if (data.m == null){ return;} - if (_temp['n'] == null) {return;} + if (data.n == null) {return;} if (data.pubkeys == null) {return;} List list = [OPS['OP_RESERVED']+data.m]; data.pubkeys.forEach((pubkey) => list.add(pubkey)); - list.add(OPS['OP_RESERVED'] + _temp['n']); + list.add(OPS['OP_RESERVED'] + data.n); list.add(OPS['OP_CHECKMULTISIG']); - _temp['output'] = bscript.compile(list); + data.output = bscript.compile(list); } void _setSigs(){ if (data.input == null) {return;} @@ -85,7 +82,7 @@ class P2MS { _chunks.add(temp); } - _temp['signatures'] = _chunks; + data.signatures = _chunks; } void _setInput(){ if (data.signatures == null) {return;} @@ -100,35 +97,26 @@ class P2MS { } tempString = tempString+(bscript.toASM(tempsignatures)); Uint8List tempList = bscript.fromASM(tempString); - _temp['input'] = bscript.compile(tempList); + data.input = bscript.compile(tempList); } void _setWitness(){ - if (_temp['input'] == null && data.input == null) {return;} + if (data.input == null && data.input == null) {return;} List temp = []; - _temp['witness'] = temp; + data.witness = temp; } void _setM(){ - if (_temp['output'] == null) {return;} - _decode(_temp['output']); + if (data.output == null) {return;} + _decode(data.output); } void _setN(){ _setPubkeys(); - if (_temp['pubkeys'] == null) {return;} - _temp['n']= _temp['pubkeys'].length; + if (data.pubkeys == null) {return;} + data.n= data.pubkeys.length; } void _setPubkeys(){ if (data.output == null) {return;} _decode(data.output); - } - void _assignVariables(){ - if (_temp['m'] != null) {data.m = _temp['m'];} - if (_temp['n'] != null) {data.n = _temp['n'];} - if (_temp['output'] != null) {data.output = _temp['output'];} - if (_temp['pubkeys'] != null) {data.pubkeys = _temp['pubkeys'];} - if (_temp['signatures'] != null) {data.signatures = _temp['signatures'];} - if (_temp['input'] != null) {data.input= _temp['input'];} - if (_temp['witness'] != null) {data.witness= _temp['witness'];} } bool _stacksEqual(a, b) { if (a.length != b.length) {return false;} @@ -150,37 +138,37 @@ class P2MS { if (temp.length < 2 ) {throw new ArgumentError('Output is invalid');} if (temp[temp.length - 1] != OPS['OP_CHECKMULTISIG']) {throw new ArgumentError('Output is invalid');} _decode(data.output); - if(_temp['m'] <= 0 || - _temp['n'] > 16 || - _temp['m'] > _temp['n'] || - _temp['n'] != _chunks.length - 3) {throw new ArgumentError('Output is invalid');} - if (!_temp['pubkeys'].every((x) => isPoint(x))) {throw new ArgumentError('Output is invalid');} - if (data.m != null && data.m != _temp['m']) {throw new ArgumentError('m mismatch');} - if (data.n != null && data.n != _temp['n']) {throw new ArgumentError('n mismatch');} - if (data.pubkeys != null && !_stacksEqual(data.pubkeys, _temp['pubkeys'])) {throw new ArgumentError('Pubkeys mismatch');} + if(data.m <= 0 || + data.n > 16 || + data.m > data.n || + data.n != _chunks.length - 3) {throw new ArgumentError('Output is invalid');} + if (!data.pubkeys.every((x) => isPoint(x))) {throw new ArgumentError('Output is invalid');} + if (data.m != null && data.m != data.m) {throw new ArgumentError('m mismatch');} + if (data.n != null && data.n != data.n) {throw new ArgumentError('n mismatch');} + if (data.pubkeys != null && !_stacksEqual(data.pubkeys, data.pubkeys)) {throw new ArgumentError('Pubkeys mismatch');} } if (data.pubkeys != null){ if (data.n != null && data.n != data.pubkeys.length) {throw new ArgumentError('Pubkey count mismatch');} - _temp['n'] = data.pubkeys.length; + data.n = data.pubkeys.length; _setOutput(); _setM(); _setN(); - if (_temp['n'] < _temp['m']) {throw new ArgumentError('Pubkey count cannot be less than m');} + if (data.n < data.m) {throw new ArgumentError('Pubkey count cannot be less than m');} } if (data.signatures != null) { _setSigs(); _setInput(); - if (data.signatures.length < _temp['m']) {throw new ArgumentError('Not enough signatures provided');} - if (data.signatures.length > _temp['m']) {throw new ArgumentError('Too many signatures provided');} + if (data.signatures.length < data.m) {throw new ArgumentError('Not enough signatures provided');} + if (data.signatures.length > data.m) {throw new ArgumentError('Too many signatures provided');} } if (data.input != null) { if (data.input[0] != OPS['OP_0']) {throw new ArgumentError('Input is invalid');} _setSigs(); - if (_temp['signatures'].length == 0 || !_temp['signatures'].every((x) => _isAcceptableSignature(x,data.options))) + if (data.signatures.length == 0 || !data.signatures.every((x) => _isAcceptableSignature(x,data.options))) {throw new ArgumentError('Input has invalid signature(s)');} - if (data.signatures != null&& !_stacksEqual(data.signatures,_temp['signatures'])) {throw new ArgumentError('Signature mismatch');} + if (data.signatures != null&& !_stacksEqual(data.signatures,data.signatures)) {throw new ArgumentError('Signature mismatch');} if (data.m != null && data.m != data.signatures.length) {throw new ArgumentError('Signature count mismatch');} } _setInput(); From 53e99ae69156cf081de83d20e262492db88bf676 Mon Sep 17 00:00:00 2001 From: Don Gunn Date: Sun, 20 Jan 2019 20:32:22 -0800 Subject: [PATCH 17/17] Added chunks label --- lib/src/payments/p2ms.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/payments/p2ms.dart b/lib/src/payments/p2ms.dart index ee60d54..19b24e1 100644 --- a/lib/src/payments/p2ms.dart +++ b/lib/src/payments/p2ms.dart @@ -133,10 +133,10 @@ class P2MS { } void _check(){ if (data.output != null){ - final temp = bscript.decompile(data.output); - if (temp[0] == null) {throw new ArgumentError('Output is invalid');} - if (temp.length < 2 ) {throw new ArgumentError('Output is invalid');} - if (temp[temp.length - 1] != OPS['OP_CHECKMULTISIG']) {throw new ArgumentError('Output is invalid');} + final tempChunks = bscript.decompile(data.output); + if (tempChunks[0] == null) {throw new ArgumentError('Output is invalid');} + if (tempChunks.length < 2 ) {throw new ArgumentError('Output is invalid');} + if (tempChunks[tempChunks.length - 1] != OPS['OP_CHECKMULTISIG']) {throw new ArgumentError('Output is invalid');} _decode(data.output); if(data.m <= 0 || data.n > 16 ||