Skip to content

Commit c053fed

Browse files
committed
fix: tx duplicates
1 parent d872c67 commit c053fed

File tree

6 files changed

+462
-28
lines changed

6 files changed

+462
-28
lines changed

src/lib/adamant-api/index.js

+49-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Queue from 'promise-queue'
22
import { Base64 } from 'js-base64'
33

4-
import { Transactions, Delegates, MessageType } from '@/lib/constants'
4+
import { Transactions, Delegates, MessageType, TransactionStatus as TS } from '@/lib/constants'
55
import utils from '@/lib/adamant'
66
import client from '@/lib/nodes/adm'
77
import { encryptPassword } from '@/lib/idb/crypto'
@@ -10,6 +10,7 @@ import { i18n } from '@/i18n'
1010
import store from '@/store'
1111
import { isStringEqualCI } from '@/lib/textHelpers'
1212
import { parseCryptoAddressesKVStxs } from '@/lib/store-crypto-address'
13+
import { getTransactionId } from '@/utils/transactions/id'
1314

1415
Queue.configure(Promise)
1516

@@ -129,6 +130,19 @@ export function getPublicKey(address = '') {
129130
})
130131
}
131132

133+
/**
134+
* Update message in the chat store
135+
* @param {{
136+
* id: number,
137+
* realId?: string,
138+
* status: number,
139+
* partnerId: string
140+
* }} payload
141+
*/
142+
function updateStoreMessage(payload) {
143+
store.commit('chat/updateMessage', payload)
144+
}
145+
132146
/**
133147
* @typedef {Object} MsgParams
134148
* @property {string} to target address
@@ -148,27 +162,54 @@ export function getPublicKey(address = '') {
148162
* @returns {Promise<{success: boolean, transactionId: string}>}
149163
*/
150164
export function sendMessage(params) {
151-
return getPublicKey(params.to)
152-
.then((publicKey) => {
165+
let realId;
166+
167+
const { to, id, amount, type, message } = params;
168+
169+
return getPublicKey(to)
170+
.then(async (publicKey) => {
153171
const text =
154-
typeof params.message === 'string' ? params.message : JSON.stringify(params.message)
172+
typeof message === 'string' ? message : JSON.stringify(message)
155173
const encoded = utils.encodeMessage(text, publicKey, myKeypair.privateKey)
156174
const chat = {
157175
message: encoded.message,
158176
own_message: encoded.nonce,
159-
type: params.type || 1
177+
type: type || 1
160178
}
161179

162180
const transaction = newTransaction(Transactions.CHAT_MESSAGE)
163-
transaction.amount = params.amount ? utils.prepareAmount(params.amount) : 0
181+
transaction.amount = amount ? utils.prepareAmount(amount) : 0
164182
transaction.asset = { chat }
165-
transaction.recipientId = params.to
183+
transaction.recipientId = to
166184

167185
return client.post('/api/chats/process', (endpoint) => {
168-
return { transaction: signTransaction(transaction, endpoint.timeDelta) }
186+
const signedTransaction = signTransaction(transaction, endpoint.timeDelta)
187+
188+
if (id) {
189+
realId = getTransactionId(signedTransaction)
190+
191+
// update `message.status` to 'REGISTERED'
192+
// and `message.id` with `realId` from signedTransaction
193+
updateStoreMessage({
194+
id,
195+
realId,
196+
status: TS.REGISTERED,
197+
partnerId: params.to
198+
})
199+
}
200+
201+
202+
return { transaction: signedTransaction }
169203
})
170204
})
171205
.catch((reason) => {
206+
// update `message.status` to 'REJECTED'
207+
updateStoreMessage({
208+
id: realId ?? id,
209+
status: TS.REJECTED,
210+
partnerId: to
211+
})
212+
172213
return reason
173214
})
174215
}

src/lib/chat/helpers/queueMessage.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ const queue = new Queue(maxConcurent, maxQueue)
1212
* @param {string} recipientId
1313
* @returns {Promise}
1414
*/
15-
export function queueMessage(message, recipientId, type) {
15+
export function queueMessage(message, recipientId, type, id) {
1616
return queue.add(() => {
1717
return admApi.sendMessage({
1818
to: recipientId,
1919
message,
20-
type
20+
type,
21+
id
2122
})
2223
})
2324
}

src/store/modules/chat/index.js

+2-18
Original file line numberDiff line numberDiff line change
@@ -733,32 +733,16 @@ const actions = {
733733
})
734734
: message
735735

736-
return queueMessage(messageAsset, recipientId, type)
737-
.then((res) => {
736+
return queueMessage(messageAsset, recipientId, type, messageObject.id)
737+
.then(async (res) => {
738738
// @todo this check must be performed on the server
739739
if (!res.success) {
740740
throw new Error(i18n.global.t('chats.message_rejected'))
741741
}
742742

743-
// update `message.status` to 'REGISTERED'
744-
// and `message.id` with `realId` from server
745-
commit('updateMessage', {
746-
id: messageObject.id,
747-
realId: res.transactionId,
748-
status: TS.REGISTERED, // not confirmed yet, wait to be stored in the blockchain (optional line)
749-
partnerId: recipientId
750-
})
751-
752743
return res
753744
})
754745
.catch((err) => {
755-
// update `message.status` to 'REJECTED'
756-
commit('updateMessage', {
757-
id: messageObject.id,
758-
status: TS.REJECTED,
759-
partnerId: recipientId
760-
})
761-
762746
throw err // call the error again so that it can be processed inside view
763747
})
764748
},

src/utils/bignumber.ts

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import BigNumber from 'bignumber.js';
2+
3+
type Endian = 1 | -1 | 'big' | 'little';
4+
5+
interface TransformBufferOptions {
6+
endian?: Endian;
7+
size?: number | 'auto';
8+
}
9+
10+
type EndianMap = {
11+
[K in Endian]: 'big' | 'little';
12+
};
13+
14+
const endianMap: EndianMap = {
15+
1: 'big',
16+
'-1': 'little',
17+
big: 'big',
18+
little: 'little',
19+
};
20+
21+
export const fromBuffer = (buf: Buffer, opts: TransformBufferOptions = {}) => {
22+
const endian = resolveEndian(opts);
23+
24+
const size = resolveSize(opts, buf.length);
25+
26+
validateBufferLength(buf, size);
27+
28+
const hex = bufferToHexArray(buf, size, endian);
29+
30+
return new BigNumber(hex.join(''), 16);
31+
};
32+
33+
export const toBuffer = (
34+
bignumber: BigNumber,
35+
opts: TransformBufferOptions | 'mpint' = {}
36+
) => {
37+
if (typeof opts === 'string') {
38+
return toMpintBuffer(bignumber);
39+
}
40+
41+
const endian = resolveEndian(opts);
42+
const hex = bignumberToHex(bignumber);
43+
44+
const size = resolveSize(opts, hex.length / 2);
45+
46+
return hexToBuffer(hex, size, endian);
47+
};
48+
49+
export function resolveEndian(opts: TransformBufferOptions): Endian {
50+
if (!opts.endian) {
51+
return 'big';
52+
}
53+
54+
return endianMap[opts.endian];
55+
}
56+
57+
export function resolveSize(
58+
opts: TransformBufferOptions,
59+
defaultSize: number
60+
): number {
61+
return opts.size === 'auto' ? Math.ceil(defaultSize) : opts.size || 1;
62+
}
63+
64+
export function validateBufferLength(buf: Buffer, size: number) {
65+
if (buf.length % size !== 0) {
66+
throw new RangeError(
67+
`Buffer length (${buf.length}) must be a multiple of size (${size})`
68+
);
69+
}
70+
}
71+
72+
export function bufferToHexArray(
73+
buf: Buffer,
74+
size: number,
75+
endian: Endian
76+
): string[] {
77+
const hex: string[] = [];
78+
79+
for (let i = 0; i < buf.length; i += size) {
80+
const chunk: string[] = [];
81+
for (let j = 0; j < size; j++) {
82+
const chunkIndex = endian === 'big' ? j : size - j - 1;
83+
chunk.push(buf[i + chunkIndex].toString(16).padStart(2, '0'));
84+
}
85+
hex.push(chunk.join(''));
86+
}
87+
88+
return hex;
89+
}
90+
91+
export function bignumberToHex(bignumber: BigNumber): string {
92+
const hex = bignumber.toString(16);
93+
if (hex.charAt(0) === '-') {
94+
throw new Error('Converting negative numbers to Buffers not supported yet');
95+
}
96+
return hex;
97+
}
98+
99+
export function hexToBuffer(hex: string, size: number, endian: Endian): Buffer {
100+
const len = Math.ceil(hex.length / (2 * size)) * size;
101+
const buf = Buffer.alloc(len);
102+
103+
while (hex.length < 2 * len) {
104+
hex = '0' + hex;
105+
}
106+
107+
const hx = hex
108+
.split(new RegExp('(.{' + 2 * size + '})'))
109+
.filter(s => s.length > 0);
110+
111+
hx.forEach((chunk, i) => {
112+
for (let j = 0; j < size; j++) {
113+
const ix = i * size + (endian === 'big' ? j : size - j - 1);
114+
buf[ix] = parseInt(chunk.slice(j * 2, j * 2 + 2), 16);
115+
}
116+
});
117+
118+
return buf;
119+
}
120+
121+
function toMpintBuffer(bignumber: BigNumber): Buffer {
122+
const buf = toBuffer(bignumber.abs(), {size: 1, endian: 'big'});
123+
124+
let len = buf.length === 1 && buf[0] === 0 ? 0 : buf.length;
125+
126+
if (buf[0] & 0x80) len++;
127+
128+
const ret = Buffer.alloc(4 + len);
129+
if (len > 0) buf.copy(ret, 4 + (buf[0] & 0x80 ? 1 : 0));
130+
if (buf[0] & 0x80) ret[4] = 0;
131+
132+
ret[0] = len & (0xff << 24);
133+
ret[1] = len & (0xff << 16);
134+
ret[2] = len & (0xff << 8);
135+
ret[3] = len & (0xff << 0);
136+
137+
// Two's compliment for negative integers
138+
const isNeg = bignumber.lt(0);
139+
if (isNeg) {
140+
for (let i = 4; i < ret.length; i++) {
141+
ret[i] = 0xff - ret[i];
142+
}
143+
}
144+
ret[4] = (ret[4] & 0x7f) | (isNeg ? 0x80 : 0);
145+
if (isNeg) ret[ret.length - 1]++;
146+
147+
return ret;
148+
}

0 commit comments

Comments
 (0)