Exemple #1
0
        private bool ChargeSizeFee(ChargeTransactionFeesInput input, ref TransactionFeeBill bill)
        {
            string symbolChargedForBaseFee = null;
            var    amountChargedForBaseFee = 0L;
            var    symbolToPayTxFee        = input.PrimaryTokenSymbol;

            if (bill.FeesMap.Any())
            {
                symbolChargedForBaseFee = bill.FeesMap.First().Key;
                amountChargedForBaseFee = bill.FeesMap.First().Value;
            }

            var availableBalance = symbolChargedForBaseFee == symbolToPayTxFee
                                   // Available balance need to deduct amountChargedForBaseFee
                ? GetBalance(Context.Sender, symbolToPayTxFee).Sub(amountChargedForBaseFee)
                : GetBalance(Context.Sender, symbolToPayTxFee);

            var txSizeFeeAmount = input.TransactionSizeFee;

            if (input.SymbolsToPayTxSizeFee.Any())
            {
                var allSymbolToTxFee = input.SymbolsToPayTxSizeFee;
                var availableSymbol  = allSymbolToTxFee.FirstOrDefault(x =>
                                                                       GetBalanceCalculatedBaseOnPrimaryToken(x, symbolChargedForBaseFee,
                                                                                                              amountChargedForBaseFee) >= txSizeFeeAmount) ??
                                       allSymbolToTxFee.FirstOrDefault(x =>
                                                                       GetBalanceCalculatedBaseOnPrimaryToken(x, symbolChargedForBaseFee,
                                                                                                              amountChargedForBaseFee) > 0);
                if (availableSymbol != null && availableSymbol.TokenSymbol != symbolToPayTxFee)
                {
                    symbolToPayTxFee = availableSymbol.TokenSymbol;
                    txSizeFeeAmount  = txSizeFeeAmount.Mul(availableSymbol.AddedTokenWeight)
                                       .Div(availableSymbol.BaseTokenWeight);
                    availableBalance = symbolChargedForBaseFee == symbolToPayTxFee
                        ? GetBalance(Context.Sender, symbolToPayTxFee).Sub(amountChargedForBaseFee)
                        : GetBalance(Context.Sender, symbolToPayTxFee);
                }
            }

            var chargeAmount = availableBalance > txSizeFeeAmount
                ? txSizeFeeAmount
                : availableBalance;

            if (symbolToPayTxFee == null)
            {
                return(availableBalance >= txSizeFeeAmount);
            }

            if (symbolChargedForBaseFee == symbolToPayTxFee)
            {
                bill.FeesMap[symbolToPayTxFee] =
                    bill.FeesMap[symbolToPayTxFee].Add(chargeAmount);
            }
            else
            {
                bill.FeesMap.Add(symbolToPayTxFee, chargeAmount);
            }

            return(availableBalance >= txSizeFeeAmount);
        }
Exemple #2
0
        /// <summary>
        /// Related transactions will be generated by acs1 pre-plugin service,
        /// and will be executed before the origin transaction.
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public override BoolValue ChargeTransactionFees(ChargeTransactionFeesInput input)
        {
            Assert(input.MethodName != null && input.ContractAddress != null, "Invalid charge transaction fees input.");

            // Primary token not created yet.
            if (string.IsNullOrEmpty(input.PrimaryTokenSymbol))
            {
                return(new BoolValue {
                    Value = true
                });
            }

            // Record tx fee bill during current charging process.
            var bill = new TransactionFeeBill();

            var fromAddress = Context.Sender;
            var methodFees  = Context.Call <MethodFees>(input.ContractAddress, nameof(GetMethodFee),
                                                        new StringValue {
                Value = input.MethodName
            });
            var successToChargeBaseFee = true;

            if (methodFees != null && methodFees.Fees.Any())
            {
                successToChargeBaseFee = ChargeBaseFee(GetBaseFeeDictionary(methodFees), ref bill);
            }

            var successToChargeSizeFee = true;

            if (!IsMethodFeeSetToZero(methodFees))
            {
                // Then also do not charge size fee.
                successToChargeSizeFee = ChargeSizeFee(input, ref bill);
            }

            // Update balances.
            foreach (var tokenToAmount in bill.FeesMap)
            {
                ModifyBalance(fromAddress, tokenToAmount.Key, -tokenToAmount.Value);
                Context.Fire(new TransactionFeeCharged
                {
                    Symbol = tokenToAmount.Key,
                    Amount = tokenToAmount.Value
                });
                if (tokenToAmount.Value == 0)
                {
                    Context.LogDebug(() => $"Maybe incorrect charged tx fee of {tokenToAmount.Key}: it's 0.");
                }
            }

            return(new BoolValue {
                Value = successToChargeBaseFee && successToChargeSizeFee
            });
        }
