forked from IbhzHazem/Sawtooth-Cryptomoji
-
Notifications
You must be signed in to change notification settings - Fork 0
/
transactions.js
120 lines (105 loc) · 3.5 KB
/
transactions.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import {
Transaction,
TransactionHeader,
Batch,
BatchHeader,
BatchList
} from 'sawtooth-sdk/protobuf';
import { createHash } from 'crypto';
import { getPublicKey, sign } from './signing.js';
import { encode } from './encoding.js';
const FAMILY_NAME = 'cryptomoji';
const FAMILY_VERSION = '0.1';
const NAMESPACE = '5f4d76';
// Takes a string and returns a hex-string SHA-512 hash
const hash = str => createHash('sha512').update(str).digest('hex');
// Returns a random 1-12 character string
const getNonce = () => (Math.random() * 10 ** 18).toString(36);
/**
* A function that takes a private key and a payload and returns a new
* signed Transaction instance.
*
* Hint:
* Remember ProtobufJS has two different APIs for encoding protobufs
* (which you'll use for the TransactionHeader) and for creating
* protobuf instances (which you'll use for the Transaction itself):
* - TransactionHeader.encode({ ... }).finish()
* - Transaction.create({ ... })
*
* Also, don't forget to encode your payload!
*/
export const createTransaction = (privateKey, payload) => {
const publicKey = getPublicKey(privateKey);
const encodedPayload = encode(payload);
const header = TransactionHeader.encode({
signerPublicKey: publicKey,
batcherPublicKey: publicKey,
familyName: FAMILY_NAME,
familyVersion: FAMILY_VERSION,
inputs: [ NAMESPACE ],
outputs: [ NAMESPACE ],
nonce: getNonce(),
payloadSha512: hash(encodedPayload)
}).finish();
return Transaction.create({
header,
headerSignature: sign(privateKey, header),
payload: encodedPayload
});
};
/**
* A function that takes a private key and one or more Transaction instances
* and returns a signed Batch instance.
*
* Should accept both multiple transactions in an array, or just one
* transaction with no array.
*/
export const createBatch = (privateKey, transactions) => {
const publicKey = getPublicKey(privateKey);
if (!Array.isArray(transactions)) {
transactions = [ transactions ];
}
const header = BatchHeader.encode({
signerPublicKey: publicKey,
transactionIds: transactions.map(t => t.headerSignature)
}).finish();
return Batch.create({
header,
headerSignature: sign(privateKey, header),
transactions
});
};
/**
* A fairly simple function that takes a one or more Batch instances and
* returns an encoded BatchList.
*
* Although there isn't much to it, axios has a bug when POSTing the generated
* Buffer. We've implemented it for you, transforming the Buffer so axios
* can handle it.
*/
export const encodeBatches = batches => {
if (!Array.isArray(batches)) {
batches = [ batches ];
}
const batchList = BatchList.encode({ batches }).finish();
// Axios will mishandle a Uint8Array constructed with a large ArrayBuffer.
// The easiest workaround is to take a slice of the array.
return batchList.slice();
};
/**
* A convenince function that takes a private key and one or more payloads and
* returns an encoded BatchList for submission. Each payload should be wrapped
* in a Transaction, which will be wrapped together in a Batch, and then
* finally wrapped in a BatchList.
*
* As with the other methods, it should handle both a single payload, or
* multiple payloads in an array.
*/
export const encodeAll = (privateKey, payloads) => {
if (!Array.isArray(payloads)) {
payloads = [ payloads ];
}
const transactions = payloads.map(p => createTransaction(privateKey, p));
const batch = createBatch(privateKey, transactions);
return encodeBatches(batch);
};