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() )); }
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(); }
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)); }
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); }
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); }
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"] ); }
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)); }
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); } }
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); }
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); } } ); }
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); } }
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)); }
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); }