-
Notifications
You must be signed in to change notification settings - Fork 122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Preparing for Multi-signature Transactions #1
base: master
Are you sure you want to change the base?
Changes from 14 commits
b67fbbd
9d3efce
f9a2cb4
ccb9980
872d2b7
098730f
3367d86
3640412
2ea98f6
c1e1842
5ba2058
ecdd7de
87ce22b
ee808d0
765aba5
89c4705
ed3e400
0141820
53e99ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
import '../utils/constants/op.dart'; | ||
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; | ||
NetworkType network; | ||
List<dynamic> _chunks; | ||
Map _temp; | ||
bool _isDecoded; | ||
|
||
P2MS({@required data}) { | ||
this.data = data; | ||
this.network = network ?? bitcoin; | ||
this._isDecoded = false; | ||
this._temp = {}; | ||
_init(); | ||
} | ||
void _init() { | ||
_enoughInformation(data); | ||
_extraValidation(data.options); | ||
_setNetwork(this.network); | ||
_extendedValidation(); | ||
} | ||
|
||
void _enoughInformation(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) { | ||
if(data.options == null){data.options = {};} | ||
data.options['validate'] = true; | ||
} | ||
|
||
void _extendedValidation(){ | ||
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']; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use P2MSData, dynamic some cases cause dangerous There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should I the type of _chunks from List? I am unsure where to use P2MSData types, aside from the _temp issue addressed later. |
||
_temp['n'] = _chunks[_chunks.length - 2] - OPS['OP_RESERVED']; | ||
_temp['pubkeys'] = _chunks.sublist(1,_chunks.length-2); | ||
} | ||
} | ||
void _setOutput(){ | ||
if (data.m == null){ return;} | ||
if (_temp['n'] == null) {return;} | ||
if (data.pubkeys == null) {return;} | ||
List<dynamic> 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;} | ||
var list = bscript.decompile(data.input); | ||
list.removeAt(0); | ||
List<Uint8List> uintList = []; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. uintList => chunks |
||
|
||
for (var i = 0; i < list.length; i++) { | ||
dynamic temp = list[i]; | ||
if(list[i] is int ){ | ||
List<int> temp1 = []; | ||
temp1.add(list[i]); | ||
temp = Uint8List.fromList(temp1); | ||
} | ||
uintList.add(temp); | ||
} | ||
|
||
_temp['signatures'] = uintList; | ||
} | ||
void _setInput(){ | ||
if (data.signatures == null) {return;} | ||
String tempString = 'OP_0 '; | ||
List<Uint8List> 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); | ||
} | ||
void _setWitness(){ | ||
if (_temp['input'] == null && data.input == null) {return;} | ||
List <Uint8List> temp = []; | ||
_temp['witness'] = temp; | ||
} | ||
|
||
void _setM(){ | ||
if (_temp['output'] == null) {return;} | ||
_decode(_temp['output']); | ||
} | ||
void _setN(){ | ||
_setPubkeys(); | ||
if (_temp['pubkeys'] == null) {return;} | ||
_temp['n']= _temp['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;} | ||
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'] == true) && (signature[0] == 0))); | ||
} | ||
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');} | ||
_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.pubkeys != null){ | ||
if (data.n != null && data.n != data.pubkeys.length) {throw new ArgumentError('Pubkey count mismatch');} | ||
_temp['n'] = data.pubkeys.length; | ||
_setOutput(); | ||
_setM(); | ||
_setN(); | ||
if (_temp['n'] < _temp['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.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(); | ||
} | ||
|
||
|
||
} | ||
|
||
|
||
class P2MSData { | ||
int m; | ||
int n; | ||
Uint8List output; | ||
Uint8List input; | ||
List<dynamic> pubkeys; | ||
List<Uint8List> signatures; | ||
List<Uint8List> witness; | ||
Map options; | ||
|
||
P2MSData( | ||
{this.m, | ||
this.n, | ||
this.output, | ||
this.input, | ||
this.pubkeys, | ||
this.signatures, | ||
this.witness, | ||
this.options}); | ||
@override | ||
String toString() { | ||
return 'P2MSData{m: $m, n: $n, output: $output, input: $input, pubkeys: $pubkeys, sigs: $signatures, options: $signatures, witness: $witness}'; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_temp does not make sense
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean that _temp should not exist and that I should change the value of P2MSData data directly?
The following code:
_temp['m'] = _chunks[0] - OPS['OP_RESERVED'];
should be:
data.m = _chunks[0] - OPS['OP_RESERVED'];
instead?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you're right 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed all _temp usage, but it has caused the code to regress and fail 3 test. I reference this regression in this commit. I am unsure of how to fix this. I don't think we need this test anymore, but it feels wrong to delete test, in response to code regression.