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  }