Exemple #3
0
        public override ConsumedResourceTokens ChargeResourceToken(ChargeResourceTokenInput input)
        {
            var consumedResourceTokens = new ConsumedResourceTokens();

            Context.LogDebug(() => $"Start executing ChargeResourceToken.{input}");
            if (input.Equals(new ChargeResourceTokenInput()))
            {
                return(consumedResourceTokens);
            }

            var symbolToAmount = new Dictionary <string, long>
            {
                { "READ", input.ReadCost },
                { "TRAFFIC", input.TrafficCost },
                { "STORAGE", input.StorageCost },
                { "WRITE", input.WriteCost }
            };

            var bill = new TransactionFeeBill();

            foreach (var pair in symbolToAmount)
            {
                Context.LogDebug(() => $"Charging {pair.Value} {pair.Key} tokens.");
                var existingBalance = State.Balances[Context.Sender][pair.Key];
                if (existingBalance < pair.Value)
                {
                    bill.TokenToAmount.Add(pair.Key, existingBalance);
                    var owningBalance = State.OwningResourceToken[Context.Sender][pair.Key]
                                        .Add(pair.Value.Sub(existingBalance));
                    State.OwningResourceToken[Context.Sender][pair.Key] = owningBalance;

                    consumedResourceTokens.IsFailedToCharge = true;
                    consumedResourceTokens.Owning.Add(pair.Key, owningBalance);

                    Context.LogDebug(() => $"Insufficient resource. {pair.Key}: {existingBalance} / {pair.Value}");
                }
                else
                {
                    bill.TokenToAmount.Add(pair.Key, pair.Value);
                }
            }

            foreach (var pair in bill.TokenToAmount)
            {
                State.ChargedResourceTokens[input.Caller][Context.Sender][pair.Key] =
                    State.ChargedResourceTokens[input.Caller][Context.Sender][pair.Key].Add(pair.Value);
                consumedResourceTokens.Value.Add(pair.Key, pair.Value);
            }

            Context.LogDebug(() => $"Finished executing ChargeResourceToken.{consumedResourceTokens}");

            return(consumedResourceTokens);
        }
        public static TransactionFeeBill operator +(TransactionFeeBill bill1, TransactionFeeBill bill2)
        {
            var result = new TransactionFeeBill();

            foreach (var symbol in bill1.FeesMap.Keys.Union(bill2.FeesMap.Keys))
            {
                var amountInBill1 = bill1.FeesMap.ContainsKey(symbol) ? bill1.FeesMap[symbol] : 0;
                var amountInBill2 = bill2.FeesMap.ContainsKey(symbol) ? bill2.FeesMap[symbol] : 0;
                result.FeesMap.Add(symbol, amountInBill1.Add(amountInBill2));
            }

            return(result);
        }
Exemple #5
0
        public override Empty ChargeResourceToken(ChargeResourceTokenInput input)
        {
            Context.LogDebug(() => $"Start executing ChargeResourceToken.{input}");
            if (input.Equals(new ChargeResourceTokenInput()))
            {
                return(new Empty());
            }

            var bill = new TransactionFeeBill();

            foreach (var pair in input.CostDic)
            {
                Context.LogDebug(() => $"Charging {pair.Value} {pair.Key} tokens.");
                var existingBalance = GetBalance(Context.Sender, pair.Key);
                if (existingBalance < pair.Value)
                {
                    bill.FeesMap.Add(pair.Key, existingBalance);
                    var owningBalance = State.OwningResourceToken[Context.Sender][pair.Key]
                                        .Add(pair.Value.Sub(existingBalance));
                    State.OwningResourceToken[Context.Sender][pair.Key] = owningBalance;
                    Context.LogDebug(() => $"Insufficient resource. {pair.Key}: {existingBalance} / {pair.Value}");
                    Context.Fire(new ResourceTokenOwned
                    {
                        Symbol = pair.Key,
                        Amount = owningBalance
                    });
                }
                else
                {
                    bill.FeesMap.Add(pair.Key, pair.Value);
                }
            }

            foreach (var pair in bill.FeesMap)
            {
                Context.Fire(new ResourceTokenCharged
                {
                    Symbol          = pair.Key,
                    Amount          = pair.Value,
                    ContractAddress = Context.Sender
                });
                if (pair.Value == 0)
                {
                    Context.LogDebug(() => $"Maybe incorrect charged resource fee of {pair.Key}: it's 0.");
                }
            }

            return(new Empty());
        }
