Exemple #1
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IAccountStateDelta state = context.PreviousStates;

            if (context.Rehearsal)
            {
                return(state
                       .SetState(ActivatedAccountsState.Address, MarkChanged));
            }

            if (!state.TryGetState(ActivatedAccountsState.Address, out Dictionary accountsAsDict))
            {
                throw new ActivatedAccountsDoesNotExistsException();
            }

            CheckObsolete(BlockChain.Policy.BlockPolicySource.V100080ObsoleteIndex, context);
            CheckPermission(context);

            var accounts = new ActivatedAccountsState(accountsAsDict);

            return(state.SetState(
                       ActivatedAccountsState.Address,
                       accounts.AddAccount(Address).Serialize()
                       ));
        }
Exemple #2
0
        public void ExecuteWithUnactivatedRecipient()
        {
            var activatedAddress = new ActivatedAccountsState().AddAccount(new PrivateKey().ToAddress());
            var balance          = ImmutableDictionary <(Address, Currency), FungibleAssetValue> .Empty
                                   .Add((_sender, _currency), _currency * 1000)
                                   .Add((_recipient, _currency), _currency * 10);

            var state = ImmutableDictionary <Address, IValue> .Empty
                        .Add(_sender.Derive(ActivationKey.DeriveKey), true.Serialize())
                        .Add(Addresses.ActivatedAccount, activatedAddress.Serialize());

            var prevState = new State(
                state: state,
                balance: balance
                );
            var action = new TransferAsset(
                sender: _sender,
                recipient: _recipient,
                amount: _currency * 100
                );
            var ex = Assert.Throws <InvalidTransferUnactivatedRecipientException>(() =>
            {
                action.Execute(new ActionContext()
                {
                    PreviousStates = prevState,
                    Signer         = _sender,
                    Rehearsal      = false,
                    BlockIndex     = 1,
                });
            });

            Assert.Equal(_sender, ex.Sender);
            Assert.Equal(_recipient, ex.Recipient);
        }
        public override IAccountStateDelta Execute(IActionContext context)
        {
            var states = context.PreviousStates;

            if (context.Rehearsal)
            {
                return(states.SetState(Nekoyume.Addresses.ActivatedAccount, MarkChanged));
            }

            CheckPermission(context);

            Log.Debug($"Start {nameof(MigrationActivatedAccountsState)}");
            if (states.TryGetState(Nekoyume.Addresses.ActivatedAccount, out Dictionary rawState))
            {
                var activatedAccountsState = new ActivatedAccountsState(rawState);
                var accounts = activatedAccountsState.Accounts;
                foreach (var agentAddress in accounts)
                {
                    var address = agentAddress.Derive(ActivationKey.DeriveKey);
                    if (states.GetState(address) is null)
                    {
                        states = states.SetState(address, true.Serialize());
                    }
                }
                Log.Debug($"Finish {nameof(MigrationActivatedAccountsState)}");
                return(states);
            }

            throw new ActivatedAccountsDoesNotExistsException();
        }
Exemple #4
0
        public async Task ActivateAccount()
        {
            var nonce      = new byte[] { 0x00, 0x01, 0x02, 0x03 };
            var privateKey = new PrivateKey();

            (ActivationKey activationKey, PendingActivationState pendingActivation) =
                ActivationKey.Create(privateKey, nonce);
            PolymorphicAction <ActionBase> action = new CreatePendingActivation(pendingActivation);

            BlockChain.MakeTransaction(AdminPrivateKey, new[] { action });
            await BlockChain.MineBlock(AdminAddress);

            var encodedActivationKey = activationKey.Encode();
            var queryResult          = await ExecuteQueryAsync(
                $"mutation {{ activationStatus {{ activateAccount(encodedActivationKey: \"{encodedActivationKey}\") }} }}");

            await BlockChain.MineBlock(AdminAddress);

            var result = (bool)queryResult.Data
                         .As <Dictionary <string, object> >()["activationStatus"]
                         .As <Dictionary <string, object> >()["activateAccount"];

            Assert.True(result);

            var state = (Bencodex.Types.Dictionary)BlockChain.GetState(
                ActivatedAccountsState.Address);
            var     activatedAccountsState = new ActivatedAccountsState(state);
            Address userAddress            = StandaloneContextFx.NineChroniclesNodeService !.MinerPrivateKey !.ToAddress();

            Assert.True(activatedAccountsState.Accounts.Contains(userAddress));
        }
