github.com/klaytn/klaytn@v1.12.1/blockchain/types/account/account_serializer.go (about) 1 // Copyright 2019 The klaytn Authors 2 // This file is part of the klaytn library. 3 // 4 // The klaytn library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The klaytn library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>. 16 17 package account 18 19 import ( 20 "encoding/json" 21 "io" 22 23 "github.com/klaytn/klaytn/common/hexutil" 24 "github.com/klaytn/klaytn/rlp" 25 ) 26 27 // AccountSerializer serializes an Account object using RLP/JSON. 28 type AccountSerializer struct { 29 accType AccountType 30 account Account 31 // If true, ExtHash fields are preserved in RLP encoding. Otherwise, ExtHash fields are unextended. 32 preserveExtHash bool 33 } 34 35 // accountJSON is an internal data structure for JSON serialization. 36 type accountJSON struct { 37 AccType AccountType `json:"accType"` 38 Account json.RawMessage `json:"account"` 39 } 40 41 // NewAccountSerializer creates a new AccountSerializer object with default values. 42 // This returned object will be used for decoding. 43 func NewAccountSerializer() *AccountSerializer { 44 return &AccountSerializer{preserveExtHash: false} 45 } 46 47 // NewAccountSerializerWithAccount creates a new AccountSerializer object with the given account. 48 func NewAccountSerializerWithAccount(a Account) *AccountSerializer { 49 return &AccountSerializer{a.Type(), a, false} 50 } 51 52 // NewAccountSerializer creates a new AccountSerializer object with default values. 53 // This returned object will be used for decoding. 54 func NewAccountSerializerExt() *AccountSerializer { 55 return &AccountSerializer{preserveExtHash: true} 56 } 57 58 // NewAccountSerializerWithAccount creates a new AccountSerializer object with the given account. 59 func NewAccountSerializerExtWithAccount(a Account) *AccountSerializer { 60 return &AccountSerializer{a.Type(), a, true} 61 } 62 63 func (ser *AccountSerializer) EncodeRLP(w io.Writer) error { 64 // If it is a LegacyAccount object, do not encode the account type. 65 if ser.accType == LegacyAccountType { 66 return rlp.Encode(w, ser.account.(*LegacyAccount)) 67 } 68 69 if err := rlp.Encode(w, ser.accType); err != nil { 70 return err 71 } 72 73 if ser.preserveExtHash { 74 if pa, ok := ser.account.(ProgramAccount); ok { 75 return pa.EncodeRLPExt(w) 76 } 77 } 78 return rlp.Encode(w, ser.account) 79 } 80 81 func (ser *AccountSerializer) GetAccount() Account { 82 return ser.account 83 } 84 85 func (ser *AccountSerializer) DecodeRLP(s *rlp.Stream) error { 86 if err := s.Decode(&ser.accType); err != nil { 87 // fallback to decoding a LegacyAccount object. 88 acc := newLegacyAccount() 89 if err := s.Decode(acc); err != nil { 90 return err 91 } 92 ser.accType = LegacyAccountType 93 ser.account = acc 94 return nil 95 } 96 97 var err error 98 ser.account, err = NewAccountWithType(ser.accType) 99 if err != nil { 100 return err 101 } 102 103 return s.Decode(ser.account) 104 } 105 106 func (ser *AccountSerializer) MarshalJSON() ([]byte, error) { 107 // if it is a legacyAccount object, do not marshal the account type. 108 if ser.accType == LegacyAccountType { 109 return json.Marshal(ser.account) 110 } 111 b, err := json.Marshal(ser.account) 112 if err != nil { 113 return nil, err 114 } 115 116 return json.Marshal(&accountJSON{ser.accType, b}) 117 } 118 119 func (ser *AccountSerializer) UnmarshalJSON(b []byte) error { 120 dec := &accountJSON{} 121 122 if err := json.Unmarshal(b, dec); err != nil { 123 return err 124 } 125 126 if len(dec.Account) == 0 { 127 // fallback to unmarshal a LegacyAccount object. 128 acc := newLegacyAccount() 129 if err := json.Unmarshal(b, acc); err != nil { 130 return err 131 } 132 ser.accType = LegacyAccountType 133 ser.account = acc 134 135 return nil 136 137 } 138 139 ser.accType = dec.AccType 140 141 var err error 142 ser.account, err = NewAccountWithType(ser.accType) 143 if err != nil { 144 return err 145 } 146 147 return json.Unmarshal(dec.Account, ser.account) 148 } 149 150 // UnextendSerializedAccount unextends ExtHash fields within an RLP-encoded account. 151 // If the supplied bytes is not an RLP-encoded account, or does not contain any ExtHash, 152 // then return the supplied bytes unchanged. 153 func UnextendSerializedAccount(b []byte) (result []byte) { 154 acc := safeDecodeRLP(b) 155 if acc == nil { 156 return b // not an account 157 } 158 159 pa := GetProgramAccount(acc) 160 if pa == nil { 161 return b // not a ProgramAccount 162 } 163 164 enc := NewAccountSerializerWithAccount(pa) 165 result, err := rlp.EncodeToBytes(enc) 166 if err != nil { 167 logger.Crit("failed to unextend account blob", "bytes", hexutil.Encode(b), "err", err) 168 } 169 return result 170 } 171 172 // Account RLP decoder that does not panic 173 func safeDecodeRLP(b []byte) Account { 174 if len(b) == 0 { 175 return nil // No type byte 176 } 177 178 var accType AccountType 179 if err := rlp.DecodeBytes(b[0:1], &accType); err != nil { 180 return nil // Invalid type byte 181 } 182 if accType == LegacyAccountType { 183 return nil // Legacy type unsupported 184 } 185 186 dec := NewAccountSerializer() 187 if err := rlp.DecodeBytes(b, dec); err != nil { 188 return nil // Cannot decode specific type 189 } 190 191 return dec.GetAccount() 192 }