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