Exemple #5
0
        public ActivationStatusQuery(StandaloneContext standaloneContext)
        {
            Field <NonNullGraphType <BooleanGraphType> >(
                name: "activated",
                resolve: context =>
            {
                var service = standaloneContext.NineChroniclesNodeService;

                if (service is null)
                {
                    return(false);
                }

                try
                {
                    if (!(service.MinerPrivateKey is { } privateKey))
                    {
                        throw new InvalidOperationException($"{nameof(service.MinerPrivateKey)} is null.");
                    }

                    if (!(service.Swarm?.BlockChain is { } blockChain))
                    {
                        throw new InvalidOperationException($"{nameof(service.Swarm.BlockChain)} is null.");
                    }

                    Address userAddress      = privateKey.ToAddress();
                    Address activatedAddress = userAddress.Derive(ActivationKey.DeriveKey);

                    if (blockChain.GetState(activatedAddress) is Bencodex.Types.Boolean)
                    {
                        return(true);
                    }

                    // Preserve previous check code due to migration period.
                    // TODO: Remove this code after v100061+
                    IValue state = blockChain.GetState(ActivatedAccountsState.Address);

                    if (state is Bencodex.Types.Dictionary asDict)
                    {
                        var activatedAccountsState = new ActivatedAccountsState(asDict);
                        var activatedAccounts      = activatedAccountsState.Accounts;
                        return(activatedAccounts.Count == 0 ||
                               activatedAccounts.Contains(userAddress));
                    }

                    return(true);
                }
                catch (Exception e)
                {
                    var msg = "Unexpected exception occurred during ActivationStatusQuery: {e}";
                    context.Errors.Add(new ExecutionError(msg, e));
                    Log.Error(msg, e);
                    return(false);
                }
            }
                );
        }
        public void Serialize()
        {
            var accounts = ImmutableHashSet.Create(default(Address));
            var state    = new ActivatedAccountsState(accounts);

            var serialized   = (Dictionary)state.Serialize();
            var deserialized = new ActivatedAccountsState(serialized);

            Assert.Equal(accounts, deserialized.Accounts);
        }
Exemple #7
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            var state = context.PreviousStates;

            if (context.Rehearsal)
            {
                return(state.MarkBalanceChanged(Amount.Currency, new[] { Sender, Recipient }));
            }

            if (Sender != context.Signer)
            {
                throw new InvalidTransferSignerException(context.Signer, Sender, Recipient);
            }

            // This works for block after 380000. Please take a look at
            // https://github.com/planetarium/libplanet/pull/1133
            if (context.BlockIndex > 380000 && Sender == Recipient)
            {
                throw new InvalidTransferRecipientException(Sender, Recipient);
            }

            Address recipientAddress = Recipient.Derive(ActivationKey.DeriveKey);

            // Check new type of activation first.
            if (state.GetState(recipientAddress) is null && state.GetState(Addresses.ActivatedAccount) is Dictionary asDict)
            {
                var activatedAccountsState = new ActivatedAccountsState(asDict);
                var activatedAccounts      = activatedAccountsState.Accounts;
                // if ActivatedAccountsState is empty, all user is activate.
                if (activatedAccounts.Count != 0 &&
                    !activatedAccounts.Contains(Recipient))
                {
                    throw new InvalidTransferUnactivatedRecipientException(Sender, Recipient);
                }
            }

            Currency currency = Amount.Currency;

            if (!(currency.Minters is null) &&
                (currency.Minters.Contains(Sender) || currency.Minters.Contains(Recipient)))
            {
                throw new InvalidTransferMinterException(
                          currency.Minters,
                          Sender,
                          Recipient
                          );
            }

            return(state.TransferAsset(Sender, Recipient, Amount));
        }
        public void SerializeWithDotNetAPI()
        {
            var accounts = ImmutableHashSet.Create(default(Address));
            var state    = new ActivatedAccountsState(accounts);

            var formatter = new BinaryFormatter();

            using var ms = new MemoryStream();
            formatter.Serialize(ms, state);
            ms.Seek(0, SeekOrigin.Begin);

            var deserialized = (ActivatedAccountsState)formatter.Deserialize(ms);

            Assert.Equal(accounts, deserialized.Accounts);
        }
