Page MenuHomePhabricator

D10628.id31270.diff
No OneTemporary

D10628.id31270.diff

diff --git a/web/cashtab/src/components/Common/Ticker.js b/web/cashtab/src/components/Common/Ticker.js
--- a/web/cashtab/src/components/Common/Ticker.js
+++ b/web/cashtab/src/components/Common/Ticker.js
@@ -34,6 +34,7 @@
opReturnPrefixHex: '6a',
opReturnPushDataHex: '04',
opReturnAppPrefixLengthHex: '04',
+ opPushDataOne: '4c',
appPrefixesHex: {
eToken: '534c5000',
cashtab: '00746162',
@@ -93,53 +94,60 @@
},
};
-export function getETokenEncodingSubstring() {
- let encodingStr =
- currency.opReturn.opReturnPrefixHex + // 6a
- currency.opReturn.opReturnAppPrefixLengthHex + // 04
- currency.opReturn.appPrefixesHex.eToken; // 534c5000
-
- return encodingStr;
-}
-
-export function getCashtabEncodingSubstring() {
- let encodingStr =
- currency.opReturn.opReturnPrefixHex + // 6a
- currency.opReturn.opReturnAppPrefixLengthHex + // 04
- currency.opReturn.appPrefixesHex.cashtab; // 00746162
-
- return encodingStr;
-}
-
-export function isCashtabOutput(hexStr) {
- if (!hexStr || typeof hexStr !== 'string') {
+export function parseOpReturn(hexStr) {
+ if (
+ !hexStr ||
+ typeof hexStr !== 'string' ||
+ hexStr.substring(0, 2) !== currency.opReturn.opReturnPrefixHex
+ ) {
return false;
}
- return hexStr.startsWith(getCashtabEncodingSubstring());
-}
-export function isEtokenOutput(hexStr) {
- if (!hexStr || typeof hexStr !== 'string') {
- return false;
- }
- return hexStr.startsWith(getETokenEncodingSubstring());
-}
+ hexStr = hexStr.slice(2); // remove the first byte i.e. 6a
-export function extractCashtabMessage(hexSubstring) {
- if (!hexSubstring || typeof hexSubstring !== 'string') {
- return '';
- }
- let substring = hexSubstring.replace(getCashtabEncodingSubstring(), ''); // remove the cashtab encoding
- substring = substring.slice(2); // remove the 2 bytes indicating the size of the next element on the stack e.g. a0 -> 160 bytes
- return substring;
-}
+ /*
+ * @Return: resultArray is structured as follows:
+ * resultArray[0] is the transaction type i.e. eToken prefix, cashtab prefix, external message itself if unrecognized prefix
+ * resultArray[1] is the actual cashtab message or the 2nd part of an external message
+ * resultArray[2 - n] are the additional messages for future protcols
+ */
+ let resultArray = [];
+ let message = '';
+ let hexStrLength = hexStr.length;
+
+ for (let i = 0; hexStrLength !== 0; i++) {
+ // part 1: check the preceding byte value for the subsequent message
+ let byteValue = hexStr.substring(0, 2);
+ let msgByteSize = 0;
+ if (byteValue === currency.opReturn.opPushDataOne) {
+ // if this byte is 4c then the next byte is the message byte size - retrieve the message byte size only
+ msgByteSize = parseInt(hexStr.substring(2, 4), 16); // hex to base16 decimal
+ hexStr = hexStr.slice(4); // strip the 4c + message byte size info
+ } else {
+ // take the byte as the message byte size
+ msgByteSize = parseInt(hexStr.substring(0, 2), 16); // hex to base16 decimal
+ hexStr = hexStr.slice(2); // strip the message byte size info
+ }
+
+ // part 2: parse the subsequent message based on bytesize
+ message = hexStr.substring(0, msgByteSize * 2);
+ if (message === currency.opReturn.appPrefixesHex.eToken) {
+ // add the extracted eToken prefix to array then exit loop
+ resultArray[i] = currency.opReturn.appPrefixesHex.eToken;
+ break;
+ } else if (message === currency.opReturn.appPrefixesHex.cashtab) {
+ // add the extracted Cashtab prefix to array
+ resultArray[i] = currency.opReturn.appPrefixesHex.cashtab;
+ } else {
+ // this is either an external message or a subsequent cashtab message loop to extract the message
+ resultArray[i] = message;
+ }
-export function extractExternalMessage(hexSubstring) {
- if (!hexSubstring || typeof hexSubstring !== 'string') {
- return '';
+ // strip out the parsed message
+ hexStr = hexStr.slice(msgByteSize * 2);
+ hexStrLength = hexStr.length;
}
- let substring = hexSubstring.slice(4); // remove the preceding OP_RETURN prefixes
- return substring;
+ return resultArray;
}
export function isValidCashPrefix(addressString) {
diff --git a/web/cashtab/src/components/Common/__tests__/Ticker.test.js b/web/cashtab/src/components/Common/__tests__/Ticker.test.js
--- a/web/cashtab/src/components/Common/__tests__/Ticker.test.js
+++ b/web/cashtab/src/components/Common/__tests__/Ticker.test.js
@@ -3,12 +3,8 @@
isValidCashPrefix,
isValidTokenPrefix,
toLegacy,
- isCashtabOutput,
- isEtokenOutput,
- extractCashtabMessage,
- extractExternalMessage,
- getETokenEncodingSubstring,
- getCashtabEncodingSubstring,
+ parseOpReturn,
+ currency,
} from '../Ticker';
test('Rejects cash address with bitcoincash: prefix', async () => {
@@ -117,75 +113,110 @@
);
});
-test('getCashtabEncodingSubstring() returns the appropriate substring for cashtab message outputs', async () => {
- const result = getCashtabEncodingSubstring();
- expect(result).toStrictEqual('6a0400746162');
-});
-
-test('getETokenEncodingSubstring() returns the appropriate substring for eToken outputs', async () => {
- const result = getETokenEncodingSubstring();
- expect(result).toStrictEqual('6a04534c5000');
-});
-
-test('isCashtabOutput() correctly validates a cashtab message output hex', async () => {
- const result = isCashtabOutput('6a04007461620b63617368746162756c6172');
- expect(result).toStrictEqual(true);
-});
-
-test('isCashtabOutput() correctly invalidates an external message output hex', async () => {
- const result = isCashtabOutput('6a0c7069616e6f74656e6e697332');
- expect(result).toStrictEqual(false);
-});
-
-test('isCashtabOutput() correctly handles null input', async () => {
- const result = isCashtabOutput(null);
- expect(result).toStrictEqual(false);
-});
-
-test('isCashtabOutput() correctly handles non-string input', async () => {
- const result = isCashtabOutput(7623723323);
- expect(result).toStrictEqual(false);
-});
-
-test('isCashtabOutput() correctly invalidates an external message output hex', async () => {
- const result = isCashtabOutput(
- '6a202731afddf3b83747943f0e650b938ea0670dcae2e08c415f53bd4c6acfd15e09',
+test('parseOpReturn() successfully parses a short cashtab message', async () => {
+ const result = Buffer.from(
+ parseOpReturn('6a04007461620d6368726973746d617320657665')[1],
+ 'hex',
);
- expect(result).toStrictEqual(false);
+ expect(result).toStrictEqual(Buffer.from('christmas eve'));
});
-test('isEtokenOutput() correctly validates an eToken output hex', async () => {
- const result = isEtokenOutput(
- '6a04534c500001010453454e442069b8431ddecf775393b1b36aa1d0ddcd7b342f1157b9671a03747378ed35ea0d08000000000000012c080000000000002008',
+test('parseOpReturn() successfully parses a long cashtab message where an additional PUSHDATA1 is present', async () => {
+ const result = Buffer.from(
+ parseOpReturn(
+ '6a04007461624ca054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e',
+ )[1],
+ 'hex',
+ );
+ expect(result).toStrictEqual(
+ Buffer.from(
+ 'Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testin',
+ ),
);
- expect(result).toStrictEqual(true);
});
-test('isEtokenOutput() correctly invalidates an eToken output hex', async () => {
- const result = isEtokenOutput(
- '5434c500001010453454e442069b8431ddecf775393b1b36aa1d0ddcd7b342f1157b9671a03747378ed35ea0d08000000000000012c080000000000002008',
+test('parseOpReturn() successfully parses a short external message', async () => {
+ const result = Buffer.from(
+ parseOpReturn('6a1173686f727420656c65637472756d313132')[0].replace(
+ currency.opReturn.appPrefixesHex.UnknownApp,
+ '',
+ ),
+ 'hex',
);
- expect(result).toStrictEqual(false);
+ expect(result).toStrictEqual(Buffer.from('short electrum112'));
});
-test('isEtokenOutput() correctly handles null input', async () => {
- const result = isEtokenOutput(null);
- expect(result).toStrictEqual(false);
+test('parseOpReturn() successfully parses a long external message where an additional PUSHDATA1 is present', async () => {
+ const result = Buffer.from(
+ parseOpReturn(
+ '6a4cd9656c65637472756d6520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d6940',
+ )[0].replace(currency.opReturn.appPrefixesHex.UnknownApp, ''),
+ 'hex',
+ );
+ expect(result).toStrictEqual(
+ Buffer.from(
+ 'electrume 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limi@',
+ ),
+ );
});
-test('isEtokenOutput() correctly handles non-string input', async () => {
- const result = isEtokenOutput(7623723323);
- expect(result).toStrictEqual(false);
+test('parseOpReturn() successfully parses a short external message that is segmented into separate short parts', async () => {
+ const parsedArray = parseOpReturn(
+ '6a1173686f727420656c65637472756d3131321173686f727420656c65637472756d3131321173686f727420656c65637472756d313132',
+ );
+ let result = parsedArray[0].replace(
+ currency.opReturn.appPrefixesHex.UnknownApp,
+ '',
+ );
+ if (parsedArray.length > 1) {
+ for (let i = 1; i <= parsedArray.length; i++) {
+ result = result + parsedArray[i];
+ }
+ }
+ result = Buffer.from(result, 'hex');
+ expect(result).toStrictEqual(
+ Buffer.from('short electrum112short electrum112short electrum112'),
+ );
});
-test('extractCashtabMessage() correctly extracts a Cashtab message', async () => {
- const result = extractCashtabMessage(
- '6a04007461620b63617368746162756c6172',
+test('parseOpReturn() successfully parses a long external message that is segmented into separate long parts', async () => {
+ const parsedArray = parseOpReturn(
+ '6a4cd9656c65637472756d6520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69404cd9656c65637472756d6520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d6940',
+ );
+ let result = parsedArray[0].replace(
+ currency.opReturn.appPrefixesHex.UnknownApp,
+ '',
+ );
+ if (parsedArray.length > 1) {
+ for (let i = 1; i <= parsedArray.length; i++) {
+ result = result + parsedArray[i];
+ }
+ }
+ result = Buffer.from(result, 'hex');
+ expect(result).toStrictEqual(
+ Buffer.from(
+ 'electrume 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limi@electrume 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limi@',
+ ),
);
- expect(result).toStrictEqual('63617368746162756c6172');
});
-test('extractExternalMessage() correctly extracts an external message', async () => {
- const result = extractExternalMessage('6a0d62696e676f656c65637472756d');
- expect(result).toStrictEqual('62696e676f656c65637472756d');
+test('parseOpReturn() successfully parses a long external message that is segmented into separate mix of long and short parts', async () => {
+ const parsedArray = parseOpReturn(
+ '6a4cd9656c65637472756d6520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69404cd9656c65637472756d6520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69742054657374696e672074686520323535206c696d69401173686f727420656c65637472756d313132',
+ );
+ let result = parsedArray[0].replace(
+ currency.opReturn.appPrefixesHex.UnknownApp,
+ '',
+ );
+ if (parsedArray.length > 1) {
+ for (let i = 1; i <= parsedArray.length; i++) {
+ result = result + parsedArray[i];
+ }
+ }
+ result = Buffer.from(result, 'hex');
+ expect(result).toStrictEqual(
+ Buffer.from(
+ 'electrume 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limi@electrume 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limit Testing the 255 limi@short electrum112',
+ ),
+ );
});
diff --git a/web/cashtab/src/hooks/useBCH.js b/web/cashtab/src/hooks/useBCH.js
--- a/web/cashtab/src/hooks/useBCH.js
+++ b/web/cashtab/src/hooks/useBCH.js
@@ -5,6 +5,7 @@
isEtokenOutput,
extractCashtabMessage,
extractExternalMessage,
+ parseOpReturn,
} from '@components/Common/Ticker';
import { isValidTokenStats } from '@utils/validation';
import SlpWallet from 'minimal-slp-wallet';
@@ -152,34 +153,59 @@
!Object.keys(thisOutput.scriptPubKey).includes('addresses')
) {
let hex = thisOutput.scriptPubKey.hex;
+ let parsedOpReturnArray = parseOpReturn(hex);
- if (isEtokenOutput(hex)) {
+ if (!parsedOpReturnArray) {
+ console.log(
+ 'useBCH.parsedTxData() error: parsed array is empty',
+ );
+ break;
+ }
+
+ let message = '';
+ let txType = parsedOpReturnArray[0];
+ if (txType === currency.opReturn.appPrefixesHex.eToken) {
// this is an eToken transaction
tokenTx = true;
- } else if (isCashtabOutput(hex)) {
- // this is a cashtab.com generated message
+ } else if (
+ txType === currency.opReturn.appPrefixesHex.cashtab
+ ) {
+ // this is a Cashtab message
try {
- substring = extractCashtabMessage(hex);
- opReturnMessage = Buffer.from(substring, 'hex');
+ opReturnMessage = Buffer.from(
+ parsedOpReturnArray[1],
+ 'hex',
+ );
isCashtabMessage = true;
} catch (err) {
// soft error if an unexpected or invalid cashtab hex is encountered
opReturnMessage = '';
console.log(
- 'useBCH.parsedTxHistory() error: invalid cashtab msg hex: ' +
- substring,
+ 'useBCH.parsedTxData() error: invalid cashtab msg hex: ' +
+ parsedOpReturnArray[1],
);
}
} else {
// this is an externally generated message
+ message = txType; // index 0 is the message content in this instance
+
+ // if there are more than one part to the external message
+ const arrayLength = parsedOpReturnArray.length;
+ for (let i = 1; i < arrayLength; i++) {
+ message = message + parsedOpReturnArray[i];
+ if (i > 1) {
+ // append double linebreaks to each additional message segment
+ message = message + '<br /><br />';
+ }
+ }
+
try {
- substring = extractExternalMessage(hex);
- opReturnMessage = Buffer.from(substring, 'hex');
+ opReturnMessage = Buffer.from(message, 'hex');
} catch (err) {
// soft error if an unexpected or invalid cashtab hex is encountered
opReturnMessage = '';
console.log(
- 'useBCH.parsedTxHistory() error: invalid external msg hex: ' +
+ 'useBCH.parsedTxData() error: invalid external msg hex: ' +
substring,
);
}

File Metadata

Mime Type
text/plain
Expires
Sat, Apr 26, 11:22 (16 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5573399
Default Alt Text
D10628.id31270.diff (18 KB)

Event Timeline