-
Notifications
You must be signed in to change notification settings - Fork 0
/
tx_reserve_fund.go
134 lines (108 loc) · 4.25 KB
/
tx_reserve_fund.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
package execution
import (
"fmt"
"math/big"
"github.com/dnerochain/dnero/common"
"github.com/dnerochain/dnero/common/result"
"github.com/dnerochain/dnero/core"
st "github.com/dnerochain/dnero/ledger/state"
"github.com/dnerochain/dnero/ledger/types"
)
var _ TxExecutor = (*ReserveFundTxExecutor)(nil)
// ------------------------------- ReserveFundTx Transaction -----------------------------------
// ReserveFundTxExecutor implements the TxExecutor interface
type ReserveFundTxExecutor struct {
state *st.LedgerState
}
// NewReserveFundTxExecutor creates a new instance of ReserveFundTxExecutor
func NewReserveFundTxExecutor(state *st.LedgerState) *ReserveFundTxExecutor {
return &ReserveFundTxExecutor{
state: state,
}
}
func (exec *ReserveFundTxExecutor) sanityCheck(chainID string, view *st.StoreView, transaction types.Tx) result.Result {
tx := transaction.(*types.ReserveFundTx)
// Validate source, basic
res := tx.Source.ValidateBasic()
if res.IsError() {
return res
}
// Get input account
sourceAccount, success := getInput(view, tx.Source)
if success.IsError() {
return result.Error("Failed to get the source account: %v", tx.Source.Address)
}
// Validate input, advanced
signBytes := tx.SignBytes(chainID)
res = validateInputAdvanced(sourceAccount, signBytes, tx.Source)
if res.IsError() {
logger.Debugf(fmt.Sprintf("validateSourceAdvanced failed on %v: %v", tx.Source.Address.Hex(), res))
return res
}
coins := tx.Source.Coins.NoNil()
if !coins.IsPositive() {
return result.Error("Amount of reserved fund not specified").
WithErrorCode(result.CodeReservedFundNotSpecified)
}
if coins.DneroWei.Cmp(types.Zero) != 0 {
return result.Error("Cannot reserve Dnero as service fund!").
WithErrorCode(result.CodeInvalidFundToReserve)
}
blockHeight := view.Height() + 1 // the view points to the parent of the current block
if minTxFee, success := sanityCheckForFee(tx.Fee, blockHeight); !success {
return result.Error("Insufficient fee. Transaction fee needs to be at least %v DTokenWei",
minTxFee).WithErrorCode(result.CodeInvalidFee)
}
fund := tx.Source.Coins
collateral := tx.Collateral
duration := tx.Duration
reserveSequence := tx.Source.Sequence
minimalBalance := fund.Plus(collateral).Plus(tx.Fee)
if !sourceAccount.Balance.IsGTE(minimalBalance) {
logger.Infof(fmt.Sprintf("ReserveFund: Source did not have enough balance %v", tx.Source.Address.Hex()))
return result.Error("Insufficient fund: Source balance is %v, but required minimal balance is %v",
sourceAccount.Balance, minimalBalance).WithErrorCode(result.CodeInsufficientFund)
}
err := sourceAccount.CheckReserveFund(collateral, fund, duration, reserveSequence)
if err != nil {
return result.Error(err.Error()).WithErrorCode(result.CodeReserveFundCheckFailed)
}
return result.OK
}
func (exec *ReserveFundTxExecutor) process(chainID string, view *st.StoreView, transaction types.Tx) (common.Hash, result.Result) {
tx := transaction.(*types.ReserveFundTx)
sourceAddress := tx.Source.Address
sourceAccount, success := getInput(view, tx.Source)
if success.IsError() {
return common.Hash{}, result.Error("Failed to get the source account")
}
collateral := tx.Collateral
fund := tx.Source.Coins
resourceIDs := tx.ResourceIDs
duration := tx.Duration
reserveSequence := tx.Source.Sequence
endBlockHeight := exec.state.Height() + duration
sourceAccount.ReserveFund(collateral, fund, resourceIDs, endBlockHeight, reserveSequence)
if !chargeFee(sourceAccount, tx.Fee) {
return common.Hash{}, result.Error("failed to charge transaction fee")
}
sourceAccount.Sequence++
view.SetAccount(sourceAddress, sourceAccount)
txHash := types.TxID(chainID, tx)
return txHash, result.OK
}
func (exec *ReserveFundTxExecutor) getTxInfo(transaction types.Tx) *core.TxInfo {
tx := transaction.(*types.ReserveFundTx)
return &core.TxInfo{
Address: tx.Source.Address,
Sequence: tx.Source.Sequence,
EffectiveGasPrice: exec.calculateEffectiveGasPrice(transaction),
}
}
func (exec *ReserveFundTxExecutor) calculateEffectiveGasPrice(transaction types.Tx) *big.Int {
tx := transaction.(*types.ReserveFundTx)
fee := tx.Fee
gas := new(big.Int).SetUint64(getRegularTxGas(exec.state))
effectiveGasPrice := new(big.Int).Div(fee.DTokenWei, gas)
return effectiveGasPrice
}