Exemple #9
0
 protected override void LoadPlainValueInternal(IImmutableDictionary <string, IValue> plainValue)
 {
     RankingState           = new RankingState((Bencodex.Types.Dictionary)plainValue["ranking_state"]);
     ShopState              = new ShopState((Bencodex.Types.Dictionary)plainValue["shop_state"]);
     TableSheetsState       = new TableSheetsState((Bencodex.Types.Dictionary)plainValue["table_sheets_state"]);
     GameConfigState        = new GameConfigState((Bencodex.Types.Dictionary)plainValue["game_config_state"]);
     RedeemCodeState        = new RedeemCodeState((Bencodex.Types.Dictionary)plainValue["redeem_code_state"]);
     AdminAddressState      = new AdminState((Bencodex.Types.Dictionary)plainValue["admin_address_state"]);
     ActivatedAccountsState = new ActivatedAccountsState(
         (Bencodex.Types.Dictionary)plainValue["activated_accounts_state"]
         );
     GoldCurrencyState = new GoldCurrencyState(
         (Bencodex.Types.Dictionary)plainValue["gold_currency_state"]
         );
 }
Exemple #10
0
        private bool CheckSigner(
            Transaction <NCAction> transaction,
            BlockChain <NCAction> blockChain
            )
        {
            try
            {
                // Check if it is a no-op transaction to prove it's made by the authorized miner.
                if (blockChain.GetState(AuthorizedMinersState.Address) is Dictionary rawAms &&
                    new AuthorizedMinersState(rawAms).Miners.Contains(transaction.Signer))
                {
                    // The authorization proof has to have no actions at all.
                    return(!transaction.Actions.Any());
                }

                if (transaction.Actions.Count == 1 &&
                    transaction.Actions.First().InnerAction is ActivateAccount aa)
                {
                    return(blockChain.GetState(aa.PendingAddress) is Dictionary rawPending &&
                           new PendingActivationState(rawPending).Verify(aa));
                }

                if (blockChain.GetState(ActivatedAccountsState.Address) is Dictionary asDict)
                {
                    IImmutableSet <Address> activatedAccounts =
                        new ActivatedAccountsState(asDict).Accounts;
                    return(!activatedAccounts.Any() ||
                           activatedAccounts.Contains(transaction.Signer));
                }
                else
                {
                    return(true);
                }
            }
            catch (InvalidSignatureException)
            {
                return(false);
            }
            catch (IncompleteBlockStatesException)
            {
                // It can be caused during `Swarm<T>.PreloadAsync()` because it doesn't fill its
                // state right away...
                // FIXME It should be removed after fix that Libplanet fills its state on IBD.
                // See also: https://github.com/planetarium/lib9c/pull/151#discussion_r506039478
                return(true);
            }
        }
        public async Task ActivateAccount()
        {
            var adminPrivateKey = new PrivateKey();
            var adminAddress = adminPrivateKey.ToAddress();
            var activateAccounts = new[] { adminAddress }.ToImmutableHashSet();

            Block <PolymorphicAction <ActionBase> > genesis =
                MakeGenesisBlock(adminAddress, new Currency("NCG", 2, minters: null), activateAccounts);
            NineChroniclesNodeService service = ServiceBuilder.CreateNineChroniclesNodeService(genesis);

            StandaloneContextFx.NineChroniclesNodeService = service;
            StandaloneContextFx.BlockChain = service.Swarm.BlockChain;

            var blockChain = StandaloneContextFx.BlockChain;

            var nonce      = new byte[] { 0x00, 0x01, 0x02, 0x03 };
            var privateKey = new PrivateKey();

            (ActivationKey activationKey, PendingActivationState pendingActivation) =
                ActivationKey.Create(privateKey, nonce);
            PolymorphicAction <ActionBase> action = new CreatePendingActivation(pendingActivation);

            blockChain.MakeTransaction(adminPrivateKey, new[] { action });
            await blockChain.MineBlock(adminAddress);

            var encodedActivationKey = activationKey.Encode();
            var queryResult          = await ExecuteQueryAsync(
                $"mutation {{ activationStatus {{ activateAccount(encodedActivationKey: \"{encodedActivationKey}\") }} }}");

            await blockChain.MineBlock(adminAddress);

            var result = (bool)queryResult.Data
                         .As <Dictionary <string, object> >()["activationStatus"]
                         .As <Dictionary <string, object> >()["activateAccount"];

            Assert.True(result);

            var state = (Bencodex.Types.Dictionary)blockChain.GetState(
                ActivatedAccountsState.Address);
            var     activatedAccountsState = new ActivatedAccountsState(state);
            Address userAddress            = service.PrivateKey.ToAddress();

            Assert.True(activatedAccountsState.Accounts.Contains(userAddress));
        }
