1
using System;
2
using System.Collections.Generic;
3
using System.Collections.Immutable;
4
using System.Linq;
5
using System.Security.Cryptography;
6
using Bencodex.Types;
7
using Libplanet.Action;
8
using Libplanet.Blocks;
9
using Libplanet.Tx;
10

11
namespace Libplanet.Store
12
{
13
    public static class StoreExtensions
14
    {
15
        /// <summary>
16
        /// Makes a store, <paramref name="to"/>, logically (but not necessarily physically)
17
        /// identical to another store, <paramref name="from"/>.  As this copies the contents
18
        /// of the store, instead of its physicall data, this can be used for migrating
19
        /// between two different types of <see cref="IStore"/> implementations.
20
        /// </summary>
21
        /// <param name="from">The store containing the source contents.</param>
22
        /// <param name="to">The store to contain the copied contents. Expected to be empty.</param>
23
        /// <exception cref="ArgumentException">Thrown when the store passed through
24
        /// <paramref name="to"/> is not empty.</exception>
25
        public static void Copy(this IStore from, IStore to)
26 2
        {
27 2
            if (from is IBlockStatesStore fromBlockStatesStore &&
28 2
                to is IBlockStatesStore toBlockStatesStore)
29 2
            {
30 2
                CopyBlockStateStore(from, to, fromBlockStatesStore, toBlockStatesStore);
31 2
            }
32
            else
33 0
            {
34 0
                throw new NotSupportedException(
35 0
                    $"The copy action is not supported from {from.GetType().FullName}" +
36 0
                    $" to {to.GetType().FullName}");
37
            }
38 2
        }
39

40
        private static void CopyBlockStateStore(
41
            IStore from,
42
            IStore to,
43
            IBlockStatesStore fromBlockStatesStore,
44
            IBlockStatesStore toBlockStatesStore)
45 2
        {
46
            // TODO: take a IProgress<> so that a caller can be aware the progress of cloning.
47 2
            if (to.ListChainIds().Any())
48 2
            {
49 2
                throw new ArgumentException("The destination store has to be empty.", nameof(to));
50
            }
51

52 2
            foreach (Guid chainId in from.ListChainIds().ToArray())
53 2
            {
54 2
                foreach (HashDigest<SHA256> blockHash in from.IterateIndexes(chainId))
55 2
                {
56 2
                    Block<NullAction> block = from.GetBlock<NullAction>(blockHash);
57 2
                    to.PutBlock(block);
58 2
                    IImmutableDictionary<string, IValue> states =
59 2
                        fromBlockStatesStore.GetBlockStates(blockHash);
60 2
                    toBlockStatesStore.SetBlockStates(blockHash, states);
61 2
                    to.AppendIndex(chainId, blockHash);
62 2
                }
63

64 2
                foreach (KeyValuePair<Address, long> kv in from.ListTxNonces(chainId))
65 0
                {
66 0
                    to.IncreaseTxNonce(chainId, kv.Key, kv.Value);
67 0
                }
68

69 2
                foreach (string key in fromBlockStatesStore.ListStateKeys(chainId))
70 0
                {
71 0
                    foreach (var pair in
72 0
                        fromBlockStatesStore.IterateStateReferences(chainId, key).Reverse())
73 0
                    {
74 0
                        pair.Deconstruct(out HashDigest<SHA256> refHash, out long refIndex);
75 0
                        toBlockStatesStore.StoreStateReference(
76 0
                            chainId,
77 0
                            ImmutableHashSet.Create(key),
78 0
                            refHash,
79 0
                            refIndex
80 0
                        );
81 0
                    }
82 0
                }
83 2
            }
84

85 2
            ImmutableHashSet<TxId> stagedTxIds =
86 2
                from.IterateStagedTransactionIds().ToImmutableHashSet();
87 2
            foreach (TxId txid in stagedTxIds)
88 0
            {
89 0
                to.PutTransaction(from.GetTransaction<NullAction>(txid));
90 0
            }
91

92 2
            to.StageTransactionIds(stagedTxIds);
93

94 2
            if (from.GetCanonicalChainId() is Guid canonId)
95 2
            {
96 2
                to.SetCanonicalChainId(canonId);
97 2
            }
98 2
        }
99

100
        /// <summary>
101
        /// An action implementation which does nothing for filling type parameter taking of
102
        /// <see cref="IAction"/>.
103
        /// </summary>
104
        private class NullAction : IAction
105
        {
106
            private IValue _value;
107

108
            /// <inheritdoc/>
109 0
            public IValue PlainValue => _value;
110

111
            /// <inheritdoc/>
112
            public IAccountStateDelta Execute(IActionContext context) =>
113 0
                context.PreviousStates;
114

115
            /// <inheritdoc/>
116
            public void LoadPlainValue(IValue plainValue)
117 0
            {
118 0
                _value = plainValue;
119 0
            }
120
        }
121
    }
122
}

Read our documentation on viewing source code .

Loading