forked from iotexproject/iotex-core
/
createdeposit.go
141 lines (129 loc) · 4.27 KB
/
createdeposit.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
// Copyright (c) 2018 IoTeX
// This is an alpha (internal) release and is not suitable for production. This source code is provided 'as is' and no
// warranties are given as to title or non-infringement, merchantability or fitness for purpose and, to the extent
// permitted by law, all liability for your use of the code is disclaimed. This source code is governed by Apache
// License 2.0 that can be found in the LICENSE file.
package mainchain
import (
"context"
"math/big"
"github.com/pkg/errors"
"github.com/iotexproject/iotex-address/address"
"github.com/iotexproject/iotex-core/action"
"github.com/iotexproject/iotex-core/action/protocol"
accountutil "github.com/iotexproject/iotex-core/action/protocol/account/util"
"github.com/iotexproject/iotex-core/pkg/enc"
"github.com/iotexproject/iotex-core/pkg/hash"
"github.com/iotexproject/iotex-core/state"
)
// DepositAddress returns the deposit address (20-byte)
func DepositAddress(subChainAddr []byte, depositIndex uint64) hash.Hash160 {
var stream []byte
stream = append(stream, subChainAddr...)
stream = append(stream, []byte(".deposit.")...)
temp := make([]byte, 8)
enc.MachineEndian.PutUint64(temp, depositIndex)
stream = append(stream, temp...)
return hash.Hash160b(stream)
}
// Deposit returns the deposit record
func (p *Protocol) Deposit(subChainAddr address.Address, depositIndex uint64) (*Deposit, error) {
key := DepositAddress(subChainAddr.Bytes(), depositIndex)
var deposit Deposit
if err := p.sf.State(key, &deposit); err != nil {
return nil, errors.Wrapf(err, "error when loading state of %x", key)
}
return &deposit, nil
}
func (p *Protocol) handleDeposit(
ctx context.Context,
deposit *action.CreateDeposit,
sm protocol.StateManager,
) (*action.Receipt, error) {
raCtx := protocol.MustGetRunActionsCtx(ctx)
account, subChainInOp, err := p.validateDeposit(raCtx.Caller, deposit, sm)
if err != nil {
return nil, err
}
return p.mutateDeposit(raCtx.Caller, raCtx.BlockHeight, deposit, account, subChainInOp, sm)
}
func (p *Protocol) validateDeposit(
caller address.Address,
deposit *action.CreateDeposit,
sm protocol.StateManager,
) (*state.Account, InOperation, error) {
cost, err := deposit.Cost()
if err != nil {
return nil, InOperation{}, errors.Wrap(err, "error when getting deposit's cost")
}
account, err := p.accountWithEnoughBalance(caller.String(), cost, sm)
if err != nil {
return nil, InOperation{}, err
}
subChainsInOp, err := p.subChainsInOperation(sm)
if err != nil {
return nil, InOperation{}, err
}
inOp, ok := subChainsInOp.Get(deposit.ChainID())
if !ok {
return nil, InOperation{}, errors.Errorf("address %s is not on a sub-chain in operation", deposit.Recipient())
}
return account, inOp, nil
}
func (p *Protocol) mutateDeposit(
caller address.Address,
blkHeight uint64,
deposit *action.CreateDeposit,
acct *state.Account,
subChainInOp InOperation,
sm protocol.StateManager,
) (*action.Receipt, error) {
// Subtract the balance from sender account
acct.Balance = big.NewInt(0).Sub(acct.Balance, deposit.Amount())
// TODO: this is not right, but currently the actions in a block is not processed according to the nonce
accountutil.SetNonce(deposit, acct)
if err := accountutil.StoreAccount(sm, caller.String(), acct); err != nil {
return nil, err
}
// Update sub-chain state
addr, err := address.FromBytes(subChainInOp.Addr)
if err != nil {
return nil, err
}
subChain, err := p.SubChain(addr)
if err != nil {
return nil, errors.Wrapf(err, "error when getting the state of sub-chain %d", subChain.ChainID)
}
depositIndex := subChain.DepositCount
subChain.DepositCount++
if err := sm.PutState(hash.BytesToHash160(addr.Bytes()), subChain); err != nil {
return nil, err
}
// Insert deposit state
recipient, err := address.FromString(deposit.Recipient())
if err != nil {
return nil, err
}
if err := sm.PutState(
DepositAddress(subChainInOp.Addr, depositIndex),
&Deposit{
Amount: deposit.Amount(),
Addr: recipient.Bytes(),
Confirmed: false,
},
); err != nil {
return nil, err
}
gas, err := deposit.IntrinsicGas()
if err != nil {
return nil, err
}
receipt := action.Receipt{
Status: 0,
BlockHeight: blkHeight,
ActionHash: deposit.Hash(),
GasConsumed: gas,
ContractAddress: addr.String(),
}
return &receipt, nil
}