Exemple #12
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IAccountStateDelta state = context.PreviousStates;

            if (context.Rehearsal)
            {
                return(state
                       .SetState(ActivatedAccountsState.Address, MarkChanged)
                       .SetState(PendingAddress, MarkChanged));
            }

            CheckObsolete(BlockChain.Policy.BlockPolicySource.V100080ObsoleteIndex, context);

            if (!state.TryGetState(ActivatedAccountsState.Address, out Dictionary accountsAsDict))
            {
                throw new ActivatedAccountsDoesNotExistsException();
            }
            if (!state.TryGetState(PendingAddress, out Dictionary pendingAsDict))
            {
                throw new PendingActivationDoesNotExistsException(PendingAddress);
            }

            var accounts = new ActivatedAccountsState(accountsAsDict);
            var pending  = new PendingActivationState(pendingAsDict);

            if (pending.Verify(Signature))
            {
                // We left this log message to track activation history.
                // Please delete it if we have an API for evaluation results on the Libplanet side.
                Log.Information("{pendingAddress} is activated by {signer} now.", pending.address, context.Signer);
                return(state.SetState(
                           ActivatedAccountsState.Address,
                           accounts.AddAccount(context.Signer).Serialize()
                           ).SetState(
                           pending.address,
                           new Bencodex.Types.Null()
                           ));
            }
            else
            {
                throw new InvalidSignatureException(pending, Signature);
            }
        }
Exemple #13
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IActionContext ctx              = context;
            var            states           = ctx.PreviousStates;
            var            weeklyArenaState = new WeeklyArenaState(0);

            if (ctx.Rehearsal)
            {
                states = states.SetState(RankingState.Address, MarkChanged);
                states = states.SetState(ShopState.Address, MarkChanged);
                states = states.SetState(TableSheetsState.Address, MarkChanged);
                states = states.SetState(weeklyArenaState.address, MarkChanged);
                states = states.SetState(GameConfigState.Address, MarkChanged);
                states = states.SetState(RedeemCodeState.Address, MarkChanged);
                states = states.SetState(AdminState.Address, MarkChanged);
                states = states.SetState(ActivatedAccountsState.Address, MarkChanged);
                states = states.SetState(GoldCurrencyState.Address, MarkChanged);
                states = states.SetState(Addresses.GoldDistribution, MarkChanged);
                return(states);
            }

            if (ctx.BlockIndex != 0)
            {
                return(states);
            }

            states = states
                     .SetState(weeklyArenaState.address, weeklyArenaState.Serialize())
                     .SetState(RankingState.Address, RankingState.Serialize())
                     .SetState(ShopState.Address, ShopState.Serialize())
                     .SetState(TableSheetsState.Address, TableSheetsState.Serialize())
                     .SetState(GameConfigState.Address, GameConfigState.Serialize())
                     .SetState(RedeemCodeState.Address, RedeemCodeState.Serialize())
                     .SetState(AdminState.Address, AdminAddressState.Serialize())
                     .SetState(ActivatedAccountsState.Address, ActivatedAccountsState.Serialize())
                     .SetState(GoldCurrencyState.Address, GoldCurrencyState.Serialize())
                     .SetState(Addresses.GoldDistribution, GoldDistributions.Select(v => v.Serialize()).Serialize());

            states = states.MintAsset(GoldCurrencyState.Address, GoldCurrencyState.Currency, 1000000000);
            return(states);
        }
