Skip to content

Commit 49e2e24

Browse files
committed
support v2 transactions
1 parent 7ef263e commit 49e2e24

File tree

3 files changed

+141
-19
lines changed

3 files changed

+141
-19
lines changed

src/components/transactions/send/TransactionVerify.vue

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
import BigNumber from 'bignumber.js';
4343
import { formatPriceString } from '@/utils/format';
4444
import { mapState } from 'vuex';
45-
import { signTransaction } from '@/sia';
45+
import { v2SignTransaction } from '@/sia';
4646
import { scanTransactions } from '@/sync/scanner';
4747
import { broadcastTransaction } from '@/api/siacentral';
4848
@@ -69,20 +69,35 @@ export default {
6969
},
7070
siaTransaction() {
7171
return {
72-
minerFees: this.transaction.minerFees,
73-
siacoinInputs: this.transaction.siacoinInputs,
74-
siacoinOutputs: this.transaction.siacoinOutputs,
75-
siafundInputs: this.transaction.siafundInputs,
76-
siafundOutputs: this.transaction.siafundOutputs,
77-
signatures: this.transaction.siacoinInputs.map(i => ({
78-
parentID: i.parentID,
79-
coveredFields: { wholeTransaction: true },
72+
minerFee: this.transaction.minerFees[0],
73+
siacoinInputs: this.transaction.siacoinInputs.map(i => ({
74+
parent: {
75+
id: i.parentID
76+
},
77+
satisfiedPolicy: {
78+
policy: {
79+
type: 'uc',
80+
policy: i.unlockConditions
81+
}
82+
},
83+
value: i.value,
8084
index: i.index
81-
})).concat((this.transaction?.siafundInputs || []).map(i => ({
82-
parentID: i.parentID,
83-
coveredFields: { wholeTransaction: true },
85+
})),
86+
siacoinOutputs: this.transaction.siacoinOutputs,
87+
siafundInputs: (this.transaction.siafundInputs || []).map(i => ({
88+
parent: {
89+
id: i.parentID
90+
},
91+
satisfiedPolicy: {
92+
policy: {
93+
type: 'uc',
94+
policy: i.unlockCondtions
95+
}
96+
},
97+
value: i.value,
8498
index: i.index
85-
})))
99+
})),
100+
siafundOutputs: this.transaction.siafundOutputs
86101
};
87102
},
88103
changeIndex() {
@@ -105,19 +120,20 @@ export default {
105120
return this.remainder.eq(0);
106121
},
107122
requiredSignatures() {
108-
return this.siaTransaction.signatures.map(i => i.index);
123+
return this.siaTransaction.siacoinInputs.map(i => i.index).concat(
124+
(this.siaTransaction.siafundInputs || []).map(i => i.index));
109125
},
110126
spentOutputs() {
111127
if (!this.data || !this.transaction)
112128
return [];
113129
114-
return this.transaction.siacoinInputs.map(a => a.parentID);
130+
return this.transaction.siacoinInputs.map(a => a.parent.id);
115131
},
116132
spentSFOutputs() {
117133
if (!this.data || !this.transaction)
118134
return [];
119135
120-
return this.transaction.siafundInputs.map(a => a.parentID);
136+
return this.transaction.siafundInputs.map(a => a.parent.id);
121137
}
122138
},
123139
data() {
@@ -151,7 +167,7 @@ export default {
151167
}
152168
},
153169
broadcastTxnset(txnset) {
154-
return broadcastTransaction(txnset, null);
170+
return broadcastTransaction(null, txnset);
155171
},
156172
async onVerifyTxn() {
157173
if (this.sending)
@@ -168,8 +184,8 @@ export default {
168184
throw new Error('transaction not signed');
169185
break;
170186
case 'default':
171-
this.signed = await signTransaction(this.wallet.seed, this.wallet.currency,
172-
this.siaTransaction, this.requiredSignatures);
187+
console.log(JSON.stringify(this.siaTransaction, null, 2));
188+
this.signed = await v2SignTransaction(this.wallet.seed, this.siaTransaction, this.requiredSignatures);
173189
break;
174190
default:
175191
throw new Error('unsupported wallet type');

src/sia/index.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,13 @@ export function encodeUnlockHash(unlockconditions) {
7474

7575
export async function recoverAddresses(seed, currency, i = 0, lookahead = 25000, last = 0, progress) {
7676
return spawnWorker(['recoverAddresses', seed, currency, i, lookahead, last], 300000, progress);
77+
}
78+
79+
export function v2TxnSigHash(txn) {
80+
return spawnWorker(['v2TxnSigHash', JSON.stringify(txn)], 15000);
81+
}
82+
83+
export function v2SignTransaction(seed, txn, indexes) {
84+
const str = JSON.stringify(txn);
85+
return spawnWorker(['v2SignTransaction', seed, str, indexes], 15000);
7786
}

wasm/main.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ func main() {
3434
"encodeTransaction": js.FuncOf(encodeTransaction),
3535
"signTransaction": js.FuncOf(signTransaction),
3636
"encodeUnlockHash": js.FuncOf(encodeUnlockHash),
37+
"v2TxnSigHash": js.FuncOf(v2TxnSigHash),
38+
"v2SignTransaction": js.FuncOf(v2SignTransaction),
3739
})
3840

3941
c := make(chan bool, 1)
@@ -242,6 +244,101 @@ func generateAddresses(this js.Value, args []js.Value) any {
242244
return nil
243245
}
244246

247+
func v2TxnSigHash(this js.Value, args []js.Value) any {
248+
if err := checkArgs(args, js.TypeString, js.TypeString, js.TypeNumber); err != nil {
249+
return err.Error()
250+
}
251+
252+
w := api.NewClient(SIASCAN_ADDRESS, "")
253+
jsonTxn := args[0].String()
254+
255+
var txn types.V2Transaction
256+
if err := json.Unmarshal([]byte(jsonTxn), &txn); err != nil {
257+
return fmt.Sprintf("error parsing transaction: %s", err)
258+
}
259+
260+
cs, err := w.ConsensusTipState()
261+
if err != nil {
262+
return fmt.Sprintf("error getting consensus state: %s", err)
263+
}
264+
265+
sigHash := cs.InputSigHash(txn)
266+
return sigHash.String()
267+
}
268+
269+
func v2SignTransaction(this js.Value, args []js.Value) any {
270+
if err := checkArgs(args, js.TypeString, js.TypeString, js.TypeObject, js.TypeFunction); err != nil {
271+
return err.Error()
272+
}
273+
274+
w := api.NewClient(SIASCAN_ADDRESS, "")
275+
276+
phrase := args[0].String()
277+
jsonTxn := args[1].String()
278+
sigIndices := make([]uint64, args[2].Length())
279+
callback := args[3]
280+
281+
for i := range sigIndices {
282+
sigIndices[i] = uint64(args[2].Index(i).Int())
283+
}
284+
285+
var txn types.V2Transaction
286+
if err := json.Unmarshal([]byte(jsonTxn), &txn); err != nil {
287+
callback.Invoke(err.Error(), js.Null())
288+
return err.Error()
289+
}
290+
291+
if len(sigIndices) != len(txn.SiacoinInputs)+len(txn.SiafundInputs) {
292+
err := fmt.Errorf("expected %d signatures, got %d", len(txn.SiacoinInputs)+len(txn.SiafundInputs), len(sigIndices))
293+
callback.Invoke(err.Error(), js.Null())
294+
return err.Error()
295+
}
296+
297+
go func() {
298+
cs, err := w.ConsensusTipState()
299+
if err != nil {
300+
callback.Invoke(fmt.Sprintf("error getting consensus state: %s", err), js.Null())
301+
return
302+
}
303+
sigHash := cs.InputSigHash(txn)
304+
305+
var seed [32]byte
306+
defer clear(seed[:])
307+
if err := phraseToSeed(phrase, &seed); err != nil {
308+
callback.Invoke(err.Error(), js.Null())
309+
return
310+
}
311+
312+
for i := range txn.SiacoinInputs {
313+
// pop the first index from sigIndices
314+
index := sigIndices[0]
315+
sigIndices = sigIndices[1:]
316+
// sign the input
317+
sk := wallet.KeyFromSeed(&seed, index)
318+
sig := sk.SignHash(sigHash)
319+
txn.SiacoinInputs[i].SatisfiedPolicy.Signatures = []types.Signature{sig}
320+
}
321+
322+
for i := range txn.SiafundInputs {
323+
// pop the first index from sigIndices
324+
index := sigIndices[0]
325+
sigIndices = sigIndices[1:]
326+
// sign the input
327+
sk := wallet.KeyFromSeed(&seed, index)
328+
sig := sk.SignHash(sigHash)
329+
txn.SiafundInputs[i].SatisfiedPolicy.Signatures = []types.Signature{sig}
330+
}
331+
332+
obj, err := interfaceToJSON(txn)
333+
if err != nil {
334+
callback.Invoke(fmt.Sprintf("error encoding signed transaction: %s", err), js.Null())
335+
return
336+
}
337+
callback.Invoke(js.Null(), obj)
338+
}()
339+
return nil
340+
}
341+
245342
func recoverAddresses(this js.Value, args []js.Value) any {
246343
if err := checkArgs(args, js.TypeString, js.TypeString, js.TypeNumber, js.TypeNumber, js.TypeNumber, js.TypeFunction); err != nil {
247344
return err.Error()

0 commit comments

Comments
 (0)