1
1
import * as $ from "../deps/scale.ts" ;
2
2
import { assert } from "../deps/std/testing/asserts.ts" ;
3
3
import * as H from "../hashers/mod.ts" ;
4
+ import * as ss58 from "../ss58/mod.ts" ;
5
+ import { hex } from "../util/mod.ts" ;
4
6
import { $null , DeriveCodec } from "./Codec.ts" ;
5
7
import { Metadata } from "./Metadata.ts" ;
6
8
@@ -21,7 +23,13 @@ export interface Signature {
21
23
value : Uint8Array ;
22
24
}
23
25
24
- export type SignExtrinsic = ( message : Uint8Array ) => Signature | Promise < Signature > ;
26
+ export type SignExtrinsic =
27
+ | ( ( message : Uint8Array ) => Signature | Promise < Signature > )
28
+ | PolkadotSigner ;
29
+
30
+ export interface PolkadotSigner {
31
+ signPayload ( payload : any ) : Promise < { signature : string } > ;
32
+ }
25
33
26
34
export interface Extrinsic {
27
35
protocolVersion : number ;
@@ -41,6 +49,7 @@ interface ExtrinsicCodecProps {
41
49
metadata : Metadata ;
42
50
deriveCodec : DeriveCodec ;
43
51
sign : SignExtrinsic ;
52
+ prefix : number ;
44
53
}
45
54
46
55
export function $extrinsic ( props : ExtrinsicCodecProps ) : $ . Codec < Extrinsic > {
@@ -53,8 +62,12 @@ export function $extrinsic(props: ExtrinsicCodecProps): $.Codec<Extrinsic> {
53
62
const callTy = props . metadata . tys [ callTyI ] ;
54
63
assert ( callTy ?. type === "Union" ) ;
55
64
const $call = deriveCodec ( callTyI ) ;
56
- const $extra = getExtrasCodec ( signedExtensions . map ( ( x ) => x . ty ) ) ;
57
- const $additional = getExtrasCodec ( signedExtensions . map ( ( x ) => x . additionalSigned ) ) ;
65
+ const [ $extra , extraPjsInfo ] = getExtensionInfo ( pjsExtraKeyMap , "ty" ) ;
66
+ const [ $additional , additionalPjsInfo ] = getExtensionInfo (
67
+ pjsAdditionalKeyMap ,
68
+ "additionalSigned" ,
69
+ ) ;
70
+ const pjsInfo = [ ...extraPjsInfo , ...additionalPjsInfo ] ;
58
71
59
72
const toSignSize = $call . _staticSize + $extra . _staticSize + $additional . _staticSize ;
60
73
const totalSize = 1 + $address . _staticSize + $sig . _staticSize + toSignSize ;
@@ -74,30 +87,67 @@ export function $extrinsic(props: ExtrinsicCodecProps): $.Codec<Extrinsic> {
74
87
} ;
75
88
const { signature } = extrinsic ;
76
89
if ( signature ) {
90
+ $address . _encode ( buffer , signature . address ) ;
77
91
if ( "additional" in signature ) {
78
- $address . _encode ( buffer , signature . address ) ;
79
92
const toSignBuffer = new $ . EncodeBuffer ( buffer . stealAlloc ( toSignSize ) ) ;
80
93
$call . _encode ( toSignBuffer , call ) ;
81
94
const callEnd = toSignBuffer . finishedSize + toSignBuffer . index ;
82
- $extra . _encode ( toSignBuffer , signature . extra ) ;
83
- const extraEnd = toSignBuffer . finishedSize + toSignBuffer . index ;
84
- $additional . _encode ( toSignBuffer , signature . additional ) ;
85
- const toSignEncoded = toSignBuffer . finish ( ) ;
86
- const callEncoded = toSignEncoded . subarray ( 0 , callEnd ) ;
87
- const extraEncoded = toSignEncoded . subarray ( callEnd , extraEnd ) ;
88
- const toSign = toSignEncoded . length > 256
89
- ? H . Blake2_256 . hash ( toSignEncoded )
90
- : toSignEncoded ;
91
- const sig = props . sign ( toSign ) ;
92
- if ( sig instanceof Promise ) {
93
- $sigPromise . _encode ( buffer , sig ) ;
95
+ if ( "signPayload" in props . sign ) {
96
+ const exts = [ ...signature . extra , ...signature . additional ] ;
97
+ const extEnds = [ ] ;
98
+ for ( let i = 0 ; i < pjsInfo . length ; i ++ ) {
99
+ pjsInfo [ i ] ! . codec . _encode ( toSignBuffer , exts [ i ] ) ;
100
+ extEnds . push ( toSignBuffer . finishedSize + toSignBuffer . index ) ;
101
+ }
102
+ const extraEnd = extEnds [ extraPjsInfo . length - 1 ] ?? callEnd ;
103
+ const toSignEncoded = toSignBuffer . finish ( ) ;
104
+ const callEncoded = toSignEncoded . subarray ( 0 , callEnd ) ;
105
+ const extraEncoded = toSignEncoded . subarray ( callEnd , extraEnd ) ;
106
+ if ( signature . address . type !== "Id" ) {
107
+ throw new Error ( "polkadot signer: address types other than Id are not supported" ) ;
108
+ }
109
+ const payload : Record < string , unknown > = {
110
+ address : ss58 . encode ( props . prefix , signature . address . value ) ,
111
+ method : hex . encodePrefixed ( callEncoded ) ,
112
+ signedExtensions : signedExtensions . map ( ( x ) => x . ident ) ,
113
+ version : extrinsic . protocolVersion ,
114
+ } ;
115
+ let last = callEnd ;
116
+ for ( let i = 0 ; i < pjsInfo . length ; i ++ ) {
117
+ const { key } = pjsInfo [ i ] ! ;
118
+ if ( ! key ) throw new Error ( "polkadot signer: unknown extension" ) ;
119
+ payload [ key ] = typeof exts [ i ] === "number"
120
+ ? exts [ i ]
121
+ : hex . encodePrefixed ( toSignEncoded . subarray ( last , extEnds [ i ] ! ) ) ;
122
+ last = extEnds [ i ] ! ;
123
+ }
124
+ const signer = props . sign ;
125
+ buffer . writeAsync ( 0 , async ( buffer ) => {
126
+ const { signature } = await signer . signPayload ( payload ) ;
127
+ buffer . insertArray ( hex . decode ( signature ) ) ;
128
+ } ) ;
129
+ buffer . insertArray ( extraEncoded ) ;
130
+ buffer . insertArray ( callEncoded ) ;
94
131
} else {
95
- $sig . _encode ( buffer , sig ) ;
132
+ $extra . _encode ( toSignBuffer , signature . extra ) ;
133
+ const extraEnd = toSignBuffer . finishedSize + toSignBuffer . index ;
134
+ $additional . _encode ( toSignBuffer , signature . additional ) ;
135
+ const toSignEncoded = toSignBuffer . finish ( ) ;
136
+ const callEncoded = toSignEncoded . subarray ( 0 , callEnd ) ;
137
+ const extraEncoded = toSignEncoded . subarray ( callEnd , extraEnd ) ;
138
+ const toSign = toSignEncoded . length > 256
139
+ ? H . Blake2_256 . hash ( toSignEncoded )
140
+ : toSignEncoded ;
141
+ const sig = props . sign ( toSign ) ;
142
+ if ( sig instanceof Promise ) {
143
+ $sigPromise . _encode ( buffer , sig ) ;
144
+ } else {
145
+ $sig . _encode ( buffer , sig ) ;
146
+ }
147
+ buffer . insertArray ( extraEncoded ) ;
148
+ buffer . insertArray ( callEncoded ) ;
96
149
}
97
- buffer . insertArray ( extraEncoded ) ;
98
- buffer . insertArray ( callEncoded ) ;
99
150
} else {
100
- $address . _encode ( buffer , signature . address ) ;
101
151
$sig . _encode ( buffer , signature . sig ) ;
102
152
$extra . _encode ( buffer , signature . extra ) ;
103
153
$call . _encode ( buffer , call ) ;
@@ -146,7 +196,29 @@ export function $extrinsic(props: ExtrinsicCodecProps): $.Codec<Extrinsic> {
146
196
function findExtrinsicTypeParam ( name : string ) {
147
197
return metadata . tys [ metadata . extrinsic . ty ] ?. params . find ( ( x ) => x . name === name ) ?. ty ;
148
198
}
149
- function getExtrasCodec ( is : number [ ] ) {
150
- return $ . tuple ( ...is . map ( ( i ) => deriveCodec ( i ) ) . filter ( ( x ) => x !== $null ) ) ;
199
+ function getExtensionInfo (
200
+ keyMap : Record < string , string | undefined > ,
201
+ key : "ty" | "additionalSigned" ,
202
+ ) : [ codec : $ . Codec < any > , pjsInfo : { key : string | undefined ; codec : $ . Codec < any > } [ ] ] {
203
+ const pjsInfo = signedExtensions
204
+ . map ( ( e ) => ( { key : keyMap [ e . ident ] , codec : deriveCodec ( e [ key ] ) } ) )
205
+ . filter ( ( x ) => x . codec !== $null ) ;
206
+ return [ $ . tuple ( ...pjsInfo . map ( ( x ) => x . codec ) ) , pjsInfo ] ;
151
207
}
152
208
}
209
+
210
+ const pjsExtraKeyMap : Record < string , string > = {
211
+ CheckEra : "era" ,
212
+ CheckMortality : "era" ,
213
+ ChargeTransactionPayment : "tip" ,
214
+ CheckNonce : "nonce" ,
215
+ } ;
216
+
217
+ const pjsAdditionalKeyMap : Record < string , string > = {
218
+ CheckEra : "blockHash" ,
219
+ CheckMortality : "blockHash" ,
220
+ CheckSpecVersion : "specVersion" ,
221
+ CheckTxVersion : "transactionVersion" ,
222
+ CheckVersion : "specVersion" ,
223
+ CheckGenesis : "genesisHash" ,
224
+ } ;
0 commit comments