Exemple #14
0
        public ActivationStatusQuery(StandaloneContext standaloneContext)
        {
            Field <NonNullGraphType <BooleanGraphType> >(
                name: "activated",
                resolve: context =>
            {
                var service = standaloneContext.NineChroniclesNodeService;

                if (service is null)
                {
                    return(false);
                }

                try
                {
                    PrivateKey privateKey = service.PrivateKey;
                    Address address       = privateKey.ToAddress();
                    BlockChain <NineChroniclesActionType> blockChain = service.Swarm.BlockChain;
                    IValue state = blockChain.GetState(ActivatedAccountsState.Address);

                    if (state is Bencodex.Types.Dictionary asDict)
                    {
                        var activatedAccountsState = new ActivatedAccountsState(asDict);
                        var activatedAccounts      = activatedAccountsState.Accounts;
                        return(activatedAccounts.Count == 0 ||
                               activatedAccounts.Contains(address));
                    }

                    return(true);
                }
                catch (Exception e)
                {
                    var msg = "Unexpected exception occurred during ActivationStatusQuery: {e}";
                    context.Errors.Add(new ExecutionError(msg, e));
                    Log.Error(msg, e);
                    return(false);
                }
            }
                );
        }
Exemple #15
0
        public override IAccountStateDelta Execute(IActionContext context)
        {
            IAccountStateDelta state = context.PreviousStates;

            if (context.Rehearsal)
            {
                return(state
                       .SetState(ActivatedAccountsState.Address, MarkChanged)
                       .SetState(PendingAddress, MarkChanged));
            }

            if (!state.TryGetState(ActivatedAccountsState.Address, out Dictionary accountsAsDict))
            {
                throw new ActivatedAccountsDoesNotExistsException();
            }
            if (!state.TryGetState(PendingAddress, out Dictionary pendingAsDict))
            {
                throw new PendingActivationDoesNotExistsException(PendingAddress);
            }

            var accounts = new ActivatedAccountsState(accountsAsDict);
            var pending  = new PendingActivationState(pendingAsDict);

            if (pending.Verify(this))
            {
                return(state.SetState(
                           ActivatedAccountsState.Address,
                           accounts.AddAccount(context.Signer).Serialize()
                           ).SetState(
                           pending.address,
                           new Bencodex.Types.Null()
                           ));
            }
            else
            {
                throw new InvalidSignatureException(pending, Signature);
            }
        }
