Skip to content

Commit

Permalink
Fix #682: no fee for frozen account related operations (#743)
Browse files Browse the repository at this point in the history
No fee required for freezing unfreezing operation.
  • Loading branch information
kfangw authored Nov 20, 2018
1 parent 8465a03 commit 081a160
Show file tree
Hide file tree
Showing 18 changed files with 346 additions and 558 deletions.
31 changes: 19 additions & 12 deletions cmd/sebak/cmd/wallet/payment.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ func init() {
Run: func(c *cobra.Command, args []string) {
var err error
var amount common.Amount
var newBalance common.Amount
var sender keypair.KP
var receiver keypair.KP
var endpoint *common.Endpoint
Expand Down Expand Up @@ -102,23 +101,23 @@ func init() {

// Check that account's balance is enough before sending the transaction
{
newBalance, err = senderAccount.GetBalance().Sub(amount)
if err == nil {
newBalance, err = newBalance.Sub(common.BaseFee)
fee := common.BaseFee
if flagFreeze {
fee = common.FrozenFee
}

_, err = senderAccount.GetBalance().Sub(amount + fee)
if err != nil {
fmt.Printf("Attempting to draft %v GON (+ %v fees), but sender account only have %v GON\n",
amount, common.BaseFee, senderAccount.GetBalance())
amount, fee, senderAccount.GetBalance())
os.Exit(1)
}
}

// TODO: Validate that the account doesn't already exists
if flagFreeze {
tx = makeTransactionCreateAccount(sender, receiver, amount, senderAccount.SequenceID, sender.Address())
tx = makeTransactionCreateAccount(sender, receiver, amount, senderAccount.SequenceID, true)
} else if flagCreateAccount {
tx = makeTransactionCreateAccount(sender, receiver, amount, senderAccount.SequenceID, "")
tx = makeTransactionCreateAccount(sender, receiver, amount, senderAccount.SequenceID, false)
} else {
tx = makeTransactionPayment(sender, receiver, amount, senderAccount.SequenceID)
}
Expand Down Expand Up @@ -170,8 +169,16 @@ func init() {
/// Returns:
/// `sebak.Transaction` = The generated `Transaction` creating the account
///
func makeTransactionCreateAccount(kpSource keypair.KP, kpDest keypair.KP, amount common.Amount, seqid uint64, target string) transaction.Transaction {
opb := operation.NewCreateAccount(kpDest.Address(), amount, target)
func makeTransactionCreateAccount(kpSource keypair.KP, kpDest keypair.KP, amount common.Amount, seqid uint64, isFrozen bool) transaction.Transaction {
var opb operation.CreateAccount
var fee common.Amount
if isFrozen {
opb = operation.NewCreateAccount(kpDest.Address(), amount, kpSource.Address())
fee = common.FrozenFee
} else {
opb = operation.NewCreateAccount(kpDest.Address(), amount, "")
fee = common.BaseFee
}

op := operation.Operation{
H: operation.Header{
Expand All @@ -182,7 +189,7 @@ func makeTransactionCreateAccount(kpSource keypair.KP, kpDest keypair.KP, amount

txBody := transaction.Body{
Source: kpSource.Address(),
Fee: common.BaseFee,
Fee: fee,
SequenceID: seqid,
Operations: []operation.Operation{op},
}
Expand Down Expand Up @@ -226,7 +233,7 @@ func makeTransactionPayment(kpSource keypair.KP, kpDest keypair.KP, amount commo

txBody := transaction.Body{
Source: kpSource.Address(),
Fee: common.Amount(common.BaseFee),
Fee: common.BaseFee,
SequenceID: seqid,
Operations: []operation.Operation{op},
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/sebak/cmd/wallet/unfreezeRequest.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func init() {
os.Exit(1)
}

if senderAccount.Linked == "" {
if !senderAccount.IsFrozen() {
fmt.Printf("Account is not frozen account")
os.Exit(1)
}
Expand Down Expand Up @@ -130,7 +130,7 @@ func makeTransactionUnfreezingRequest(kpSource keypair.KP, seqid uint64) transac

txBody := transaction.Body{
Source: kpSource.Address(),
Fee: common.Amount(common.BaseFee),
Fee: common.BaseFee,
SequenceID: seqid,
Operations: []operation.Operation{op},
}
Expand Down
4 changes: 4 additions & 0 deletions lib/block/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ func NewBlockAccountLinked(address string, balance common.Amount, linked string)
}
}

func (b *BlockAccount) IsFrozen() bool {
return b.Linked != ""
}

func (b *BlockAccount) String() string {
return string(common.MustJSONMarshal(b))
}
Expand Down
3 changes: 3 additions & 0 deletions lib/common/constant.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ const (
// is `0.1` BOS.
BaseReserve Amount = 1000000

// FrozenFee is a special transaction fee about freezing, and unfreezing.
FrozenFee Amount = 0

// GenesisBlockHeight set the block height of genesis block
GenesisBlockHeight uint64 = 1

Expand Down
74 changes: 34 additions & 40 deletions lib/node/runner/checker_ballot_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,35 +267,42 @@ func ValidateTx(st *storage.LevelDBBackend, config common.Config, tx transaction
// tx = Transaction to check
//
func ValidateOp(st *storage.LevelDBBackend, config common.Config, source *block.BlockAccount, op operation.Operation) (err error) {

var funcIsFrozenPayable = func(source *block.BlockAccount) (err error) {
// Unfreezing must be done after X period from unfreezing request
iterFunc, closeFunc := block.GetBlockOperationsBySource(st, source.Address, nil)
bo, _, _ := iterFunc() //Get the first operation submitted by the source(frozen) account
closeFunc()
// Before unfreezing payment, unfreezing request shoud be saved
if bo.Type != operation.TypeUnfreezingRequest {
return errors.UnfreezingRequestNotRequested
}
lastblock := block.GetLatestBlock(st)
// unfreezing period is 241920.
if lastblock.Height-bo.Height < common.UnfreezingPeriod {
return errors.UnfreezingNotReachedExpiration
}
return nil
}

switch op.H.Type {
case operation.TypeCreateAccount:
var ok bool
var casted operation.CreateAccount
if casted, ok = op.B.(operation.CreateAccount); !ok {
return errors.TypeOperationBodyNotMatched
}
if exists, err := block.ExistsBlockAccount(st, op.B.(operation.CreateAccount).Target); err == nil && exists {

if exists, err := block.ExistsBlockAccount(st, casted.Target); err == nil && exists {
return errors.BlockAccountAlreadyExists
}
if source.Linked != "" {
// Unfreezing must be done after X period from unfreezing request
iterFunc, closeFunc := block.GetBlockOperationsBySource(st, source.Address, nil)
bo, _, _ := iterFunc()
closeFunc()
// Before unfreezing payment, unfreezing request shoud be saved
if bo.Type != operation.TypeUnfreezingRequest {
return errors.UnfreezingRequestNotRequested
}
lastblock := block.GetLatestBlock(st)
// unfreezing period is 241920.
if lastblock.Height-bo.Height < common.UnfreezingPeriod {
return errors.UnfreezingNotReachedExpiration
}
// If it's a frozen account we check that only whole units are frozen
if casted.Linked != "" && (casted.Amount%common.Unit) != 0 {
return errors.FrozenAccountCreationWholeUnit // FIXME

if source.IsFrozen() {
if err = funcIsFrozenPayable(source); err != nil {
return err
}
}

case operation.TypePayment:
var ok bool
var casted operation.Payment
Expand All @@ -307,37 +314,24 @@ func ValidateOp(st *storage.LevelDBBackend, config common.Config, source *block.
if taccount, err = block.GetBlockAccount(st, casted.Target); err != nil {
return errors.BlockAccountDoesNotExists
}

// If it's a frozen account, it cannot receive payment
if taccount.Linked != "" {
if taccount.IsFrozen() {
return errors.FrozenAccountNoDeposit
}
if source.Linked != "" {
// If it's a frozen account, everything must be withdrawn
var expected common.Amount
expected, err = source.Balance.Sub(common.BaseFee)
if err != nil || casted.Amount != expected {
return errors.FrozenAccountMustWithdrawEverything
}
// Unfreezing must be done after X period from unfreezing request
iterFunc, closeFunc := block.GetBlockOperationsBySource(st, source.Address, nil)
bo, _, _ := iterFunc()
closeFunc()
// Before unfreezing payment, unfreezing request shoud be saved
if bo.Type != operation.TypeUnfreezingRequest {
return errors.UnfreezingRequestNotRequested
}
lastblock := block.GetLatestBlock(st)
// unfreezing period is 241920.
if lastblock.Height-bo.Height < common.UnfreezingPeriod {
return errors.UnfreezingNotReachedExpiration

// The source account is frozen account
if source.IsFrozen() {
if err = funcIsFrozenPayable(source); err != nil {
return err
}
}
case operation.TypeUnfreezingRequest:
if _, ok := op.B.(operation.UnfreezeRequest); !ok {
return errors.TypeOperationBodyNotMatched
}
// Unfreezing should be done from a frozen account
if source.Linked == "" {
if !source.IsFrozen() {
return errors.UnfreezingFromInvalidAccount
}
// Repeated unfreeze request shoud be blocked after unfreeze request saved
Expand All @@ -358,7 +352,7 @@ func ValidateOp(st *storage.LevelDBBackend, config common.Config, source *block.
return errors.BlockAccountDoesNotExists
}
// If it's a frozen account, it cannot receive payment
if taccount.Linked != "" {
if taccount.IsFrozen() {
return errors.FrozenAccountNoDeposit
}

Expand Down
1 change: 1 addition & 0 deletions lib/node/runner/checker_ballot_transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ func TestValidateTxOverBalance(t *testing.T) {
// Now the total amount of the ops + balance is equal to the balance
opbody.Amount = opbody.Amount.MustSub(common.BaseFee.MustMult(len(tx.B.Operations)))
tx.B.Operations[0].B = opbody
tx.B.Fee = common.BaseFee * 4
require.Nil(t, ValidateTx(st, common.Config{}, tx))
}

Expand Down
1 change: 0 additions & 1 deletion lib/node/runner/checker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ func TestOnlyValidTransactionInTransactionPool(t *testing.T) {
tx.Sign(rootKP, networkID)

runChecker(tx, nil)

require.True(t, nodeRunner.TransactionPool.Has(tx.GetHash()), "valid transaction must be in `Pool`")
}

Expand Down
107 changes: 0 additions & 107 deletions lib/node/runner/isaac_simulation_unfreezing_test.go

This file was deleted.

Loading

0 comments on commit 081a160

Please sign in to comment.