Exemple #6
0
        public override Empty ClaimTransactionFees(Empty input)
        {
            Context.LogDebug(() => "Claim transaction fee.");
            if (State.TreasuryContract.Value == null)
            {
                var treasuryContractAddress =
                    Context.GetContractAddressByName(SmartContractConstants.TreasuryContractSystemName);
                if (treasuryContractAddress == null)
                {
                    // Which means Treasury Contract didn't deployed yet. Ignore this method.
                    return(new Empty());
                }

                State.TreasuryContract.Value = treasuryContractAddress;
            }

            var transactions = Context.GetPreviousBlockTransactions();

            Context.LogDebug(() => $"Got {transactions.Count} transaction(s) from previous block.");

            var senders   = transactions.Select(t => t.From).ToList();
            var totalBill = new TransactionFeeBill();

            foreach (var sender in senders)
            {
                var oldBill = State.ChargedFees[sender];
                if (oldBill != null)
                {
                    totalBill += oldBill;
                }

                // Clear
                State.ChargedFees[sender] = new TransactionFeeBill();
            }

            foreach (var bill in totalBill.TokenToAmount)
            {
                var symbol = bill.Key;
                var amount = bill.Value;
                State.Balances[Context.Self][symbol] = State.Balances[Context.Self][symbol].Add(amount);
                TransferTransactionFeesToFeeReceiver(symbol, amount);
            }

            Context.LogDebug(() => "Finish claim transaction fee.");

            return(new Empty());
        }
Exemple #7
0
        /// <summary>
        /// Related transactions will be generated by acs1 pre-plugin service,
        /// and will be executed before the origin transaction.
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public override TransactionFee ChargeTransactionFees(ChargeTransactionFeesInput input)
        {
            Assert(input.MethodName != null && input.ContractAddress != null, "Invalid charge transaction fees input.");

            var transactionFee = new TransactionFee();

            // Primary token not created yet.
            if (string.IsNullOrEmpty(input.PrimaryTokenSymbol))
            {
                return(transactionFee);
            }

            // Record tx fee bill during current charging process.
            var bill = new TransactionFeeBill();

            var fromAddress = Context.Sender;
            var methodFees  = Context.Call <MethodFees>(input.ContractAddress, nameof(GetMethodFee),
                                                        new StringValue {
                Value = input.MethodName
            });
            var successToChargeBaseFee = true;

            if (methodFees != null && methodFees.Fees.Any())
            {
                successToChargeBaseFee = ChargeBaseFee(GetBaseFeeDictionary(methodFees), ref bill);
            }

            var successToChargeSizeFee = ChargeSizeFee(input, ref bill);

            // Update the bill.
            var oldBill = State.ChargedFees[fromAddress];

            State.ChargedFees[fromAddress] = oldBill == null ? bill : oldBill + bill;

            // Update balances.
            foreach (var tokenToAmount in bill.TokenToAmount)
            {
                State.Balances[fromAddress][tokenToAmount.Key] =
                    State.Balances[fromAddress][tokenToAmount.Key].Sub(tokenToAmount.Value);
                transactionFee.Value[tokenToAmount.Key] = tokenToAmount.Value;
            }

            transactionFee.IsFailedToCharge = !successToChargeBaseFee || !successToChargeSizeFee;
            return(transactionFee);
        }
Exemple #8
0
        private bool ChargeBaseFee(Dictionary <string, long> methodFeeMap, ref TransactionFeeBill bill)
        {
            if (!ChargeFirstSufficientToken(methodFeeMap, out var symbolToChargeBaseFee,
                                            out var amountToChargeBaseFee, out var existingBalance))
            {
                Context.LogDebug(() => "Failed to charge first sufficient token.");
                if (symbolToChargeBaseFee != null)
                {
                    bill.FeesMap.Add(symbolToChargeBaseFee, existingBalance);
                } // If symbol == null, then charge nothing in base fee part.

                return(false);
            }

            bill.FeesMap.Add(symbolToChargeBaseFee, amountToChargeBaseFee);

            return(true);
        }
