github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/app/types/account.go (about) 1 package types 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 8 "github.com/tendermint/go-amino" 9 "gopkg.in/yaml.v2" 10 11 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 12 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 13 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth" 14 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/exported" 15 authtypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth/types" 16 17 ethcmn "github.com/ethereum/go-ethereum/common" 18 "github.com/ethereum/go-ethereum/crypto" 19 ethcrypto "github.com/ethereum/go-ethereum/crypto" 20 ) 21 22 var _ exported.Account = (*EthAccount)(nil) 23 var _ exported.GenesisAccount = (*EthAccount)(nil) 24 var emptyCodeHash = crypto.Keccak256(nil) 25 26 func init() { 27 authtypes.RegisterAccountTypeCodec(&EthAccount{}, EthAccountName) 28 } 29 30 // ---------------------------------------------------------------------------- 31 // Main FBchain account 32 // ---------------------------------------------------------------------------- 33 34 // EthAccount implements the auth.Account interface and embeds an 35 // auth.BaseAccount type. It is compatible with the auth.AccountKeeper. 36 type EthAccount struct { 37 *authtypes.BaseAccount `json:"base_account" yaml:"base_account"` 38 CodeHash []byte `json:"code_hash" yaml:"code_hash"` 39 } 40 41 func (acc *EthAccount) UnmarshalFromAmino(cdc *amino.Codec, data []byte) error { 42 var dataLen uint64 = 0 43 var baseAccountFlag bool 44 45 for { 46 data = data[dataLen:] 47 48 if len(data) <= 0 { 49 break 50 } 51 52 pos, pbType, err := amino.ParseProtoPosAndTypeMustOneByte(data[0]) 53 if err != nil { 54 return err 55 } 56 // all EthAccount fields are (2) 57 if pbType != amino.Typ3_ByteLength { 58 return fmt.Errorf("invalid pbType: %v", pbType) 59 } 60 data = data[1:] 61 62 var n int 63 dataLen, n, err = amino.DecodeUvarint(data) 64 if err != nil { 65 return err 66 } 67 68 data = data[n:] 69 if len(data) < int(dataLen) { 70 return fmt.Errorf("not enough data for field %d", pos) 71 } 72 subData := data[:dataLen] 73 74 switch pos { 75 case 1: 76 baseAccountFlag = true 77 if acc.BaseAccount == nil { 78 acc.BaseAccount = &auth.BaseAccount{} 79 } else { 80 *acc.BaseAccount = auth.BaseAccount{} 81 } 82 err = acc.BaseAccount.UnmarshalFromAmino(cdc, subData) 83 if err != nil { 84 return err 85 } 86 case 2: 87 acc.CodeHash = make([]byte, len(subData)) 88 copy(acc.CodeHash, subData) 89 default: 90 return fmt.Errorf("unexpect feild num %d", pos) 91 } 92 } 93 if !baseAccountFlag { 94 acc.BaseAccount = nil 95 } 96 return nil 97 } 98 99 type componentAccount struct { 100 ethAccount EthAccount 101 baseAccount authtypes.BaseAccount 102 } 103 104 func (acc EthAccount) Copy() sdk.Account { 105 // we need only allocate one object on the heap with componentAccount 106 var cacc componentAccount 107 108 cacc.baseAccount.Address = acc.Address 109 cacc.baseAccount.Coins = acc.Coins 110 cacc.baseAccount.PubKey = acc.PubKey 111 cacc.baseAccount.AccountNumber = acc.AccountNumber 112 cacc.baseAccount.Sequence = acc.Sequence 113 114 cacc.ethAccount.BaseAccount = &cacc.baseAccount 115 cacc.ethAccount.CodeHash = acc.CodeHash 116 117 return &cacc.ethAccount 118 } 119 120 func (acc EthAccount) AminoSize(cdc *amino.Codec) int { 121 size := 0 122 if acc.BaseAccount != nil { 123 baccSize := acc.BaseAccount.AminoSize(cdc) 124 size += 1 + amino.UvarintSize(uint64(baccSize)) + baccSize 125 } 126 if len(acc.CodeHash) != 0 { 127 size += 1 + amino.ByteSliceSize(acc.CodeHash) 128 } 129 return size 130 } 131 132 func (acc EthAccount) MarshalToAmino(cdc *amino.Codec) ([]byte, error) { 133 var buf bytes.Buffer 134 buf.Grow(acc.AminoSize(cdc)) 135 err := acc.MarshalAminoTo(cdc, &buf) 136 if err != nil { 137 return nil, err 138 } 139 return buf.Bytes(), nil 140 } 141 142 func (acc EthAccount) MarshalAminoTo(cdc *amino.Codec, buf *bytes.Buffer) error { 143 // field 1 144 if acc.BaseAccount != nil { 145 const pbKey = 1<<3 | 2 146 buf.WriteByte(pbKey) 147 baccSize := acc.BaseAccount.AminoSize(cdc) 148 err := amino.EncodeUvarintToBuffer(buf, uint64(baccSize)) 149 if err != nil { 150 return err 151 } 152 lenBeforeData := buf.Len() 153 err = acc.BaseAccount.MarshalAminoTo(cdc, buf) 154 if err != nil { 155 return err 156 } 157 if buf.Len()-lenBeforeData != baccSize { 158 return amino.NewSizerError(acc.BaseAccount, baccSize, buf.Len()-lenBeforeData) 159 } 160 } 161 162 // field 2 163 if len(acc.CodeHash) != 0 { 164 const pbKey = 2<<3 | 2 165 err := amino.EncodeByteSliceWithKeyToBuffer(buf, acc.CodeHash, pbKey) 166 if err != nil { 167 return err 168 } 169 } 170 171 return nil 172 } 173 174 // ProtoAccount defines the prototype function for BaseAccount used for an 175 // AccountKeeper. 176 func ProtoAccount() exported.Account { 177 return &EthAccount{ 178 BaseAccount: &auth.BaseAccount{}, 179 CodeHash: ethcrypto.Keccak256(nil), 180 } 181 } 182 183 // EthAddress returns the account address ethereum format. 184 func (acc EthAccount) EthAddress() ethcmn.Address { 185 return ethcmn.BytesToAddress(acc.Address.Bytes()) 186 } 187 188 // TODO: remove on SDK v0.40 189 190 // Balance returns the balance of an account. 191 func (acc EthAccount) Balance(denom string) sdk.Dec { 192 return acc.GetCoins().AmountOf(denom) 193 } 194 195 // SetBalance sets an account's balance of the given coin denomination. 196 // 197 // CONTRACT: assumes the denomination is valid. 198 func (acc *EthAccount) SetBalance(denom string, amt sdk.Dec) { 199 coins := acc.GetCoins() 200 diff := amt.Sub(coins.AmountOf(denom)) 201 switch { 202 case diff.IsPositive(): 203 // Increase coins to amount 204 coins = coins.Add(sdk.NewCoin(denom, diff)) 205 case diff.IsNegative(): 206 // Decrease coins to amount 207 coins = coins.Sub(sdk.NewCoins(sdk.NewCoin(denom, diff.Neg()))) 208 default: 209 return 210 } 211 212 if err := acc.SetCoins(coins); err != nil { 213 panic(fmt.Errorf("could not set %s coins for address %s: %w", denom, acc.EthAddress().String(), err)) 214 } 215 } 216 217 type ethermintAccountPretty struct { 218 Address sdk.AccAddress `json:"address" yaml:"address"` 219 EthAddress string `json:"eth_address" yaml:"eth_address"` 220 Coins sdk.Coins `json:"coins" yaml:"coins"` 221 PubKey string `json:"public_key" yaml:"public_key"` 222 AccountNumber uint64 `json:"account_number" yaml:"account_number"` 223 Sequence uint64 `json:"sequence" yaml:"sequence"` 224 CodeHash string `json:"code_hash" yaml:"code_hash"` 225 } 226 227 // MarshalYAML returns the YAML representation of an account. 228 func (acc EthAccount) MarshalYAML() (interface{}, error) { 229 ethAddress := "" 230 if !sdk.IsWasmAddress(acc.Address) { 231 ethAddress = acc.EthAddress().String() 232 } 233 alias := ethermintAccountPretty{ 234 Address: acc.Address, 235 EthAddress: ethAddress, 236 Coins: acc.Coins, 237 AccountNumber: acc.AccountNumber, 238 Sequence: acc.Sequence, 239 CodeHash: ethcmn.Bytes2Hex(acc.CodeHash), 240 } 241 242 var err error 243 244 if acc.PubKey != nil { 245 alias.PubKey, err = sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, acc.PubKey) 246 if err != nil { 247 return nil, err 248 } 249 } 250 251 bz, err := yaml.Marshal(alias) 252 if err != nil { 253 return nil, err 254 } 255 256 return string(bz), err 257 } 258 259 // MarshalJSON returns the JSON representation of an EthAccount. 260 func (acc EthAccount) MarshalJSON() ([]byte, error) { 261 var ethAddress = "" 262 263 if acc.BaseAccount != nil && acc.Address != nil { 264 if !sdk.IsWasmAddress(acc.Address) { 265 ethAddress = acc.EthAddress().String() 266 } 267 } 268 269 alias := ethermintAccountPretty{ 270 Address: acc.Address, 271 EthAddress: ethAddress, 272 Coins: acc.Coins, 273 AccountNumber: acc.AccountNumber, 274 Sequence: acc.Sequence, 275 CodeHash: ethcmn.Bytes2Hex(acc.CodeHash), 276 } 277 278 var err error 279 280 if acc.PubKey != nil { 281 alias.PubKey, err = sdk.Bech32ifyPubKey(sdk.Bech32PubKeyTypeAccPub, acc.PubKey) 282 if err != nil { 283 return nil, err 284 } 285 } 286 287 return json.Marshal(alias) 288 } 289 290 // UnmarshalJSON unmarshals raw JSON bytes into an EthAccount. 291 func (acc *EthAccount) UnmarshalJSON(bz []byte) error { 292 var ( 293 alias ethermintAccountPretty 294 err error 295 ) 296 297 if err := json.Unmarshal(bz, &alias); err != nil { 298 return err 299 } 300 301 switch { 302 case !alias.Address.Empty() && alias.EthAddress != "": 303 // Both addresses provided. Verify correctness 304 ethAddress := ethcmn.HexToAddress(alias.EthAddress) 305 ethAddressFromAccAddress := ethcmn.BytesToAddress(alias.Address.Bytes()) 306 307 if !bytes.Equal(ethAddress.Bytes(), alias.Address.Bytes()) { 308 err = sdkerrors.Wrapf( 309 sdkerrors.ErrInvalidAddress, 310 "expected %s, got %s", 311 ethAddressFromAccAddress.String(), ethAddress.String(), 312 ) 313 } 314 315 case !alias.Address.Empty() && alias.EthAddress == "": 316 // unmarshal sdk.AccAddress only. Do nothing here 317 case alias.Address.Empty() && alias.EthAddress != "": 318 // retrieve sdk.AccAddress from ethereum address 319 ethAddress := ethcmn.HexToAddress(alias.EthAddress) 320 alias.Address = sdk.AccAddress(ethAddress.Bytes()) 321 case alias.Address.Empty() && alias.EthAddress == "": 322 err = sdkerrors.Wrapf( 323 sdkerrors.ErrInvalidAddress, 324 "account must contain address in Ethereum Hex or Cosmos Bech32 format", 325 ) 326 } 327 328 if err != nil { 329 return err 330 } 331 332 acc.BaseAccount = &authtypes.BaseAccount{ 333 Coins: alias.Coins, 334 Address: alias.Address, 335 AccountNumber: alias.AccountNumber, 336 Sequence: alias.Sequence, 337 } 338 acc.CodeHash = ethcmn.Hex2Bytes(alias.CodeHash) 339 340 if alias.PubKey != "" { 341 acc.BaseAccount.PubKey, err = sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeAccPub, alias.PubKey) 342 if err != nil { 343 return err 344 } 345 } 346 return nil 347 } 348 349 // String implements the fmt.Stringer interface 350 func (acc EthAccount) String() string { 351 out, _ := yaml.Marshal(acc) 352 return string(out) 353 } 354 355 // IsContract returns if the account contains contract code. 356 func (acc EthAccount) IsContract() bool { 357 return !bytes.Equal(acc.CodeHash, emptyCodeHash) 358 }