2
2
3
3
use {
4
4
crate :: id,
5
- borsh:: { BorshDeserialize , BorshSerialize } ,
6
5
solana_program:: {
7
6
instruction:: { AccountMeta , Instruction } ,
7
+ program_error:: ProgramError ,
8
8
pubkey:: Pubkey ,
9
9
} ,
10
+ std:: mem:: size_of,
10
11
} ;
11
12
12
13
/// Instructions supported by the program
13
- #[ derive( Clone , Debug , BorshSerialize , BorshDeserialize , PartialEq ) ]
14
- pub enum RecordInstruction {
14
+ #[ derive( Clone , Debug , PartialEq ) ]
15
+ pub enum RecordInstruction < ' a > {
15
16
/// Create a new record
16
17
///
17
18
/// Accounts expected by this instruction:
@@ -30,7 +31,7 @@ pub enum RecordInstruction {
30
31
/// Offset to start writing record, expressed as `u64`.
31
32
offset : u64 ,
32
33
/// Data to replace the existing record data
33
- data : Vec < u8 > ,
34
+ data : & ' a [ u8 ] ,
34
35
} ,
35
36
36
37
/// Update the authority of the provided record account
@@ -53,28 +54,80 @@ pub enum RecordInstruction {
53
54
CloseAccount ,
54
55
}
55
56
57
+ impl < ' a > RecordInstruction < ' a > {
58
+ /// Unpacks a byte buffer into a [RecordInstruction].
59
+ pub fn unpack ( input : & ' a [ u8 ] ) -> Result < Self , ProgramError > {
60
+ let ( & tag, rest) = input
61
+ . split_first ( )
62
+ . ok_or ( ProgramError :: InvalidInstructionData ) ?;
63
+ Ok ( match tag {
64
+ 0 => Self :: Initialize ,
65
+ 1 => {
66
+ const U32_BYTES : usize = 4 ;
67
+ const U64_BYTES : usize = 8 ;
68
+ let offset = rest
69
+ . get ( ..U64_BYTES )
70
+ . and_then ( |slice| slice. try_into ( ) . ok ( ) )
71
+ . map ( u64:: from_le_bytes)
72
+ . ok_or ( ProgramError :: InvalidInstructionData ) ?;
73
+ let ( length, data) = rest[ U64_BYTES ..] . split_at ( U32_BYTES ) ;
74
+ let length = u32:: from_le_bytes (
75
+ length
76
+ . try_into ( )
77
+ . map_err ( |_| ProgramError :: InvalidInstructionData ) ?,
78
+ ) as usize ;
79
+
80
+ Self :: Write {
81
+ offset,
82
+ data : & data[ ..length] ,
83
+ }
84
+ }
85
+ 2 => Self :: SetAuthority ,
86
+ 3 => Self :: CloseAccount ,
87
+ _ => return Err ( ProgramError :: InvalidInstructionData ) ,
88
+ } )
89
+ }
90
+
91
+ /// Packs a [RecordInstruction] into a byte buffer.
92
+ pub fn pack ( & self ) -> Vec < u8 > {
93
+ let mut buf = Vec :: with_capacity ( size_of :: < Self > ( ) ) ;
94
+ match self {
95
+ Self :: Initialize => buf. push ( 0 ) ,
96
+ Self :: Write { offset, data } => {
97
+ buf. push ( 1 ) ;
98
+ buf. extend_from_slice ( & offset. to_le_bytes ( ) ) ;
99
+ buf. extend_from_slice ( & ( data. len ( ) as u32 ) . to_le_bytes ( ) ) ;
100
+ buf. extend_from_slice ( data) ;
101
+ }
102
+ Self :: SetAuthority => buf. push ( 2 ) ,
103
+ Self :: CloseAccount => buf. push ( 3 ) ,
104
+ } ;
105
+ buf
106
+ }
107
+ }
108
+
56
109
/// Create a `RecordInstruction::Initialize` instruction
57
110
pub fn initialize ( record_account : & Pubkey , authority : & Pubkey ) -> Instruction {
58
- Instruction :: new_with_borsh (
59
- id ( ) ,
60
- & RecordInstruction :: Initialize ,
61
- vec ! [
111
+ Instruction {
112
+ program_id : id ( ) ,
113
+ accounts : vec ! [
62
114
AccountMeta :: new( * record_account, false ) ,
63
115
AccountMeta :: new_readonly( * authority, false ) ,
64
116
] ,
65
- )
117
+ data : RecordInstruction :: Initialize . pack ( ) ,
118
+ }
66
119
}
67
120
68
121
/// Create a `RecordInstruction::Write` instruction
69
- pub fn write ( record_account : & Pubkey , signer : & Pubkey , offset : u64 , data : Vec < u8 > ) -> Instruction {
70
- Instruction :: new_with_borsh (
71
- id ( ) ,
72
- & RecordInstruction :: Write { offset, data } ,
73
- vec ! [
122
+ pub fn write ( record_account : & Pubkey , signer : & Pubkey , offset : u64 , data : & [ u8 ] ) -> Instruction {
123
+ Instruction {
124
+ program_id : id ( ) ,
125
+ accounts : vec ! [
74
126
AccountMeta :: new( * record_account, false ) ,
75
127
AccountMeta :: new_readonly( * signer, true ) ,
76
128
] ,
77
- )
129
+ data : RecordInstruction :: Write { offset, data } . pack ( ) ,
130
+ }
78
131
}
79
132
80
133
/// Create a `RecordInstruction::SetAuthority` instruction
@@ -83,92 +136,79 @@ pub fn set_authority(
83
136
signer : & Pubkey ,
84
137
new_authority : & Pubkey ,
85
138
) -> Instruction {
86
- Instruction :: new_with_borsh (
87
- id ( ) ,
88
- & RecordInstruction :: SetAuthority ,
89
- vec ! [
139
+ Instruction {
140
+ program_id : id ( ) ,
141
+ accounts : vec ! [
90
142
AccountMeta :: new( * record_account, false ) ,
91
143
AccountMeta :: new_readonly( * signer, true ) ,
92
144
AccountMeta :: new_readonly( * new_authority, false ) ,
93
145
] ,
94
- )
146
+ data : RecordInstruction :: SetAuthority . pack ( ) ,
147
+ }
95
148
}
96
149
97
150
/// Create a `RecordInstruction::CloseAccount` instruction
98
151
pub fn close_account ( record_account : & Pubkey , signer : & Pubkey , receiver : & Pubkey ) -> Instruction {
99
- Instruction :: new_with_borsh (
100
- id ( ) ,
101
- & RecordInstruction :: CloseAccount ,
102
- vec ! [
152
+ Instruction {
153
+ program_id : id ( ) ,
154
+ accounts : vec ! [
103
155
AccountMeta :: new( * record_account, false ) ,
104
156
AccountMeta :: new_readonly( * signer, true ) ,
105
157
AccountMeta :: new( * receiver, false ) ,
106
158
] ,
107
- )
159
+ data : RecordInstruction :: CloseAccount . pack ( ) ,
160
+ }
108
161
}
109
162
110
163
#[ cfg( test) ]
111
164
mod tests {
112
- use { super :: * , crate :: state:: tests:: TEST_DATA , solana_program:: program_error:: ProgramError } ;
165
+ use {
166
+ super :: * , crate :: state:: tests:: TEST_DATA , solana_program:: program_error:: ProgramError ,
167
+ spl_pod:: bytemuck:: pod_bytes_of,
168
+ } ;
113
169
114
170
#[ test]
115
171
fn serialize_initialize ( ) {
116
172
let instruction = RecordInstruction :: Initialize ;
117
173
let expected = vec ! [ 0 ] ;
118
- assert_eq ! ( instruction. try_to_vec( ) . unwrap( ) , expected) ;
119
- assert_eq ! (
120
- RecordInstruction :: try_from_slice( & expected) . unwrap( ) ,
121
- instruction
122
- ) ;
174
+ assert_eq ! ( instruction. pack( ) , expected) ;
175
+ assert_eq ! ( RecordInstruction :: unpack( & expected) . unwrap( ) , instruction) ;
123
176
}
124
177
125
178
#[ test]
126
179
fn serialize_write ( ) {
127
- let data = TEST_DATA . try_to_vec ( ) . unwrap ( ) ;
180
+ let data = pod_bytes_of ( & TEST_DATA ) ;
128
181
let offset = 0u64 ;
129
- let instruction = RecordInstruction :: Write {
130
- offset : 0 ,
131
- data : data. clone ( ) ,
132
- } ;
182
+ let instruction = RecordInstruction :: Write { offset : 0 , data } ;
133
183
let mut expected = vec ! [ 1 ] ;
134
184
expected. extend_from_slice ( & offset. to_le_bytes ( ) ) ;
135
- expected. append ( & mut data. try_to_vec ( ) . unwrap ( ) ) ;
136
- assert_eq ! ( instruction. try_to_vec( ) . unwrap( ) , expected) ;
137
- assert_eq ! (
138
- RecordInstruction :: try_from_slice( & expected) . unwrap( ) ,
139
- instruction
140
- ) ;
185
+ expected. extend_from_slice ( & ( data. len ( ) as u32 ) . to_le_bytes ( ) ) ;
186
+ expected. extend_from_slice ( data) ;
187
+ assert_eq ! ( instruction. pack( ) , expected) ;
188
+ assert_eq ! ( RecordInstruction :: unpack( & expected) . unwrap( ) , instruction) ;
141
189
}
142
190
143
191
#[ test]
144
192
fn serialize_set_authority ( ) {
145
193
let instruction = RecordInstruction :: SetAuthority ;
146
194
let expected = vec ! [ 2 ] ;
147
- assert_eq ! ( instruction. try_to_vec( ) . unwrap( ) , expected) ;
148
- assert_eq ! (
149
- RecordInstruction :: try_from_slice( & expected) . unwrap( ) ,
150
- instruction
151
- ) ;
195
+ assert_eq ! ( instruction. pack( ) , expected) ;
196
+ assert_eq ! ( RecordInstruction :: unpack( & expected) . unwrap( ) , instruction) ;
152
197
}
153
198
154
199
#[ test]
155
200
fn serialize_close_account ( ) {
156
201
let instruction = RecordInstruction :: CloseAccount ;
157
202
let expected = vec ! [ 3 ] ;
158
- assert_eq ! ( instruction. try_to_vec( ) . unwrap( ) , expected) ;
159
- assert_eq ! (
160
- RecordInstruction :: try_from_slice( & expected) . unwrap( ) ,
161
- instruction
162
- ) ;
203
+ assert_eq ! ( instruction. pack( ) , expected) ;
204
+ assert_eq ! ( RecordInstruction :: unpack( & expected) . unwrap( ) , instruction) ;
163
205
}
164
206
165
207
#[ test]
166
208
fn deserialize_invalid_instruction ( ) {
167
209
let mut expected = vec ! [ 12 ] ;
168
- expected. append ( & mut TEST_DATA . try_to_vec ( ) . unwrap ( ) ) ;
169
- let err: ProgramError = RecordInstruction :: try_from_slice ( & expected)
170
- . unwrap_err ( )
171
- . into ( ) ;
172
- assert ! ( matches!( err, ProgramError :: BorshIoError ( _) ) ) ;
210
+ expected. append ( & mut pod_bytes_of ( & TEST_DATA ) . to_vec ( ) ) ;
211
+ let err: ProgramError = RecordInstruction :: unpack ( & expected) . unwrap_err ( ) ;
212
+ assert_eq ! ( err, ProgramError :: InvalidInstructionData ) ;
173
213
}
174
214
}
0 commit comments