Exemple #9
0
        private bool ChargeSizeFee(ChargeTransactionFeesInput input, ref TransactionFeeBill bill)
        {
            string symbolChargedForBaseFee = null;
            var    amountChargedForBaseFee = 0L;

            if (bill.TokenToAmount.Any())
            {
                symbolChargedForBaseFee = bill.TokenToAmount.First().Key;
                amountChargedForBaseFee = bill.TokenToAmount.First().Value;
            }

            var availableBalance = symbolChargedForBaseFee == input.PrimaryTokenSymbol
                                   // Available balance need to deduct amountChargedForBaseFee
                ? State.Balances[Context.Sender][input.PrimaryTokenSymbol].Sub(amountChargedForBaseFee)
                : State.Balances[Context.Sender][input.PrimaryTokenSymbol];
            var txSizeFeeAmount = input.TransactionSizeFee;
            var chargeAmount    = availableBalance > txSizeFeeAmount // Is available balance enough to pay tx size fee?
                ? txSizeFeeAmount
                : availableBalance;

            if (input.PrimaryTokenSymbol == null)
            {
                return(availableBalance >= txSizeFeeAmount);
            }

            if (symbolChargedForBaseFee == input.PrimaryTokenSymbol)
            {
                bill.TokenToAmount[input.PrimaryTokenSymbol] =
                    bill.TokenToAmount[input.PrimaryTokenSymbol].Add(chargeAmount);
            }
            else
            {
                bill.TokenToAmount.Add(input.PrimaryTokenSymbol, chargeAmount);
            }

            return(availableBalance >= txSizeFeeAmount);
        }
Exemple #10
0
        public override Empty ChargeResourceToken(ChargeResourceTokenInput input)
        {
            AssertTransactionGeneratedByPlugin();
            Context.LogDebug(() => $"Start executing ChargeResourceToken.{input}");
            if (input.Equals(new ChargeResourceTokenInput()))
            {
                return(new Empty());
            }

            var bill = new TransactionFeeBill();

            foreach (var pair in input.CostDic)
            {
                Context.LogDebug(() => $"Charging {pair.Value} {pair.Key} tokens.");
                var existingBalance = GetBalance(Context.Sender, pair.Key);
                Assert(existingBalance >= pair.Value,
                       $"Insufficient resource of {pair.Key}. Need balance: {pair.Value}; Current balance: {existingBalance}.");
                bill.FeesMap.Add(pair.Key, pair.Value);
            }

            foreach (var pair in bill.FeesMap)
            {
                Context.Fire(new ResourceTokenCharged
                {
                    Symbol          = pair.Key,
                    Amount          = pair.Value,
                    ContractAddress = Context.Sender
                });
                if (pair.Value == 0)
                {
                    Context.LogDebug(() => $"Maybe incorrect charged resource fee of {pair.Key}: it's 0.");
                }
            }

            return(new Empty());
        }