Exemple #16
0
        public InitializeStates(
            RankingState rankingState,
            ShopState shopState,
            Dictionary <string, string> tableSheets,
            GameConfigState gameConfigState,
            RedeemCodeState redeemCodeState,
            AdminState adminAddressState,
            ActivatedAccountsState activatedAccountsState,
            GoldCurrencyState goldCurrencyState,
            GoldDistribution[] goldDistributions,
            PendingActivationState[] pendingActivationStates,
            AuthorizedMinersState authorizedMinersState = null,
            CreditsState creditsState = null)
        {
            Ranking           = (Bencodex.Types.Dictionary)rankingState.Serialize();
            Shop              = (Bencodex.Types.Dictionary)shopState.Serialize();
            TableSheets       = tableSheets;
            GameConfig        = (Bencodex.Types.Dictionary)gameConfigState.Serialize();
            RedeemCode        = (Bencodex.Types.Dictionary)redeemCodeState.Serialize();
            AdminAddress      = (Bencodex.Types.Dictionary)adminAddressState.Serialize();
            ActivatedAccounts = (Bencodex.Types.Dictionary)activatedAccountsState.Serialize();
            GoldCurrency      = (Bencodex.Types.Dictionary)goldCurrencyState.Serialize();
            GoldDistributions = new Bencodex.Types.List(
                goldDistributions.Select(d => d.Serialize()).Cast <Bencodex.Types.IValue>()
                );
            PendingActivations = new Bencodex.Types.List(pendingActivationStates.Select(p => p.Serialize()));

            if (!(authorizedMinersState is null))
            {
                AuthorizedMiners = (Bencodex.Types.Dictionary)authorizedMinersState.Serialize();
            }

            if (!(creditsState is null))
            {
                Credits = (Bencodex.Types.Dictionary)creditsState.Serialize();
            }
        }
        public async Task ActivateAccount()
        {
            var adminPrivateKey = new PrivateKey();
            var adminAddress = adminPrivateKey.ToAddress();
            var activateAccounts = new[] { adminAddress }.ToImmutableHashSet();

            Block <PolymorphicAction <ActionBase> > genesis =
                BlockChain <PolymorphicAction <ActionBase> > .MakeGenesisBlock(
                    new PolymorphicAction <ActionBase>[]
            {
                new InitializeStates()
                {
                    RankingState     = new RankingState(),
                    ShopState        = new ShopState(),
                    TableSheetsState = new TableSheetsState(),
                    GameConfigState  = new GameConfigState(),
                    RedeemCodeState  = new RedeemCodeState(Bencodex.Types.Dictionary.Empty
                                                           .Add("address", RedeemCodeState.Address.Serialize())
                                                           .Add("map", Bencodex.Types.Dictionary.Empty)
                                                           ),
                    AdminAddressState      = new AdminState(adminAddress, 1500000),
                    ActivatedAccountsState = new ActivatedAccountsState(activateAccounts),
                    GoldCurrencyState      = new GoldCurrencyState(new Currency("NCG", minter: null)),
                    GoldDistributions      = new GoldDistribution[0],
                },
            }
                    );

            var apvPrivateKey = new PrivateKey();
            var apv            = AppProtocolVersion.Sign(apvPrivateKey, 0);
            var userPrivateKey = new PrivateKey();
            var properties     = new LibplanetNodeServiceProperties <PolymorphicAction <ActionBase> >
            {
                Host = System.Net.IPAddress.Loopback.ToString(),
                AppProtocolVersion   = apv,
                GenesisBlock         = genesis,
                StorePath            = null,
                StoreStatesCacheSize = 2,
                PrivateKey           = userPrivateKey,
                Port = null,
                MinimumDifficulty = 4096,
                NoMiner           = true,
                Render            = false,
                Peers             = ImmutableHashSet <Peer> .Empty,
                TrustedAppProtocolVersionSigners = null,
            };

            var service = new NineChroniclesNodeService(properties, null);

            service.PrivateKey = userPrivateKey;
            StandaloneContextFx.NineChroniclesNodeService = service;
            StandaloneContextFx.BlockChain = service.Swarm.BlockChain;

            var blockChain = StandaloneContextFx.BlockChain;

            var nonce      = new byte[] { 0x00, 0x01, 0x02, 0x03 };
            var privateKey = new PrivateKey();

            (ActivationKey activationKey, PendingActivationState pendingActivation) =
                ActivationKey.Create(privateKey, nonce);
            PolymorphicAction <ActionBase> action = new CreatePendingActivation(pendingActivation);

            blockChain.MakeTransaction(adminPrivateKey, new[] { action });
            await blockChain.MineBlock(adminAddress);

            var encodedActivationKey = activationKey.Encode();
            var queryResult          = await ExecuteQueryAsync(
                $"mutation {{ activationStatus {{ activateAccount(encodedActivationKey: \"{encodedActivationKey}\") }} }}");

            await blockChain.MineBlock(adminAddress);

            var result = (bool)queryResult.Data
                         .As <Dictionary <string, object> >()["activationStatus"]
                         .As <Dictionary <string, object> >()["activateAccount"];

            Assert.True(result);

            var state = (Bencodex.Types.Dictionary)blockChain.GetState(
                ActivatedAccountsState.Address);
            var activatedAccountsState = new ActivatedAccountsState(state);
            var userAddress            = userPrivateKey.ToAddress();

            Assert.True(activatedAccountsState.Accounts.Contains(userAddress));
        }
