diff --git a/app/constants/Decred.js b/app/constants/Decred.js index 8c8966bec8..3377302fc4 100644 --- a/app/constants/Decred.js +++ b/app/constants/Decred.js @@ -116,21 +116,7 @@ export const STSchnorrSecp256k1 = 2; // ripemd160Size is the size of the RIPEMD-160 hash algorithm checksum in bytes. export const ripemd160Size = 20; -// OP_CODES -export const OP_0 = 0x00; // 0 -export const OP_1 = 0x51; // 81 - AKA OP_TRUE -export const OP_16 = 0x60; // 96 -export const OP_DUP = 0x76; // 118 -export const OP_HASH160 = 0xa9; // 169 -export const OP_DATA_20 = 0x14; // 20 -export const OP_EQUAL = 0x87; // 135 -export const OP_EQUALVERIFY = 0x88; // 136 -export const OP_CHECKSIG = 0xac; // 172 -export const OP_SSTX = 0xba; // 186 DECRED -export const OP_SSGEN = 0xbb; // 187 DECRED -export const OP_SSRTX = 0xbc; // 188 DECRED -export const OP_SSTXCHANGE = 0xbd; // 189 DECRED -export const OP_DATA_33 = 0x21; // 33 -export const OP_DATA_65 = 0x41; // 65 -export const OP_CHECKSIGALT = 0xbe; // 190 DECRED -export const OP_DATA_45 = 0x2d; // 45 +// SStxPKHMinOutSize is the minimum size of an OP_RETURN commitment output +// for an SStx tx. +// 20 bytes P2SH/P2PKH + 8 byte amount + 4 byte fee range limits +export const SStxPKHMinOutSize = 32; diff --git a/app/constants/index.js b/app/constants/index.js index 1a0576c6b4..efbb8286f2 100644 --- a/app/constants/index.js +++ b/app/constants/index.js @@ -2,3 +2,4 @@ export * from "./Decred"; export * from "./Decrediton"; export * from "./Messages"; export * from "./config"; +export * from "./opcode"; diff --git a/app/constants/opcode.js b/app/constants/opcode.js new file mode 100644 index 0000000000..b8c7be58c6 --- /dev/null +++ b/app/constants/opcode.js @@ -0,0 +1,526 @@ +// These constants are the values of the official opcodes used on the btc wiki, +// in bitcoin core and in most if not all other references and software related +// to handling DCR scripts. +export const OP_0 = 0x00; // 0 +export const OP_FALSE = 0x00; // 0 - AKA OP_0 +export const OP_DATA_1 = 0x01; // 1 +export const OP_DATA_2 = 0x02; // 2 +export const OP_DATA_3 = 0x03; // 3 +export const OP_DATA_4 = 0x04; // 4 +export const OP_DATA_5 = 0x05; // 5 +export const OP_DATA_6 = 0x06; // 6 +export const OP_DATA_7 = 0x07; // 7 +export const OP_DATA_8 = 0x08; // 8 +export const OP_DATA_9 = 0x09; // 9 +export const OP_DATA_10 = 0x0a; // 10 +export const OP_DATA_11 = 0x0b; // 11 +export const OP_DATA_12 = 0x0c; // 12 +export const OP_DATA_13 = 0x0d; // 13 +export const OP_DATA_14 = 0x0e; // 14 +export const OP_DATA_15 = 0x0f; // 15 +export const OP_DATA_16 = 0x10; // 16 +export const OP_DATA_17 = 0x11; // 17 +export const OP_DATA_18 = 0x12; // 18 +export const OP_DATA_19 = 0x13; // 19 +export const OP_DATA_20 = 0x14; // 20 +export const OP_DATA_21 = 0x15; // 21 +export const OP_DATA_22 = 0x16; // 22 +export const OP_DATA_23 = 0x17; // 23 +export const OP_DATA_24 = 0x18; // 24 +export const OP_DATA_25 = 0x19; // 25 +export const OP_DATA_26 = 0x1a; // 26 +export const OP_DATA_27 = 0x1b; // 27 +export const OP_DATA_28 = 0x1c; // 28 +export const OP_DATA_29 = 0x1d; // 29 +export const OP_DATA_30 = 0x1e; // 30 +export const OP_DATA_31 = 0x1f; // 31 +export const OP_DATA_32 = 0x20; // 32 +export const OP_DATA_33 = 0x21; // 33 +export const OP_DATA_34 = 0x22; // 34 +export const OP_DATA_35 = 0x23; // 35 +export const OP_DATA_36 = 0x24; // 36 +export const OP_DATA_37 = 0x25; // 37 +export const OP_DATA_38 = 0x26; // 38 +export const OP_DATA_39 = 0x27; // 39 +export const OP_DATA_40 = 0x28; // 40 +export const OP_DATA_41 = 0x29; // 41 +export const OP_DATA_42 = 0x2a; // 42 +export const OP_DATA_43 = 0x2b; // 43 +export const OP_DATA_44 = 0x2c; // 44 +export const OP_DATA_45 = 0x2d; // 45 +export const OP_DATA_46 = 0x2e; // 46 +export const OP_DATA_47 = 0x2f; // 47 +export const OP_DATA_48 = 0x30; // 48 +export const OP_DATA_49 = 0x31; // 49 +export const OP_DATA_50 = 0x32; // 50 +export const OP_DATA_51 = 0x33; // 51 +export const OP_DATA_52 = 0x34; // 52 +export const OP_DATA_53 = 0x35; // 53 +export const OP_DATA_54 = 0x36; // 54 +export const OP_DATA_55 = 0x37; // 55 +export const OP_DATA_56 = 0x38; // 56 +export const OP_DATA_57 = 0x39; // 57 +export const OP_DATA_58 = 0x3a; // 58 +export const OP_DATA_59 = 0x3b; // 59 +export const OP_DATA_60 = 0x3c; // 60 +export const OP_DATA_61 = 0x3d; // 61 +export const OP_DATA_62 = 0x3e; // 62 +export const OP_DATA_63 = 0x3f; // 63 +export const OP_DATA_64 = 0x40; // 64 +export const OP_DATA_65 = 0x41; // 65 +export const OP_DATA_66 = 0x42; // 66 +export const OP_DATA_67 = 0x43; // 67 +export const OP_DATA_68 = 0x44; // 68 +export const OP_DATA_69 = 0x45; // 69 +export const OP_DATA_70 = 0x46; // 70 +export const OP_DATA_71 = 0x47; // 71 +export const OP_DATA_72 = 0x48; // 72 +export const OP_DATA_73 = 0x49; // 73 +export const OP_DATA_74 = 0x4a; // 74 +export const OP_DATA_75 = 0x4b; // 75 +export const OP_PUSHDATA1 = 0x4c; // 76 +export const OP_PUSHDATA2 = 0x4d; // 77 +export const OP_PUSHDATA4 = 0x4e; // 78 +export const OP_1NEGATE = 0x4f; // 79 +export const OP_RESERVED = 0x50; // 80 +export const OP_1 = 0x51; // 81 - AKA OP_TRUE +export const OP_TRUE = 0x51; // 81 +export const OP_2 = 0x52; // 82 +export const OP_3 = 0x53; // 83 +export const OP_4 = 0x54; // 84 +export const OP_5 = 0x55; // 85 +export const OP_6 = 0x56; // 86 +export const OP_7 = 0x57; // 87 +export const OP_8 = 0x58; // 88 +export const OP_9 = 0x59; // 89 +export const OP_10 = 0x5a; // 90 +export const OP_11 = 0x5b; // 91 +export const OP_12 = 0x5c; // 92 +export const OP_13 = 0x5d; // 93 +export const OP_14 = 0x5e; // 94 +export const OP_15 = 0x5f; // 95 +export const OP_16 = 0x60; // 96 +export const OP_NOP = 0x61; // 97 +export const OP_VER = 0x62; // 98 +export const OP_IF = 0x63; // 99 +export const OP_NOTIF = 0x64; // 100 +export const OP_VERIF = 0x65; // 101 +export const OP_VERNOTIF = 0x66; // 102 +export const OP_ELSE = 0x67; // 103 +export const OP_ENDIF = 0x68; // 104 +export const OP_VERIFY = 0x69; // 105 +export const OP_RETURN = 0x6a; // 106 +export const OP_TOALTSTACK = 0x6b; // 107 +export const OP_FROMALTSTACK = 0x6c; // 108 +export const OP_2DROP = 0x6d; // 109 +export const OP_2DUP = 0x6e; // 110 +export const OP_3DUP = 0x6f; // 111 +export const OP_2OVER = 0x70; // 112 +export const OP_2ROT = 0x71; // 113 +export const OP_2SWAP = 0x72; // 114 +export const OP_IFDUP = 0x73; // 115 +export const OP_DEPTH = 0x74; // 116 +export const OP_DROP = 0x75; // 117 +export const OP_DUP = 0x76; // 118 +export const OP_NIP = 0x77; // 119 +export const OP_OVER = 0x78; // 120 +export const OP_PICK = 0x79; // 121 +export const OP_ROLL = 0x7a; // 122 +export const OP_ROT = 0x7b; // 123 +export const OP_SWAP = 0x7c; // 124 +export const OP_TUCK = 0x7d; // 125 +export const OP_CAT = 0x7e; // 126 +export const OP_SUBSTR = 0x7f; // 127 +export const OP_LEFT = 0x80; // 128 +export const OP_RIGHT = 0x81; // 129 +export const OP_SIZE = 0x82; // 130 +export const OP_INVERT = 0x83; // 131 +export const OP_AND = 0x84; // 132 +export const OP_OR = 0x85; // 133 +export const OP_XOR = 0x86; // 134 +export const OP_EQUAL = 0x87; // 135 +export const OP_EQUALVERIFY = 0x88; // 136 +export const OP_ROTR = 0x89; // 137 +export const OP_ROTL = 0x8a; // 138 +export const OP_1ADD = 0x8b; // 139 +export const OP_1SUB = 0x8c; // 140 +export const OP_2MUL = 0x8d; // 141 +export const OP_2DIV = 0x8e; // 142 +export const OP_NEGATE = 0x8f; // 143 +export const OP_ABS = 0x90; // 144 +export const OP_NOT = 0x91; // 145 +export const OP_0NOTEQUAL = 0x92; // 146 +export const OP_ADD = 0x93; // 147 +export const OP_SUB = 0x94; // 148 +export const OP_MUL = 0x95; // 149 +export const OP_DIV = 0x96; // 150 +export const OP_MOD = 0x97; // 151 +export const OP_LSHIFT = 0x98; // 152 +export const OP_RSHIFT = 0x99; // 153 +export const OP_BOOLAND = 0x9a; // 154 +export const OP_BOOLOR = 0x9b; // 155 +export const OP_NUMEQUAL = 0x9c; // 156 +export const OP_NUMEQUALVERIFY = 0x9d; // 157 +export const OP_NUMNOTEQUAL = 0x9e; // 158 +export const OP_LESSTHAN = 0x9f; // 159 +export const OP_GREATERTHAN = 0xa0; // 160 +export const OP_LESSTHANOREQUAL = 0xa1; // 161 +export const OP_GREATERTHANOREQUAL = 0xa2; // 162 +export const OP_MIN = 0xa3; // 163 +export const OP_MAX = 0xa4; // 164 +export const OP_WITHIN = 0xa5; // 165 +export const OP_RIPEMD160 = 0xa6; // 166 +export const OP_SHA1 = 0xa7; // 167 +export const OP_BLAKE256 = 0xa8; // 168 +export const OP_HASH160 = 0xa9; // 169 +export const OP_HASH256 = 0xaa; // 170 +export const OP_CODESEPARATOR = 0xab; // 171 +export const OP_CHECKSIG = 0xac; // 172 +export const OP_CHECKSIGVERIFY = 0xad; // 173 +export const OP_CHECKMULTISIG = 0xae; // 174 +export const OP_CHECKMULTISIGVERIFY = 0xaf; // 175 +export const OP_NOP1 = 0xb0; // 176 +export const OP_NOP2 = 0xb1; // 177 +export const OP_CHECKLOCKTIMEVERIFY = 0xb1; // 177 - AKA OP_NOP2 +export const OP_NOP3 = 0xb2; // 178 +export const OP_CHECKSEQUENCEVERIFY = 0xb2; // 178 - AKA OP_NOP3 +export const OP_NOP4 = 0xb3; // 179 +export const OP_NOP5 = 0xb4; // 180 +export const OP_NOP6 = 0xb5; // 181 +export const OP_NOP7 = 0xb6; // 182 +export const OP_NOP8 = 0xb7; // 183 +export const OP_NOP9 = 0xb8; // 184 +export const OP_NOP10 = 0xb9; // 185 +export const OP_SSTX = 0xba; // 186 DECRED +export const OP_SSGEN = 0xbb; // 187 DECRED +export const OP_SSRTX = 0xbc; // 188 DECRED +export const OP_SSTXCHANGE = 0xbd; // 189 DECRED +export const OP_CHECKSIGALT = 0xbe; // 190 DECRED +export const OP_CHECKSIGALTVERIFY = 0xbf; // 191 DECRED +export const OP_SHA256 = 0xc0; // 192 +export const OP_UNKNOWN193 = 0xc1; // 193 +export const OP_UNKNOWN194 = 0xc2; // 194 +export const OP_UNKNOWN195 = 0xc3; // 195 +export const OP_UNKNOWN196 = 0xc4; // 196 +export const OP_UNKNOWN197 = 0xc5; // 197 +export const OP_UNKNOWN198 = 0xc6; // 198 +export const OP_UNKNOWN199 = 0xc7; // 199 +export const OP_UNKNOWN200 = 0xc8; // 200 +export const OP_UNKNOWN201 = 0xc9; // 201 +export const OP_UNKNOWN202 = 0xca; // 202 +export const OP_UNKNOWN203 = 0xcb; // 203 +export const OP_UNKNOWN204 = 0xcc; // 204 +export const OP_UNKNOWN205 = 0xcd; // 205 +export const OP_UNKNOWN206 = 0xce; // 206 +export const OP_UNKNOWN207 = 0xcf; // 207 +export const OP_UNKNOWN208 = 0xd0; // 208 +export const OP_UNKNOWN209 = 0xd1; // 209 +export const OP_UNKNOWN210 = 0xd2; // 210 +export const OP_UNKNOWN211 = 0xd3; // 211 +export const OP_UNKNOWN212 = 0xd4; // 212 +export const OP_UNKNOWN213 = 0xd5; // 213 +export const OP_UNKNOWN214 = 0xd6; // 214 +export const OP_UNKNOWN215 = 0xd7; // 215 +export const OP_UNKNOWN216 = 0xd8; // 216 +export const OP_UNKNOWN217 = 0xd9; // 217 +export const OP_UNKNOWN218 = 0xda; // 218 +export const OP_UNKNOWN219 = 0xdb; // 219 +export const OP_UNKNOWN220 = 0xdc; // 220 +export const OP_UNKNOWN221 = 0xdd; // 221 +export const OP_UNKNOWN222 = 0xde; // 222 +export const OP_UNKNOWN223 = 0xdf; // 223 +export const OP_UNKNOWN224 = 0xe0; // 224 +export const OP_UNKNOWN225 = 0xe1; // 225 +export const OP_UNKNOWN226 = 0xe2; // 226 +export const OP_UNKNOWN227 = 0xe3; // 227 +export const OP_UNKNOWN228 = 0xe4; // 228 +export const OP_UNKNOWN229 = 0xe5; // 229 +export const OP_UNKNOWN230 = 0xe6; // 230 +export const OP_UNKNOWN231 = 0xe7; // 231 +export const OP_UNKNOWN232 = 0xe8; // 232 +export const OP_UNKNOWN233 = 0xe9; // 233 +export const OP_UNKNOWN234 = 0xea; // 234 +export const OP_UNKNOWN235 = 0xeb; // 235 +export const OP_UNKNOWN236 = 0xec; // 236 +export const OP_UNKNOWN237 = 0xed; // 237 +export const OP_UNKNOWN238 = 0xee; // 238 +export const OP_UNKNOWN239 = 0xef; // 239 +export const OP_UNKNOWN240 = 0xf0; // 240 +export const OP_UNKNOWN241 = 0xf1; // 241 +export const OP_UNKNOWN242 = 0xf2; // 242 +export const OP_UNKNOWN243 = 0xf3; // 243 +export const OP_UNKNOWN244 = 0xf4; // 244 +export const OP_UNKNOWN245 = 0xf5; // 245 +export const OP_UNKNOWN246 = 0xf6; // 246 +export const OP_UNKNOWN247 = 0xf7; // 247 +export const OP_UNKNOWN248 = 0xf8; // 248 +export const OP_INVALID249 = 0xf9; // 249 - bitcoin core internal +export const OP_SMALLINTEGER = 0xfa; // 250 - bitcoin core internal +export const OP_PUBKEYS = 0xfb; // 251 - bitcoin core internal +export const OP_UNKNOWN252 = 0xfc; // 252 +export const OP_PUBKEYHASH = 0xfd; // 253 - bitcoin core internal +export const OP_PUBKEY = 0xfe; // 254 - bitcoin core internal +export const OP_INVALIDOPCODE = 0xff; // 255 - bitcoin core internal + +// opcodeArray holds details about all possible opcodes such as how many bytes +// the opcode and any associated data should take, its human-readable name, and +// the handler function. +export const opcodeArray = [ + // Data push opcodes. + { value: OP_FALSE, name: "OP_0", length: 1 }, + { value: OP_DATA_1, name: "OP_DATA_1", length: 2 }, + { value: OP_DATA_2, name: "OP_DATA_2", length: 3 }, + { value: OP_DATA_3, name: "OP_DATA_3", length: 4 }, + { value: OP_DATA_4, name: "OP_DATA_4", length: 5 }, + { value: OP_DATA_5, name: "OP_DATA_5", length: 6 }, + { value: OP_DATA_6, name: "OP_DATA_6", length: 7 }, + { value: OP_DATA_7, name: "OP_DATA_7", length: 8 }, + { value: OP_DATA_8, name: "OP_DATA_8", length: 9 }, + { value: OP_DATA_9, name: "OP_DATA_9", length: 10 }, + { value: OP_DATA_10, name: "OP_DATA_10", length: 11 }, + { value: OP_DATA_11, name: "OP_DATA_11", length: 12 }, + { value: OP_DATA_12, name: "OP_DATA_12", length: 13 }, + { value: OP_DATA_13, name: "OP_DATA_13", length: 14 }, + { value: OP_DATA_14, name: "OP_DATA_14", length: 15 }, + { value: OP_DATA_15, name: "OP_DATA_15", length: 16 }, + { value: OP_DATA_16, name: "OP_DATA_16", length: 17 }, + { value: OP_DATA_17, name: "OP_DATA_17", length: 18 }, + { value: OP_DATA_18, name: "OP_DATA_18", length: 19 }, + { value: OP_DATA_19, name: "OP_DATA_19", length: 20 }, + { value: OP_DATA_20, name: "OP_DATA_20", length: 21 }, + { value: OP_DATA_21, name: "OP_DATA_21", length: 22 }, + { value: OP_DATA_22, name: "OP_DATA_22", length: 23 }, + { value: OP_DATA_23, name: "OP_DATA_23", length: 24 }, + { value: OP_DATA_24, name: "OP_DATA_24", length: 25 }, + { value: OP_DATA_25, name: "OP_DATA_25", length: 26 }, + { value: OP_DATA_26, name: "OP_DATA_26", length: 27 }, + { value: OP_DATA_27, name: "OP_DATA_27", length: 28 }, + { value: OP_DATA_28, name: "OP_DATA_28", length: 29 }, + { value: OP_DATA_29, name: "OP_DATA_29", length: 30 }, + { value: OP_DATA_30, name: "OP_DATA_30", length: 31 }, + { value: OP_DATA_31, name: "OP_DATA_31", length: 32 }, + { value: OP_DATA_32, name: "OP_DATA_32", length: 33 }, + { value: OP_DATA_33, name: "OP_DATA_33", length: 34 }, + { value: OP_DATA_34, name: "OP_DATA_34", length: 35 }, + { value: OP_DATA_35, name: "OP_DATA_35", length: 36 }, + { value: OP_DATA_36, name: "OP_DATA_36", length: 37 }, + { value: OP_DATA_37, name: "OP_DATA_37", length: 38 }, + { value: OP_DATA_38, name: "OP_DATA_38", length: 39 }, + { value: OP_DATA_39, name: "OP_DATA_39", length: 40 }, + { value: OP_DATA_40, name: "OP_DATA_40", length: 41 }, + { value: OP_DATA_41, name: "OP_DATA_41", length: 42 }, + { value: OP_DATA_42, name: "OP_DATA_42", length: 43 }, + { value: OP_DATA_43, name: "OP_DATA_43", length: 44 }, + { value: OP_DATA_44, name: "OP_DATA_44", length: 45 }, + { value: OP_DATA_45, name: "OP_DATA_45", length: 46 }, + { value: OP_DATA_46, name: "OP_DATA_46", length: 47 }, + { value: OP_DATA_47, name: "OP_DATA_47", length: 48 }, + { value: OP_DATA_48, name: "OP_DATA_48", length: 49 }, + { value: OP_DATA_49, name: "OP_DATA_49", length: 50 }, + { value: OP_DATA_50, name: "OP_DATA_50", length: 51 }, + { value: OP_DATA_51, name: "OP_DATA_51", length: 52 }, + { value: OP_DATA_52, name: "OP_DATA_52", length: 53 }, + { value: OP_DATA_53, name: "OP_DATA_53", length: 54 }, + { value: OP_DATA_54, name: "OP_DATA_54", length: 55 }, + { value: OP_DATA_55, name: "OP_DATA_55", length: 56 }, + { value: OP_DATA_56, name: "OP_DATA_56", length: 57 }, + { value: OP_DATA_57, name: "OP_DATA_57", length: 58 }, + { value: OP_DATA_58, name: "OP_DATA_58", length: 59 }, + { value: OP_DATA_59, name: "OP_DATA_59", length: 60 }, + { value: OP_DATA_60, name: "OP_DATA_60", length: 61 }, + { value: OP_DATA_61, name: "OP_DATA_61", length: 62 }, + { value: OP_DATA_62, name: "OP_DATA_62", length: 63 }, + { value: OP_DATA_63, name: "OP_DATA_63", length: 64 }, + { value: OP_DATA_64, name: "OP_DATA_64", length: 65 }, + { value: OP_DATA_65, name: "OP_DATA_65", length: 66 }, + { value: OP_DATA_66, name: "OP_DATA_66", length: 67 }, + { value: OP_DATA_67, name: "OP_DATA_67", length: 68 }, + { value: OP_DATA_68, name: "OP_DATA_68", length: 69 }, + { value: OP_DATA_69, name: "OP_DATA_69", length: 70 }, + { value: OP_DATA_70, name: "OP_DATA_70", length: 71 }, + { value: OP_DATA_71, name: "OP_DATA_71", length: 72 }, + { value: OP_DATA_72, name: "OP_DATA_72", length: 73 }, + { value: OP_DATA_73, name: "OP_DATA_73", length: 74 }, + { value: OP_DATA_74, name: "OP_DATA_74", length: 75 }, + { value: OP_DATA_75, name: "OP_DATA_75", length: 76 }, + { value: OP_PUSHDATA1, name: "OP_PUSHDATA1", length: -1 }, + { value: OP_PUSHDATA2, name: "OP_PUSHDATA2", length: -2 }, + { value: OP_PUSHDATA4, name: "OP_PUSHDATA4", length: -4 }, + { value: OP_1NEGATE, name: "OP_1NEGATE", length: 1 }, + { value: OP_RESERVED, name: "OP_RESERVED", length: 1 }, + { value: OP_TRUE, name: "OP_1", length: 1 }, + { value: OP_2, name: "OP_2", length: 1 }, + { value: OP_3, name: "OP_3", length: 1 }, + { value: OP_4, name: "OP_4", length: 1 }, + { value: OP_5, name: "OP_5", length: 1 }, + { value: OP_6, name: "OP_6", length: 1 }, + { value: OP_7, name: "OP_7", length: 1 }, + { value: OP_8, name: "OP_8", length: 1 }, + { value: OP_9, name: "OP_9", length: 1 }, + { value: OP_10, name: "OP_10", length: 1 }, + { value: OP_11, name: "OP_11", length: 1 }, + { value: OP_12, name: "OP_12", length: 1 }, + { value: OP_13, name: "OP_13", length: 1 }, + { value: OP_14, name: "OP_14", length: 1 }, + { value: OP_15, name: "OP_15", length: 1 }, + { value: OP_16, name: "OP_16", length: 1 }, + { value: OP_NOP, name: "OP_NOP", length: 1 }, + { value: OP_VER, name: "OP_VER", length: 1 }, + { value: OP_IF, name: "OP_IF", length: 1 }, + { value: OP_NOTIF, name: "OP_NOTIF", length: 1 }, + { value: OP_VERIF, name: "OP_VERIF", length: 1 }, + { value: OP_VERNOTIF, name: "OP_VERNOTIF", length: 1 }, + { value: OP_ELSE, name: "OP_ELSE", length: 1 }, + { value: OP_ENDIF, name: "OP_ENDIF", length: 1 }, + { value: OP_VERIFY, name: "OP_VERIFY", length: 1 }, + { value: OP_RETURN, name: "OP_RETURN", length: 1 }, + { value: OP_CHECKLOCKTIMEVERIFY, name: "OP_CHECKLOCKTIMEVERIFY", length: 1 }, + { value: OP_CHECKSEQUENCEVERIFY, name: "OP_CHECKSEQUENCEVERIFY", length: 1 }, + { value: OP_TOALTSTACK, name: "OP_TOALTSTACK", length: 1 }, + { value: OP_FROMALTSTACK, name: "OP_FROMALTSTACK", length: 1 }, + { value: OP_2DROP, name: "OP_2DROP", length: 1 }, + { value: OP_2DUP, name: "OP_2DUP", length: 1 }, + { value: OP_3DUP, name: "OP_3DUP", length: 1 }, + { value: OP_2OVER, name: "OP_2OVER", length: 1 }, + { value: OP_2ROT, name: "OP_2ROT", length: 1 }, + { value: OP_2SWAP, name: "OP_2SWAP", length: 1 }, + { value: OP_IFDUP, name: "OP_IFDUP", length: 1 }, + { value: OP_DEPTH, name: "OP_DEPTH", length: 1 }, + { value: OP_DROP, name: "OP_DROP", length: 1 }, + { value: OP_DUP, name: "OP_DUP", length: 1 }, + { value: OP_NIP, name: "OP_NIP", length: 1 }, + { value: OP_OVER, name: "OP_OVER", length: 1 }, + { value: OP_PICK, name: "OP_PICK", length: 1 }, + { value: OP_ROLL, name: "OP_ROLL", length: 1 }, + { value: OP_ROT, name: "OP_ROT", length: 1 }, + { value: OP_SWAP, name: "OP_SWAP", length: 1 }, + { value: OP_TUCK, name: "OP_TUCK", length: 1 }, + { value: OP_CAT, name: "OP_CAT", length: 1 }, + { value: OP_SUBSTR, name: "OP_SUBSTR", length: 1 }, + { value: OP_LEFT, name: "OP_LEFT", length: 1 }, + { value: OP_RIGHT, name: "OP_RIGHT", length: 1 }, + { value: OP_SIZE, name: "OP_SIZE", length: 1 }, + { value: OP_INVERT, name: "OP_INVERT", length: 1 }, + { value: OP_AND, name: "OP_AND", length: 1 }, + { value: OP_OR, name: "OP_OR", length: 1 }, + { value: OP_XOR, name: "OP_XOR", length: 1 }, + { value: OP_EQUAL, name: "OP_EQUAL", length: 1 }, + { value: OP_EQUALVERIFY, name: "OP_EQUALVERIFY", length: 1 }, + { value: OP_ROTR, name: "OP_ROTR", length: 1 }, + { value: OP_ROTL, name: "OP_ROTL", length: 1 }, + { value: OP_1ADD, name: "OP_1ADD", length: 1 }, + { value: OP_1SUB, name: "OP_1SUB", length: 1 }, + { value: OP_2MUL, name: "OP_2MUL", length: 1 }, + { value: OP_2DIV, name: "OP_2DIV", length: 1 }, + { value: OP_NEGATE, name: "OP_NEGATE", length: 1 }, + { value: OP_ABS, name: "OP_ABS", length: 1 }, + { value: OP_NOT, name: "OP_NOT", length: 1 }, + { value: OP_0NOTEQUAL, name: "OP_0NOTEQUAL", length: 1 }, + { value: OP_ADD, name: "OP_ADD", length: 1 }, + { value: OP_SUB, name: "OP_SUB", length: 1 }, + { value: OP_MUL, name: "OP_MUL", length: 1 }, + { value: OP_DIV, name: "OP_DIV", length: 1 }, + { value: OP_MOD, name: "OP_MOD", length: 1 }, + { value: OP_LSHIFT, name: "OP_LSHIFT", length: 1 }, + { value: OP_RSHIFT, name: "OP_RSHIFT", length: 1 }, + { value: OP_BOOLAND, name: "OP_BOOLAND", length: 1 }, + { value: OP_BOOLOR, name: "OP_BOOLOR", length: 1 }, + { value: OP_NUMEQUAL, name: "OP_NUMEQUAL", length: 1 }, + { value: OP_NUMEQUALVERIFY, name: "OP_NUMEQUALVERIFY", length: 1 }, + { value: OP_NUMNOTEQUAL, name: "OP_NUMNOTEQUAL", length: 1 }, + { value: OP_LESSTHAN, name: "OP_LESSTHAN", length: 1 }, + { value: OP_GREATERTHAN, name: "OP_GREATERTHAN", length: 1 }, + { value: OP_LESSTHANOREQUAL, name: "OP_LESSTHANOREQUAL", length: 1 }, + { value: OP_GREATERTHANOREQUAL, name: "OP_GREATERTHANOREQUAL", length: 1 }, + { value: OP_MIN, name: "OP_MIN", length: 1 }, + { value: OP_MAX, name: "OP_MAX", length: 1 }, + { value: OP_WITHIN, name: "OP_WITHIN", length: 1 }, + { value: OP_RIPEMD160, name: "OP_RIPEMD160", length: 1 }, + { value: OP_SHA1, name: "OP_SHA1", length: 1 }, + { value: OP_SHA256, name: "OP_SHA256", length: 1 }, + { value: OP_BLAKE256, name: "OP_BLAKE256", length: 1 }, + { value: OP_HASH160, name: "OP_HASH160", length: 1 }, + { value: OP_HASH256, name: "OP_HASH256", length: 1 }, + { value: OP_CODESEPARATOR, name: "OP_CODESEPARATOR", length: 1 }, // Disabled + { value: OP_CHECKSIG, name: "OP_CHECKSIG", length: 1 }, + { value: OP_CHECKSIGVERIFY, name: "OP_CHECKSIGVERIFY", length: 1 }, + { value: OP_CHECKMULTISIG, name: "OP_CHECKMULTISIG", length: 1 }, + { value: OP_CHECKMULTISIGVERIFY, name: "OP_CHECKMULTISIGVERIFY", length: 1 }, + { value: OP_NOP1, name: "OP_NOP1", length: 1 }, + { value: OP_NOP4, name: "OP_NOP4", length: 1 }, + { value: OP_NOP5, name: "OP_NOP5", length: 1 }, + { value: OP_NOP6, name: "OP_NOP6", length: 1 }, + { value: OP_NOP7, name: "OP_NOP7", length: 1 }, + { value: OP_NOP8, name: "OP_NOP8", length: 1 }, + { value: OP_NOP9, name: "OP_NOP9", length: 1 }, + { value: OP_NOP10, name: "OP_NOP10", length: 1 }, + { value: OP_SSTX, name: "OP_SSTX", length: 1 }, + { value: OP_SSGEN, name: "OP_SSGEN", length: 1 }, + { value: OP_SSRTX, name: "OP_SSRTX", length: 1 }, + { value: OP_SSTXCHANGE, name: "OP_SSTXCHANGE", length: 1 }, + { value: OP_CHECKSIGALT, name: "OP_CHECKSIGALT", length: 1 }, + { value: OP_CHECKSIGALTVERIFY, name: "OP_CHECKSIGALTVERIFY", length: 1 }, + { value: OP_UNKNOWN193, name: "OP_UNKNOWN193", length: 1 }, + { value: OP_UNKNOWN194, name: "OP_UNKNOWN194", length: 1 }, + { value: OP_UNKNOWN195, name: "OP_UNKNOWN195", length: 1 }, + { value: OP_UNKNOWN196, name: "OP_UNKNOWN196", length: 1 }, + { value: OP_UNKNOWN197, name: "OP_UNKNOWN197", length: 1 }, + { value: OP_UNKNOWN198, name: "OP_UNKNOWN198", length: 1 }, + { value: OP_UNKNOWN199, name: "OP_UNKNOWN199", length: 1 }, + { value: OP_UNKNOWN200, name: "OP_UNKNOWN200", length: 1 }, + { value: OP_UNKNOWN201, name: "OP_UNKNOWN201", length: 1 }, + { value: OP_UNKNOWN202, name: "OP_UNKNOWN202", length: 1 }, + { value: OP_UNKNOWN203, name: "OP_UNKNOWN203", length: 1 }, + { value: OP_UNKNOWN204, name: "OP_UNKNOWN204", length: 1 }, + { value: OP_UNKNOWN205, name: "OP_UNKNOWN205", length: 1 }, + { value: OP_UNKNOWN206, name: "OP_UNKNOWN206", length: 1 }, + { value: OP_UNKNOWN207, name: "OP_UNKNOWN207", length: 1 }, + { value: OP_UNKNOWN208, name: "OP_UNKNOWN208", length: 1 }, + { value: OP_UNKNOWN209, name: "OP_UNKNOWN209", length: 1 }, + { value: OP_UNKNOWN210, name: "OP_UNKNOWN210", length: 1 }, + { value: OP_UNKNOWN211, name: "OP_UNKNOWN211", length: 1 }, + { value: OP_UNKNOWN212, name: "OP_UNKNOWN212", length: 1 }, + { value: OP_UNKNOWN213, name: "OP_UNKNOWN213", length: 1 }, + { value: OP_UNKNOWN214, name: "OP_UNKNOWN214", length: 1 }, + { value: OP_UNKNOWN215, name: "OP_UNKNOWN215", length: 1 }, + { value: OP_UNKNOWN216, name: "OP_UNKNOWN216", length: 1 }, + { value: OP_UNKNOWN217, name: "OP_UNKNOWN217", length: 1 }, + { value: OP_UNKNOWN218, name: "OP_UNKNOWN218", length: 1 }, + { value: OP_UNKNOWN219, name: "OP_UNKNOWN219", length: 1 }, + { value: OP_UNKNOWN220, name: "OP_UNKNOWN220", length: 1 }, + { value: OP_UNKNOWN221, name: "OP_UNKNOWN221", length: 1 }, + { value: OP_UNKNOWN222, name: "OP_UNKNOWN222", length: 1 }, + { value: OP_UNKNOWN223, name: "OP_UNKNOWN223", length: 1 }, + { value: OP_UNKNOWN224, name: "OP_UNKNOWN224", length: 1 }, + { value: OP_UNKNOWN225, name: "OP_UNKNOWN225", length: 1 }, + { value: OP_UNKNOWN226, name: "OP_UNKNOWN226", length: 1 }, + { value: OP_UNKNOWN227, name: "OP_UNKNOWN227", length: 1 }, + { value: OP_UNKNOWN228, name: "OP_UNKNOWN228", length: 1 }, + { value: OP_UNKNOWN229, name: "OP_UNKNOWN229", length: 1 }, + { value: OP_UNKNOWN230, name: "OP_UNKNOWN230", length: 1 }, + { value: OP_UNKNOWN231, name: "OP_UNKNOWN231", length: 1 }, + { value: OP_UNKNOWN232, name: "OP_UNKNOWN232", length: 1 }, + { value: OP_UNKNOWN233, name: "OP_UNKNOWN233", length: 1 }, + { value: OP_UNKNOWN234, name: "OP_UNKNOWN234", length: 1 }, + { value: OP_UNKNOWN235, name: "OP_UNKNOWN235", length: 1 }, + { value: OP_UNKNOWN236, name: "OP_UNKNOWN236", length: 1 }, + { value: OP_UNKNOWN237, name: "OP_UNKNOWN237", length: 1 }, + { value: OP_UNKNOWN238, name: "OP_UNKNOWN238", length: 1 }, + { value: OP_UNKNOWN239, name: "OP_UNKNOWN239", length: 1 }, + { value: OP_UNKNOWN240, name: "OP_UNKNOWN240", length: 1 }, + { value: OP_UNKNOWN241, name: "OP_UNKNOWN241", length: 1 }, + { value: OP_UNKNOWN242, name: "OP_UNKNOWN242", length: 1 }, + { value: OP_UNKNOWN243, name: "OP_UNKNOWN243", length: 1 }, + { value: OP_UNKNOWN244, name: "OP_UNKNOWN244", length: 1 }, + { value: OP_UNKNOWN245, name: "OP_UNKNOWN245", length: 1 }, + { value: OP_UNKNOWN246, name: "OP_UNKNOWN246", length: 1 }, + { value: OP_UNKNOWN247, name: "OP_UNKNOWN247", length: 1 }, + { value: OP_UNKNOWN248, name: "OP_UNKNOWN248", length: 1 }, + { value: OP_INVALID249, name: "OP_INVALID249", length: 1 }, + { value: OP_SMALLINTEGER, name: "OP_SMALLINTEGER", length: 1 }, + { value: OP_PUBKEYS, name: "OP_PUBKEYS", length: 1 }, + { value: OP_UNKNOWN252, name: "OP_UNKNOWN252", length: 1 }, + { value: OP_PUBKEYHASH, name: "OP_PUBKEYHASH", length: 1 }, + { value: OP_PUBKEY, name: "OP_PUBKEY", length: 1 }, + { value: OP_INVALIDOPCODE, name: "OP_INVALIDOPCODE", length: 1 } +]; diff --git a/app/helpers/addresses.js b/app/helpers/addresses.js index 240297bc3e..b38e138513 100644 --- a/app/helpers/addresses.js +++ b/app/helpers/addresses.js @@ -87,7 +87,7 @@ const checksum = (input) => { }; // checkEncode prepends two version bytes and appends a four byte checksum. -const checkEncode = (input, version) => { +export const checkEncode = (input, version) => { let b = Buffer.from(version); b = Buffer.concat([b, input]); const calculatedChecksum = checksum(b); @@ -149,7 +149,7 @@ export const newAddressScriptHashFromHash = (scriptHash, params) => { // known. const getNewAddressScriptHashFromHash = (scriptHash, netID) => { // Check for a valid script hash length. - if (scriptHash.length != ripemd160Size) { + if (scriptHash.length !== ripemd160Size) { return { error: "pkHash must be 20 bytes" }; } diff --git a/app/helpers/msgTx.js b/app/helpers/msgTx.js index dfc8397ba2..a59e48c8e6 100644 --- a/app/helpers/msgTx.js +++ b/app/helpers/msgTx.js @@ -4,7 +4,8 @@ import { putUint8, putUint16, putUint32, - putUint64 + putUint64, + hexToBytes } from "./byteActions"; import { Uint64LE } from "int64-buffer"; @@ -136,8 +137,9 @@ function serializeSize(output) { // writeOutPoint encodes op to the Decred protocol encoding for an OutPoint // to w. function writeOutPoint(input, arr8, position) { - arr8.set(input.opRawHash, position); - position += input.opRawHash.length; + const opRawHash = hexToBytes(input.prevTxId); + arr8.set(opRawHash, position); + position += opRawHash.length; arr8.set(putUint32(input.outputIndex), position); position += 4; arr8.set(putUint8(input.outputTree), position); @@ -197,8 +199,8 @@ export function decodeRawTransaction(rawTx) { tx.inputs = []; for (let i = 0; i < tx.numInputs; i++) { const input = {}; - input.opRawHash = rawTx.slice(position, position + 32); - input.prevTxId = reverseHash(rawToHex(input.opRawHash)); + const opRawHash = rawTx.slice(position, position + 32); + input.prevTxId = reverseHash(rawToHex(opRawHash)); position += 32; input.outputIndex = rawTx.readUInt32LE(position); position += 4; diff --git a/app/helpers/scripts.js b/app/helpers/scripts.js index 3ded6e54a3..e6908b7a4b 100644 --- a/app/helpers/scripts.js +++ b/app/helpers/scripts.js @@ -18,7 +18,10 @@ import { OP_SSTXCHANGE, OP_DATA_33, OP_DATA_65, - OP_CHECKSIGALT + OP_CHECKSIGALT, + OP_PUSHDATA4, + opcodeArray, + OP_RETURN } from "constants"; const MaxUint16 = 1 << (16 - 1); @@ -429,11 +432,23 @@ export const extractPkScriptAddrs = (version, pkScript, chainParams) => { }; } - // // Check for null data script. - // if isNullDataScript(version, pkScript) { - // // Null data transactions have no addresses or required signatures. - // return NullDataTy, nil, 0, nil - // } + const parsedScript = parseScript(pkScript, opcodeArray); + let pops; + if (parsedScript) { + const { error, retScript } = parsedScript; + if (error) return error; + pops = retScript; + } + // Check for null data script. + if (isNullData(pops)) { + // Null data transactions have no addresses or required signatures. + return { + // scriptclass NullDataTy + scriptClass: 0, + address: [], + requiredSig: 0 + }; + } // Don't attempt to extract addresses or required signatures for nonstandard // transactions. @@ -529,3 +544,120 @@ export const EstimateSerializeSizeFromScriptSizes = ( changeSize ); }; + +// isNullData returns true if the passed script is a null data transaction, +// false otherwise. +const isNullData = (pops) => { + if (!pops) return; + // A nulldata transaction is either a single OP_RETURN or an + // OP_RETURN SMALLDATA (where SMALLDATA is a data push up to + // MaxDataCarrierSize bytes). + const MaxDataCarrierSize = 256; + const l = pops.length; + if (l === 1 && pops[0].opcode.value === OP_RETURN) { + return true; + } + + return ( + l === 2 && + pops[0].opcode.value == OP_RETURN && + (isSmallInt(pops[1].opcode.value) || + pops[1].opcode.value <= OP_PUSHDATA4) && + pops[1].data.length <= MaxDataCarrierSize + ); +}; + +// parseScript parses a script getting all of its opcodes and which data they +// may have. This code was removed from dcrd due to Zero alloc optimization +// refactor optmization at txscript, but it is fine for decrediton as for now we +// dont decode big scripts on it. +// source: https://github.com/decred/dcrd/pull/1656/commits/fcb1f3a7a137f3d69091c23c0f349d35df6c1ee6 +const parseScript = (script, opcodes) => { + if (!script) return; + const retScript = []; + for (let i = 0; i < script.length; i++) { + const instr = script[i]; + const op = opcodes[instr]; + const pop = { opcode: op }; + + if (op.length == 1) { + retScript.push(pop); + continue; + } else if (op.length > 1) { + if (script.slice(i).length < op.length) { + return { + retScript, + error: `opcode ${op.name} requires ${ + op.length + } bytes, but script only has ${script.slice(i).length} remaining.` + }; + } + pop.data = script.slice(i + 1, i + op.length); + i += op.length - 1; + } else if (op.legnth < 0) { + let l; + let off = i + 1; + + // negativeLengthHelper is an aux method to help get data and move the offset + // of a script with negative length (little endian length) so we can get the + // data. This way we can avoid code repetition. + const negativeLengthHelper = () => { + // Move offset to beginning of the data. + off += -op.length; + + // Disallow entries that do not fit script or were + // sign extended. + if (l > script.slice(off).length || l < 0) { + return { + retScript, + error: `opcode ${op.name} pushes ${l} bytes, but script only has ${ + script.slice(off).length + } remaining` + }; + } + + pop.data = script.slice(off, off + l); + i += 1 - op.length + l; + }; + if (script.slice(off).length < -op.length) { + return { + retScript, + error: `opcode ${ + op.name + } requires ${-op.length} bytes, but script only has ${ + script.slice(off).length + } remaining.` + }; + } + + // Next -length bytes are little endian length of data. + switch (op.length) { + case -1: + l = script[off]; + negativeLengthHelper(); + break; + case -2: + l = (script[off + 1] << 8) | script[off]; + negativeLengthHelper(); + break; + case -4: + l = + (script[off + 3] << 24) | + (script[off + 2] << 16) | + (script[off + 1] << 8) | + script[off]; + negativeLengthHelper(); + break; + default: + return { + retScript, + error: `invalid opcode length ${op.length}` + }; + } + } + + retScript.push(pop); + } + + return { retScript }; +}; diff --git a/app/helpers/tickets.js b/app/helpers/tickets.js index 2b11f7b9c0..0defb7aded 100644 --- a/app/helpers/tickets.js +++ b/app/helpers/tickets.js @@ -1,4 +1,6 @@ import { GetTicketsResponse } from "../middleware/walletrpc/api_pb"; +import { OP_RETURN, SStxPKHMinOutSize, ripemd160Size } from "constants"; +import { checkEncode } from "./addresses"; export const TicketTypes = new Map([ [GetTicketsResponse.TicketDetails.TicketStatus.UNKNOWN, "unknown"], @@ -20,7 +22,7 @@ export const TicketTypes = new Map([ // Reference for what a voting script looks like (as of this writing): // https://github.com/decred/dcrd/blob/3f3174c987091b03bb34f1fdf4614d10ce6fbfc5/blockchain/stake/staketx.go#L458 export function decodeVoteScript(network, outputScript) { - if (outputScript.length < 4 || outputScript[0] !== 0x6a) { + if (outputScript.length < 4 || outputScript[0] !== OP_RETURN) { // 0x6a == OP_RETURN return null; } @@ -137,3 +139,75 @@ export function decodeVoteScript(network, outputScript) { return voteChoices; } + +// addrFromSStxPkScrCommitment extracts a P2SH or P2PKH address from a ticket +// commitment pkScript. +export const addrFromSStxPkScrCommitment = (pkScript, params) => { + if (pkScript.length < SStxPKHMinOutSize) { + return { error: `pkScript must be ${SStxPKHMinOutSize} bytes` }; + } + + // The MSB of the encoded amount specifies if the output is P2SH. Since + // it is encoded with little endian, the MSB is in final byte in the encoded + // amount. + // + // This is a faster equivalent of: + // + // amtBytes := script[22:30] + // amtEncoded := binary.LittleEndian.Uint64(amtBytes) + // isP2SH := (amtEncoded & uint64(1<<63)) != 0 + + const isP2SH = pkScript[29] & (0x80 != 0); + // The 20 byte PKH or SH. + const hashBytes = pkScript.slice(2, 22); + + // Return the correct address type. + if (isP2SH) { + return newAddressScriptHashFromHash(hashBytes, params); + } + return newAddressPubKeyHash(hashBytes, params, 0); +}; + +// newAddressScriptHashFromHash is the internal API to create a script hash +// address with a known leading identifier byte for a network, rather than +// looking it up through its parameters. This is useful when creating a new +// address structure from a string encoding where the identifier byte is already +// known. +const newAddressScriptHashFromHash = (scriptHash, netId) => { + if (scriptHash.length !== ripemd160Size) { + return { error: "pkHash must be 20 bytes" }; + } + + return checkEncode(scriptHash.slice(0, 20), netId); +}; + +// newAddressPubKeyHash returns a new AddressPubKeyHash. pkHash must +// be 20 bytes. +const newAddressPubKeyHash = (scriptHash, net, algo) => { + // Ensure the provided signature algo is supported. + let addrID; + switch (algo) { + // when extracting address from a SStxPkScrCommitment script, the algo used + // is dcrec.STEcdsaSecp256k1 equals 0. + case 0: + addrID = net.PubKeyHashAddrID; + break; + // TODO finish getting address from pubkeyHash to add support to decrediton. + // case dcrec.STEd25519: + // addrID = net.AddrIDPubKeyHashEd25519V0() + // case dcrec.STSchnorrSecp256k1: + // addrID = net.AddrIDPubKeyHashSchnorrV0() + default: + return null; + } + + // Ensure the provided pubkey hash length is valid. + if (scriptHash.length !== ripemd160Size) { + return { error: "pkHash must be 20 bytes" }; + } + + const addr = { netID: addrID }; + addr.hash = scriptHash.slice(0, ripemd160Size); + + return checkEncode(scriptHash.slice(0, 20), addrID); +}; diff --git a/app/wallet/service.js b/app/wallet/service.js index 93338132cc..42d9e0c376 100644 --- a/app/wallet/service.js +++ b/app/wallet/service.js @@ -24,6 +24,7 @@ import { decodeRawTransaction as decodeHelper } from "helpers"; import { extractPkScriptAddrs } from "helpers/scripts"; +import { addrFromSStxPkScrCommitment } from "helpers/tickets"; const promisify = (fn) => (...args) => new Promise((ok, fail) => @@ -297,10 +298,20 @@ export const decodeRawTransaction = (rawTx, chainParams) => { } const decodedTx = decodeHelper(rawTx); - decodedTx.outputs = decodedTx.outputs.map((o) => { + decodedTx.outputs = decodedTx.outputs.map((o, i) => { + let decodedScript = extractPkScriptAddrs(0, o.script, chainParams); + // if scriptClass equals NullDataTy (which is 0) && i&1 == 1 + // extract address from SStxPkScrCommitment script. + if (decodedScript.scriptClass === 0 && i&1 === 1) { + decodedScript = { + address: addrFromSStxPkScrCommitment(o.script, chainParams), + scriptClass: 0, + requiredSig: 0 + }; + } return { ...o, - decodedScript: extractPkScriptAddrs(0, o.script, chainParams) + decodedScript }; }); diff --git a/test/data/HexTransactions.js b/test/data/HexTransactions.js index 9853628a2f..d0640c5c82 100644 --- a/test/data/HexTransactions.js +++ b/test/data/HexTransactions.js @@ -520,4 +520,7 @@ export const hugeMixedSplitTx = "010000006af37808f8f33aafcfd91f9158f110ea9dac7ca "07844c37f8df455cf583a9789196828fdabcc0f7cc5f9ccda32911035ce8a5aa02203f00e66a8f6609" + "cf63390d82f25f4cb4d35668172e43bd38d5bd91bffb90e1d40121036e19fde113020f8afe936d29db" + "215053cf8513ce5bdde7a7c59d45541d69b498"; - \ No newline at end of file + + + +export const purchasedTicketTx = "0100000001846bc0a283d908e48c24899380dc1550e99548e6049dd79453c20a94f5f9a7690000000000ffffffff030b848d420100000000001aba76a9146ba1f7a65b7f3a1db3455e17b11b48fc55df25b788ac00000000000000000000206a1e267b1f1a005cec33d03338e6200ec6e616ed9d88af8f8d42010000000058000000000000000000001abd76a914000000000000000000000000000000000000000088ac0000000084c3060001af8f8d420100000000000000ffffffff6a473044022043f26897017cdb9cf61d9f13aa0ece87c1eb61c2cf83371568d4d2093830beff02203948589692b7a0dd3e504286afe9e309989f9f298c7e9070610c0b6f2aa873410121030875038ca21fee734f101888fef4315270e59d36eaff9830616e1e4dcf22f82b" diff --git a/test/data/decodedTransactions.js b/test/data/decodedTransactions.js new file mode 100644 index 0000000000..97cf601f6c --- /dev/null +++ b/test/data/decodedTransactions.js @@ -0,0 +1,98 @@ +export const decodedPurchasedTicketTx = { + "version": 1, + "serType": 0, + "numInputs": 1, + "inputs": [ + { + "prevTxId": "69a7f9f5940ac25394d79d04e64895e95015dc809389248ce408d983a2c06b84", + "outputIndex": 0, + "outputTree": 0, + "sequence": 4294967295, + "index": 0 + } + ], + "numOutputs": 3, + "outputs": [ + { + "value": 5411537931, + "version": 0, + "script": Buffer.from([186, 118, 169, 20, 107, 161, 247, 166, 91, 127, 58, 29, 179, 69, 94, 23, 177, 27, 72, 252, 85, 223, 37, 183, 136, 172]), + "index": 0, + "decodedScript": { "scriptClass": 6, "address": "TsaqEon1LTaYNUC1yj4mL4rm5ps5UuqvrZN", "requiredSig": 1 } + }, + { + "value": 0, + "version": 0, + "script": Buffer.from([106, 30, 38, 123, 31, 26, 0, 92, 236, 51, 208, 51, 56, 230, 32, 14, 198, 230, 22, 237, 157, 136, 175, 143, 141, 66, 1, 0, 0, 0, 0, 88]), + "index": 1, + "decodedScript": { "address": "TsUXbeUq2GBNkxFd3iYC8Qk9cjyRqJ86FCH", "scriptClass": 0, "requiredSig": 0 } + }, + { + "value": 0, + "version": 0, + "script": Buffer.from([189, 118, 169, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 172]), + "index": 2, + "decodedScript": { "scriptClass": 9, "address": "TsR28UZRprhgQQhzWns2M6cAwchrNVvbYq2", "requiredSig": 1 } + } + ], + "lockTime": 0, + "expiry": 443268 +}; + +// multiTxPrefix is a tx prefix in the format of how our decodeTxs are. We get +// this format from wallet.decodeRawTransaction(). +export const multiTxPrefix = { + serType: 1, // TxSerializeNoWitness, + version: 1, + numInputs: 1, + numOutputs: 2, + inputs: [ + { + prevTxId: "0000000000000000000000000000000000000000000000000000000000000000", + outputIndex: 0xffffffff, + sequence: 0xffffffff, + outputTree: 0, + index: 0 + } + ], + outputs: [ + { + value: 0x12a05f200, + index: 0, + version: 0xabab, + script: Buffer.from([ + 0x41, // OP_DATA_65 + 0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5, + 0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42, + 0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1, + 0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24, + 0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97, + 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, + 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, + 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, + 0xa6, // 65-byte signature + 0xac, // OP_CHECKSIG + ]), + }, + { + index: 1, + value: 0x5f5e100, + version: 0xbcbc, + script: Buffer.from([ + 0x41, // OP_DATA_65 + 0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5, + 0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42, + 0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1, + 0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24, + 0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97, + 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, + 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, + 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, + 0xa6, // 65-byte signature + 0xac, // OP_CHECKSIG + ]), + }, + ], + lockTime: 0, + expiry: 0, +}; diff --git a/test/data/transactionsToEncode.js b/test/data/rawTransactions.js similarity index 53% rename from test/data/transactionsToEncode.js rename to test/data/rawTransactions.js index 1d09022793..1ffbd4e196 100644 --- a/test/data/transactionsToEncode.js +++ b/test/data/rawTransactions.js @@ -1,63 +1,3 @@ - -// multiTxPrefix is a tx prefix in the format of how our decodeTxs are. We get -// this format from wallet.decodeRawTransaction(). -export const multiTxPrefix = { - serType: 1, // TxSerializeNoWitness, - version: 1, - numInputs: 1, - numOutputs: 2, - inputs: [ - { - opRawHash: Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - prevTxId: "0000000000000000000000000000000000000000000000000000000000000000", - outputIndex: 0xffffffff, - sequence: 0xffffffff, - outputTree: 0, - index: 0 - } - ], - outputs: [ - { - value: 0x12a05f200, - index: 0, - version: 0xabab, - script: Buffer.from([ - 0x41, // OP_DATA_65 - 0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5, - 0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42, - 0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1, - 0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24, - 0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97, - 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, - 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, - 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, - 0xa6, // 65-byte signature - 0xac, // OP_CHECKSIG - ]), - }, - { - index: 1, - value: 0x5f5e100, - version: 0xbcbc, - script: Buffer.from([ - 0x41, // OP_DATA_65 - 0x04, 0xd6, 0x4b, 0xdf, 0xd0, 0x9e, 0xb1, 0xc5, - 0xfe, 0x29, 0x5a, 0xbd, 0xeb, 0x1d, 0xca, 0x42, - 0x81, 0xbe, 0x98, 0x8e, 0x2d, 0xa0, 0xb6, 0xc1, - 0xc6, 0xa5, 0x9d, 0xc2, 0x26, 0xc2, 0x86, 0x24, - 0xe1, 0x81, 0x75, 0xe8, 0x51, 0xc9, 0x6b, 0x97, - 0x3d, 0x81, 0xb0, 0x1c, 0xc3, 0x1f, 0x04, 0x78, - 0x34, 0xbc, 0x06, 0xd6, 0xd6, 0xed, 0xf6, 0x20, - 0xd1, 0x84, 0x24, 0x1a, 0x6a, 0xed, 0x8b, 0x63, - 0xa6, // 65-byte signature - 0xac, // OP_CHECKSIG - ]), - }, - ], - lockTime: 0, - expiry: 0, -} - // multiTxPrefixEncoded is the serialized buffer after being encoded. export const multiTxPrefixEncoded = Buffer.from([ 0x01, 0x00, 0x01, 0x00, // Version [0] diff --git a/test/data/script.js b/test/data/script.js index ffdea21fbb..6c3469140d 100644 --- a/test/data/script.js +++ b/test/data/script.js @@ -2,7 +2,7 @@ import { hexToBytes } from "../../app/helpers"; import { OP_DATA_45 } from "../../app/constants"; // TODO finish importing extractPkScriptAddrs method so we can finish testing. -export const scriptDataTest = [ +export const MAINNET_scriptDataTest = [ // { // name: "standard p2pk with compressed pubkey (0x02)", // script: hexToBytes("2102192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4ac"), @@ -54,6 +54,18 @@ export const scriptDataTest = [ // reqSigs: 1, // class: ScriptHashTy == 3, }, + // {"version":1,"serType":0,"numInputs":1,"inputs": + // [{"opRawHash":{"type":"Buffer","data": + // [132,107,192,162,131,217,8,228,140,36,137,147,128,220,21,80,233,149,72,230,4,157,215,148,83,194,10,148,245,249,167,105]}, + // "prevTxId":"69a7f9f5940ac25394d79d04e64895e95015dc809389248ce408d983a2c06b84","outputIndex":0,"outputTree":0,"sequence":4294967295,"index":0}], + // "numOutputs":3, + // "outputs":[{"value":5411537931,"version":0,"script":{"type":"Buffer","data": + // [186,118,169,20,107,161,247,166,91,127,58,29,179,69,94,23,177,27,72,252,85,223,37,183,136,172]},"index":0}, + // {"value":0,"version":0,"script":{"type":"Buffer", + // "data":[106,30,38,123,31,26,0,92,236,51,208,51,56,230,32,14,198,230,22,237,157,136,175,143,141,66,1,0,0,0,0,88]},"index":1} + // ,{"value":0,"version":0,"script":{"type":"Buffer","data":[189,118,169,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,136,172]},"index":2}] + // ,"lockTime":0,"expiry":443268} + // from real tx 60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1, vout 0 // { // name: "standard 1 of 2 multisig", @@ -180,10 +192,18 @@ export const scriptDataTest = [ { name: "script that does not parse", script: [OP_DATA_45], - expected: { "address": [], "requiredSig": 0, "scriptClass": 0 }, + expected: "opcode OP_DATA_45 requires 46 bytes, but script only has 1 remaining.", // reqSigs: 0, // class: NonStandardTy, // noparse: true, // }, } -] \ No newline at end of file +]; + +export const TESTNET_scriptDataTest = [ + { + name: "sstxcommitment tx", + script: [106,30,38,123,31,26,0,92,236,51,208,51,56,230,32,14,198,230,22,237,157,136,175,143,141,66,1,0,0,0,0,88], + expected: { "address": [], "requiredSig": 0, "scriptClass": 0 }, + } +] diff --git a/test/unit/helpers/encodeWitness.spec.js b/test/unit/helpers/encodeWitness.spec.js index fb81ede37b..709de582bd 100644 --- a/test/unit/helpers/encodeWitness.spec.js +++ b/test/unit/helpers/encodeWitness.spec.js @@ -3,7 +3,10 @@ import { selializeNoWitnessEncode, decodeRawTransaction } from "../../../app/helpers/msgTx"; -import { multiTxPrefix, multiTxPrefixEncoded } from "../../data/transactionsToEncode"; +import { hexToBytes } from "../../../app/helpers/byteActions"; +import { multiTxPrefixEncoded } from "../../data/rawTransactions"; +import { multiTxPrefix } from "../../data/decodedTransactions"; +import { txShouldFail } from "../../data/HexTransactions"; test("Needed size to serialize tx calculated by calculateSerializeSize():", () => { expect(calculateSerializeSize(multiTxPrefix)).toBe(211); diff --git a/test/unit/helpers/script.spec.js b/test/unit/helpers/script.spec.js index af1fde26a3..99f39aea4e 100644 --- a/test/unit/helpers/script.spec.js +++ b/test/unit/helpers/script.spec.js @@ -1,10 +1,16 @@ -import { scriptDataTest } from "../../data/script"; +import { MAINNET_scriptDataTest, TESTNET_scriptDataTest } from "../../data/script"; import { extractPkScriptAddrs } from "../../../app/helpers/scripts"; -import { MainNetParams } from "../../../app/constants"; +import { MainNetParams, TestNetParams } from "../../../app/constants"; const scriptVersion = 0; -scriptDataTest.forEach(testData => { +MAINNET_scriptDataTest.forEach(testData => { test(testData.name, () => { expect(extractPkScriptAddrs(scriptVersion, testData.script, MainNetParams)).toStrictEqual(testData.expected); }); }); + +TESTNET_scriptDataTest.forEach(testData => { + test(testData.name, () => { + expect(extractPkScriptAddrs(scriptVersion, testData.script, TestNetParams)).toStrictEqual(testData.expected); + }); +}); diff --git a/test/unit/wallet/service.spec.js b/test/unit/wallet/service.spec.js new file mode 100644 index 0000000000..2376665fb0 --- /dev/null +++ b/test/unit/wallet/service.spec.js @@ -0,0 +1,13 @@ +import { decodeRawTransaction } from "../../../app/wallet/service"; +import { hexToBytes } from "../../../app/helpers"; + +import { purchasedTicketTx } from "../../data/HexTransactions"; +import { decodedPurchasedTicketTx } from "../../data/decodedTransactions"; +import { MainNetParams, TestNetParams } from "../../../app/constants"; + +// TODO create integrations tests directory, differentiate them at our +// package.json mv this code part to there +test("decode ticket raw transaction", () => { + const bufferTx = Buffer.from(hexToBytes(purchasedTicketTx)); + expect(decodeRawTransaction(bufferTx, TestNetParams)).toStrictEqual(decodedPurchasedTicketTx); +});