Exemple #11
0
        /// <summary>
        /// Related transactions will be generated by acs1 pre-plugin service,
        /// and will be executed before the origin transaction.
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public override TransactionFee ChargeTransactionFees(ChargeTransactionFeesInput input)
        {
            Assert(input.MethodName != null && input.ContractAddress != null, "Invalid charge transaction fees input.");

            var result = new TransactionFee();

            if (input.PrimaryTokenSymbol == string.Empty)
            {
                // Primary token not created yet.
                return(result);
            }

            var fee = Context.Call <MethodFees>(input.ContractAddress, nameof(GetMethodFee),
                                                new StringValue {
                Value = input.MethodName
            });

            var bill = new TransactionFeeBill();

            var fromAddress = Context.Sender;

            if (fee != null && fee.Fees.Any())
            {
                if (!ChargeFirstSufficientToken(fee.Fees.ToDictionary(f => f.Symbol, f => f.BasicFee), out var symbol,
                                                out var amount, out var existingBalance))
                {
                    Assert(false, "Failed to charge first sufficient token.");
                }

                bill.TokenToAmount.Add(symbol, amount);
                // Charge base fee.
                State.Balances[fromAddress][symbol] = existingBalance.Sub(amount);
                result.Value.Add(symbol, amount);
            }

            var balanceAfterChargingBaseFee = State.Balances[fromAddress][input.PrimaryTokenSymbol];
            var txSizeFeeAmount             = input.TransactionSizeFee;

            txSizeFeeAmount = balanceAfterChargingBaseFee > txSizeFeeAmount // Enough to pay tx size fee.
                ? txSizeFeeAmount
                                                                            // It's safe to convert from long to int here because
                                                                            // balanceAfterChargingBaseFee <= txSizeFeeAmount and
                                                                            // typeof(txSizeFeeAmount) == int
                : (int)balanceAfterChargingBaseFee;

            bill += new TransactionFeeBill
            {
                TokenToAmount =
                {
                    {
                        input.PrimaryTokenSymbol,
                        txSizeFeeAmount
                    }
                }
            };

            // Charge tx size fee.
            var finalBalanceOfNativeSymbol = balanceAfterChargingBaseFee.Sub(txSizeFeeAmount);

            State.Balances[fromAddress][input.PrimaryTokenSymbol] = finalBalanceOfNativeSymbol;

            // Record the bill finally.
            var oldBill = State.ChargedFees[fromAddress];

            State.ChargedFees[fromAddress] = oldBill == null ? bill : oldBill + bill;

            // If balanceAfterChargingBaseFee < txSizeFeeAmount, make sender's balance of native symbol to 0 and make current execution failed.
            Assert(
                balanceAfterChargingBaseFee >=
                input.TransactionSizeFee,
                $"Insufficient balance to pay tx size fee: {balanceAfterChargingBaseFee} < {input.TransactionSizeFee}.\n " +
                $"Primary token: {input.PrimaryTokenSymbol}");

            if (result.Value.ContainsKey(input.PrimaryTokenSymbol))
            {
                result.Value[input.PrimaryTokenSymbol] =
                    result.Value[input.PrimaryTokenSymbol].Add(txSizeFeeAmount);
            }
            else
            {
                result.Value.Add(input.PrimaryTokenSymbol, txSizeFeeAmount);
            }

            return(result);
        }
Exemple #12
0
        /// <summary>
        /// Related transactions will be generated by acs1 pre-plugin service,
        /// and will be executed before the origin transaction.
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public override ChargeTransactionFeesOutput ChargeTransactionFees(ChargeTransactionFeesInput input)
        {
            AssertTransactionGeneratedByPlugin();
            Assert(input.MethodName != null && input.ContractAddress != null, "Invalid charge transaction fees input.");

            // Primary token not created yet.
            if (State.ChainPrimaryTokenSymbol.Value == null)
            {
                return(new ChargeTransactionFeesOutput {
                    Success = true
                });
            }

            // Record tx fee bill during current charging process.
            var bill = new TransactionFeeBill();

            var fromAddress = Context.Sender;
            var methodFees  = Context.Call <MethodFees>(input.ContractAddress, nameof(GetMethodFee),
                                                        new StringValue {
                Value = input.MethodName
            });
            var successToChargeBaseFee = true;

            if (methodFees != null && methodFees.Fees.Any())
            {
                successToChargeBaseFee = ChargeBaseFee(GetBaseFeeDictionary(methodFees), ref bill);
            }

            var successToChargeSizeFee = true;

            if (!IsMethodFeeSetToZero(methodFees))
            {
                // Then also do not charge size fee.
                successToChargeSizeFee = ChargeSizeFee(input, ref bill);
            }

            // Update balances.
            foreach (var tokenToAmount in bill.FeesMap)
            {
                ModifyBalance(fromAddress, tokenToAmount.Key, -tokenToAmount.Value);
                Context.Fire(new TransactionFeeCharged
                {
                    Symbol = tokenToAmount.Key,
                    Amount = tokenToAmount.Value
                });
                if (tokenToAmount.Value == 0)
                {
                    Context.LogDebug(() => $"Maybe incorrect charged tx fee of {tokenToAmount.Key}: it's 0.");
                }
            }

            var chargingResult = successToChargeBaseFee && successToChargeSizeFee;
            var chargingOutput = new ChargeTransactionFeesOutput {
                Success = chargingResult
            };

            if (!chargingResult)
            {
                chargingOutput.ChargingInformation = "Transaction fee not enough.";
            }
            return(chargingOutput);
        }