Exemple #18
0
        internal static TxPolicyViolationException ValidateNextBlockTxRaw(
            BlockChain <NCAction> blockChain,
            Transaction <NCAction> transaction,
            ImmutableHashSet <Address> allAuthorizedMiners)
        {
            // Avoid NRE when genesis block appended
            // Here, index is the index of a prospective block that transaction
            // will be included.
            long index = blockChain.Count > 0 ? blockChain.Tip.Index : 0;

            if (transaction.Actions.Count > 1)
            {
                return(new TxPolicyViolationException(
                           transaction.Id,
                           $"Transaction {transaction.Id} has too many actions: " +
                           $"{transaction.Actions.Count}"));
            }
            else if (IsObsolete(transaction, index))
            {
                return(new TxPolicyViolationException(
                           transaction.Id,
                           $"Transaction {transaction.Id} is obsolete."));
            }

            try
            {
                // Check if it is a no-op transaction to prove it's made by the authorized miner.
                if (IsAuthorizedMinerTransactionRaw(transaction, allAuthorizedMiners))
                {
                    // FIXME: This works under a strong assumption that any miner that was ever
                    // in a set of authorized miners can only create transactions without
                    // any actions.
                    return(transaction.Actions.Any()
                        ? new TxPolicyViolationException(
                               transaction.Id,
                               $"Transaction {transaction.Id} by an authorized miner should not " +
                               $"have any action: {transaction.Actions.Count}")
                        : null);
                }

                // Check ActivateAccount
                if (transaction.Actions.Count == 1 &&
                    transaction.Actions.First().InnerAction is IActivateAction aa)
                {
                    return(blockChain.GetState(aa.GetPendingAddress()) is Dictionary rawPending &&
                           new PendingActivationState(rawPending).Verify(aa.GetSignature())
                        ? null
                        : new TxPolicyViolationException(
                               transaction.Id,
                               $"Transaction {transaction.Id} has an invalid activate action."));
                }

                // Check admin
                if (IsAdminTransaction(blockChain, transaction))
                {
                    return(null);
                }

                switch (blockChain.GetState(transaction.Signer.Derive(ActivationKey.DeriveKey)))
                {
                case null:
                    // Fallback for pre-migration.
                    if (blockChain.GetState(ActivatedAccountsState.Address)
                        is Dictionary asDict)
                    {
                        IImmutableSet <Address> activatedAccounts =
                            new ActivatedAccountsState(asDict).Accounts;
                        return(!activatedAccounts.Any() ||
                               activatedAccounts.Contains(transaction.Signer)
                                ? null
                                : new TxPolicyViolationException(
                                   transaction.Id,
                                   $"Transaction {transaction.Id} is by a signer " +
                                   $"without account activation: {transaction.Signer}"));
                    }
                    return(null);

                case Bencodex.Types.Boolean _:
                    return(null);
                }

                return(null);
            }
            catch (InvalidSignatureException)
            {
                return(new TxPolicyViolationException(
                           transaction.Id,
                           $"Transaction {transaction.Id} has invalid signautre."));
            }
            catch (IncompleteBlockStatesException)
            {
                // It can be caused during `Swarm<T>.PreloadAsync()` because it doesn't fill its
                // state right away...
                // FIXME: It should be removed after fix that Libplanet fills its state on IBD.
                // See also: https://github.com/planetarium/lib9c/pull/151#discussion_r506039478
                return(null);
            }

            return(null);
        }