github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/execution/engine/account.go (about) 1 package engine 2 3 import ( 4 "bytes" 5 "math/big" 6 7 "github.com/hyperledger/burrow/acm" 8 "github.com/hyperledger/burrow/acm/acmstate" 9 "github.com/hyperledger/burrow/crypto" 10 "github.com/hyperledger/burrow/deploy/compile" 11 "github.com/hyperledger/burrow/execution/errors" 12 "github.com/hyperledger/burrow/txs/payload" 13 "golang.org/x/crypto/sha3" 14 ) 15 16 func InitEVMCode(st acmstate.ReaderWriter, address crypto.Address, code []byte) error { 17 return initEVMCode(st, address, nil, code) 18 } 19 20 func InitChildCode(st acmstate.ReaderWriter, address crypto.Address, parent crypto.Address, code []byte) error { 21 return initEVMCode(st, address, &parent, code) 22 } 23 24 func initEVMCode(st acmstate.ReaderWriter, address crypto.Address, parent *crypto.Address, code []byte) error { 25 acc, err := MustAccount(st, address) 26 if err != nil { 27 return err 28 } 29 if acc.EVMCode != nil || acc.WASMCode != nil { 30 return errors.Errorf(errors.Codes.IllegalWrite, 31 "tried to initialise code for a contract that already has code: %v", address) 32 } 33 34 acc.EVMCode = code 35 36 // keccak256 hash of a contract's code 37 hash := sha3.NewLegacyKeccak256() 38 hash.Write(code) 39 codehash := hash.Sum(nil) 40 41 forebear := &address 42 metamap := acc.ContractMeta 43 if parent != nil { 44 // find our ancestor, i.e. the initial contract that was deployed, from which this contract descends 45 ancestor, err := st.GetAccount(*parent) 46 if err != nil { 47 return err 48 } 49 if ancestor == nil { 50 return errors.Errorf(errors.Codes.NonExistentAccount, 51 "parent %v of account %v does not exist", *parent, address) 52 } 53 if ancestor.Forebear != nil { 54 ancestor, err = st.GetAccount(*ancestor.Forebear) 55 if err != nil { 56 return err 57 } 58 if ancestor == nil { 59 return errors.Errorf(errors.Codes.NonExistentAccount, 60 "forebear %v of account %v does not exist", *ancestor.Forebear, *parent) 61 } 62 forebear = ancestor.Forebear 63 } else { 64 forebear = parent 65 } 66 metamap = ancestor.ContractMeta 67 } 68 69 // If we have a list of ABIs for this contract, we also know what contract code it is allowed to create 70 // For compatibility with older contracts, allow any contract to be created if we have no mappings 71 if metamap != nil && len(metamap) > 0 { 72 found := codehashPermitted(codehash, metamap) 73 74 // Libraries lie about their deployed bytecode 75 if !found { 76 deployCodehash := compile.GetDeployCodeHash(code, address) 77 found = codehashPermitted(deployCodehash, metamap) 78 } 79 80 if !found { 81 return errors.Errorf(errors.Codes.InvalidContractCode, 82 "could not find code with code hash: %X", codehash) 83 } 84 } 85 86 acc.CodeHash = codehash 87 acc.Forebear = forebear 88 89 return st.UpdateAccount(acc) 90 } 91 92 func codehashPermitted(codehash []byte, metamap []*acm.ContractMeta) bool { 93 for _, m := range metamap { 94 if bytes.Equal(codehash, m.CodeHash) { 95 return true 96 } 97 } 98 99 return false 100 } 101 102 func InitWASMCode(st acmstate.ReaderWriter, address crypto.Address, code []byte) error { 103 acc, err := MustAccount(st, address) 104 if err != nil { 105 return err 106 } 107 if acc.EVMCode != nil || acc.WASMCode != nil { 108 return errors.Errorf(errors.Codes.IllegalWrite, 109 "tried to re-initialise code for contract %v", address) 110 } 111 112 acc.WASMCode = code 113 // keccak256 hash of a contract's code 114 hash := sha3.NewLegacyKeccak256() 115 hash.Write(code) 116 acc.CodeHash = hash.Sum(nil) 117 return st.UpdateAccount(acc) 118 } 119 120 // TODO: consider pushing big.Int usage all the way to account balance 121 func Transfer(st acmstate.ReaderWriter, from, to crypto.Address, amount *big.Int) error { 122 if !amount.IsInt64() { 123 return errors.Errorf(errors.Codes.IntegerOverflow, "transfer amount %v overflows int64", amount) 124 } 125 if amount.Sign() == 0 { 126 return nil 127 } 128 acc, err := MustAccount(st, from) 129 if err != nil { 130 return err 131 } 132 if new(big.Int).SetUint64(acc.Balance).Cmp(amount) < 0 { 133 return errors.Codes.InsufficientBalance 134 } 135 err = UpdateAccount(st, from, func(account *acm.Account) error { 136 return account.SubtractFromBalance(amount.Uint64()) 137 }) 138 if err != nil { 139 return err 140 } 141 return UpdateAccount(st, to, func(account *acm.Account) error { 142 return account.AddToBalance(amount.Uint64()) 143 }) 144 } 145 146 func UpdateContractMeta(st acmstate.ReaderWriter, metaSt acmstate.MetadataWriter, address crypto.Address, payloadMeta []*payload.ContractMeta) error { 147 if len(payloadMeta) == 0 { 148 return nil 149 } 150 acc, err := MustAccount(st, address) 151 if err != nil { 152 return err 153 } 154 155 contractMeta := make([]*acm.ContractMeta, len(payloadMeta)) 156 for i, abi := range payloadMeta { 157 metahash := acmstate.GetMetadataHash(abi.Meta) 158 contractMeta[i] = &acm.ContractMeta{ 159 MetadataHash: metahash[:], 160 CodeHash: abi.CodeHash, 161 } 162 err = metaSt.SetMetadata(metahash, abi.Meta) 163 if err != nil { 164 return errors.Errorf(errors.Codes.IllegalWrite, 165 "cannot update metadata for %v: %v", address, err) 166 } 167 } 168 acc.ContractMeta = contractMeta 169 return st.UpdateAccount(acc) 170 } 171 172 func RemoveAccount(st acmstate.ReaderWriter, address crypto.Address) error { 173 acc, err := st.GetAccount(address) 174 if err != nil { 175 return err 176 } 177 if acc == nil { 178 return errors.Errorf(errors.Codes.DuplicateAddress, 179 "tried to remove an account at an address that does not exist: %v", address) 180 } 181 return st.RemoveAccount(address) 182 } 183 184 func UpdateAccount(st acmstate.ReaderWriter, address crypto.Address, updater func(acc *acm.Account) error) error { 185 acc, err := MustAccount(st, address) 186 if err != nil { 187 return err 188 } 189 err = updater(acc) 190 if err != nil { 191 return err 192 } 193 return st.UpdateAccount(acc) 194 } 195 196 func MustAccount(st acmstate.Reader, address crypto.Address) (*acm.Account, error) { 197 acc, err := st.GetAccount(address) 198 if err != nil { 199 return nil, err 200 } 201 if acc == nil { 202 return nil, errors.Errorf(errors.Codes.NonExistentAccount, 203 "account %v does not exist", address) 204 } 205 return acc, nil 206 }