├── Article
├── r1.png
├── r2.png
├── r3.png
├── r4.png
├── note.png
├── demo1.png
├── demo2.png
├── demo3.png
├── demo4.png
├── demo5.png
├── index.htm
├── smiley.png
├── tree1.png
├── tree2.png
├── tree3.png
├── tree4.png
├── firewall.png
├── important.png
├── key-small.png
├── unittest.png
├── unittests.png
├── consistency1.png
├── consistency2.png
├── consistency3.png
├── consistency4.png
├── consistency5.png
├── consistency6.png
├── consistency7.png
├── consistency8.png
├── consistency9.png
├── sad smiley.png
├── tree1-layout.xml
├── tree-3-layout.xml
├── tree-7-layout.xml
├── consistencyProofFlowchart.png
├── consistencyProofFlowchart-layout.xml
└── tree-3.fsd
├── various useful links.txt
├── MerkleTreeDemo
├── Libs
│ ├── Clifton.Core.dll
│ ├── FlowSharpLib.dll
│ ├── Newtonsoft.Json.dll
│ ├── websocket-sharp.dll
│ ├── FlowSharpService.dll
│ ├── FlowSharpRestService.dll
│ ├── FlowSharpCanvasService.dll
│ ├── FlowSharpToolboxService.dll
│ ├── FlowSharpWebSocketService.dll
│ ├── FlowSharpServiceInterfaces.dll
│ ├── FlowSharpCodeShapeInterfaces.dll
│ ├── Clifton.SemanticProcessorService.dll
│ ├── FlowSharpMouseControllerService.dll
│ └── Clifton.WinForm.ServiceInterfaces.dll
├── App.config
├── Properties
│ ├── Settings.settings
│ ├── Settings.Designer.cs
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ └── Resources.resx
├── bin
│ └── Debug
│ │ └── modules.xml
├── DemoMerkleTree.cs
├── Program.cs
├── DemoMerkleNode.cs
├── Bootstrap.cs
├── WebSocketHelpers.cs
├── ExtensionMethods.cs
├── Demo.resx
├── MerkleTreeDemo.csproj
├── Demo.cs
└── Demo.Designer.cs
├── App.config
├── Constants.cs
├── README.md
├── MerkleException.cs
├── MerkleProofHash.cs
├── LICENSE
├── Properties
└── AssemblyInfo.cs
├── MerkleTests
├── Properties
│ └── AssemblyInfo.cs
├── MerkleTests.csproj
└── NodeTests.cs
├── MerkleTree.sln
├── MerkleHash.cs
├── MerkleTree.csproj
├── why merkle trees.txt
├── .gitignore
├── MerkleNode.cs
└── MerkleTree.cs
/Article/r1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/r1.png
--------------------------------------------------------------------------------
/Article/r2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/r2.png
--------------------------------------------------------------------------------
/Article/r3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/r3.png
--------------------------------------------------------------------------------
/Article/r4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/r4.png
--------------------------------------------------------------------------------
/Article/note.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/note.png
--------------------------------------------------------------------------------
/Article/demo1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/demo1.png
--------------------------------------------------------------------------------
/Article/demo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/demo2.png
--------------------------------------------------------------------------------
/Article/demo3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/demo3.png
--------------------------------------------------------------------------------
/Article/demo4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/demo4.png
--------------------------------------------------------------------------------
/Article/demo5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/demo5.png
--------------------------------------------------------------------------------
/Article/index.htm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/index.htm
--------------------------------------------------------------------------------
/Article/smiley.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/smiley.png
--------------------------------------------------------------------------------
/Article/tree1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/tree1.png
--------------------------------------------------------------------------------
/Article/tree2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/tree2.png
--------------------------------------------------------------------------------
/Article/tree3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/tree3.png
--------------------------------------------------------------------------------
/Article/tree4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/tree4.png
--------------------------------------------------------------------------------
/Article/firewall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/firewall.png
--------------------------------------------------------------------------------
/Article/important.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/important.png
--------------------------------------------------------------------------------
/Article/key-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/key-small.png
--------------------------------------------------------------------------------
/Article/unittest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/unittest.png
--------------------------------------------------------------------------------
/Article/unittests.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/unittests.png
--------------------------------------------------------------------------------
/Article/consistency1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/consistency1.png
--------------------------------------------------------------------------------
/Article/consistency2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/consistency2.png
--------------------------------------------------------------------------------
/Article/consistency3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/consistency3.png
--------------------------------------------------------------------------------
/Article/consistency4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/consistency4.png
--------------------------------------------------------------------------------
/Article/consistency5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/consistency5.png
--------------------------------------------------------------------------------
/Article/consistency6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/consistency6.png
--------------------------------------------------------------------------------
/Article/consistency7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/consistency7.png
--------------------------------------------------------------------------------
/Article/consistency8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/consistency8.png
--------------------------------------------------------------------------------
/Article/consistency9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/consistency9.png
--------------------------------------------------------------------------------
/Article/sad smiley.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/sad smiley.png
--------------------------------------------------------------------------------
/Article/tree1-layout.xml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/tree1-layout.xml
--------------------------------------------------------------------------------
/various useful links.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/various useful links.txt
--------------------------------------------------------------------------------
/Article/tree-3-layout.xml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/tree-3-layout.xml
--------------------------------------------------------------------------------
/Article/tree-7-layout.xml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/tree-7-layout.xml
--------------------------------------------------------------------------------
/MerkleTreeDemo/Libs/Clifton.Core.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/MerkleTreeDemo/Libs/Clifton.Core.dll
--------------------------------------------------------------------------------
/MerkleTreeDemo/Libs/FlowSharpLib.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/MerkleTreeDemo/Libs/FlowSharpLib.dll
--------------------------------------------------------------------------------
/Article/consistencyProofFlowchart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/consistencyProofFlowchart.png
--------------------------------------------------------------------------------
/MerkleTreeDemo/Libs/Newtonsoft.Json.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/MerkleTreeDemo/Libs/Newtonsoft.Json.dll
--------------------------------------------------------------------------------
/MerkleTreeDemo/Libs/websocket-sharp.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/MerkleTreeDemo/Libs/websocket-sharp.dll
--------------------------------------------------------------------------------
/MerkleTreeDemo/Libs/FlowSharpService.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/MerkleTreeDemo/Libs/FlowSharpService.dll
--------------------------------------------------------------------------------
/Article/consistencyProofFlowchart-layout.xml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/Article/consistencyProofFlowchart-layout.xml
--------------------------------------------------------------------------------
/MerkleTreeDemo/Libs/FlowSharpRestService.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/MerkleTreeDemo/Libs/FlowSharpRestService.dll
--------------------------------------------------------------------------------
/MerkleTreeDemo/Libs/FlowSharpCanvasService.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/MerkleTreeDemo/Libs/FlowSharpCanvasService.dll
--------------------------------------------------------------------------------
/MerkleTreeDemo/Libs/FlowSharpToolboxService.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/MerkleTreeDemo/Libs/FlowSharpToolboxService.dll
--------------------------------------------------------------------------------
/MerkleTreeDemo/Libs/FlowSharpWebSocketService.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/MerkleTreeDemo/Libs/FlowSharpWebSocketService.dll
--------------------------------------------------------------------------------
/MerkleTreeDemo/Libs/FlowSharpServiceInterfaces.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/MerkleTreeDemo/Libs/FlowSharpServiceInterfaces.dll
--------------------------------------------------------------------------------
/MerkleTreeDemo/Libs/FlowSharpCodeShapeInterfaces.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/MerkleTreeDemo/Libs/FlowSharpCodeShapeInterfaces.dll
--------------------------------------------------------------------------------
/MerkleTreeDemo/Libs/Clifton.SemanticProcessorService.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/MerkleTreeDemo/Libs/Clifton.SemanticProcessorService.dll
--------------------------------------------------------------------------------
/MerkleTreeDemo/Libs/FlowSharpMouseControllerService.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/MerkleTreeDemo/Libs/FlowSharpMouseControllerService.dll
--------------------------------------------------------------------------------
/MerkleTreeDemo/Libs/Clifton.WinForm.ServiceInterfaces.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cliftonm/MerkleTree/HEAD/MerkleTreeDemo/Libs/Clifton.WinForm.ServiceInterfaces.dll
--------------------------------------------------------------------------------
/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/MerkleTreeDemo/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/MerkleTreeDemo/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Constants.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Marc Clifton
3 | * The Code Project Open License (CPOL) 1.02
4 | * http://www.codeproject.com/info/cpol10.aspx
5 | */
6 |
7 | namespace Clifton.Blockchain
8 | {
9 | public static class Constants
10 | {
11 | public const int HASH_LENGTH = 32;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MerkleTree
2 | A Merkle Tree implementation in C#
3 |
4 | # Documentation
5 | [Full Description](https://cdn.rawgit.com/cliftonm/MerkleTree/master/Article/indexWithToc.htm)
6 |
7 | # Explore Audit and Consistency Proofs
8 | 
9 |
--------------------------------------------------------------------------------
/MerkleException.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Marc Clifton
3 | * The Code Project Open License (CPOL) 1.02
4 | * http://www.codeproject.com/info/cpol10.aspx
5 | */
6 |
7 | using System;
8 |
9 | namespace Clifton.Blockchain
10 | {
11 | public class MerkleException : ApplicationException
12 | {
13 | public MerkleException(string msg) : base(msg)
14 | {
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/MerkleTreeDemo/bin/Debug/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/MerkleTreeDemo/DemoMerkleTree.cs:
--------------------------------------------------------------------------------
1 | using Clifton.Blockchain;
2 |
3 | namespace MerkleTreeDemo
4 | {
5 | public class DemoMerkleTree : MerkleTree
6 | {
7 | protected override MerkleNode CreateNode(MerkleHash hash)
8 | {
9 | return new DemoMerkleNode(hash);
10 | }
11 |
12 | protected override MerkleNode CreateNode(MerkleNode left, MerkleNode right)
13 | {
14 | return new DemoMerkleNode((DemoMerkleNode)left, (DemoMerkleNode)right);
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/MerkleTreeDemo/Program.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Marc Clifton
3 | * The Code Project Open License (CPOL) 1.02
4 | * http://www.codeproject.com/info/cpol10.aspx
5 | */
6 |
7 | using System;
8 | using System.Windows.Forms;
9 |
10 | namespace MerkleTreeDemo
11 | {
12 | static partial class Program
13 | {
14 | ///
15 | /// The main entry point for the application.
16 | ///
17 | [STAThread]
18 | static void Main()
19 | {
20 | Application.EnableVisualStyles();
21 | Application.SetCompatibleTextRenderingDefault(false);
22 | Bootstrap("modules.xml");
23 | Application.Run(new Demo());
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/MerkleProofHash.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Marc Clifton
3 | * The Code Project Open License (CPOL) 1.02
4 | * http://www.codeproject.com/info/cpol10.aspx
5 | */
6 |
7 | namespace Clifton.Blockchain
8 | {
9 | public class MerkleProofHash
10 | {
11 | public enum Branch
12 | {
13 | Left,
14 | Right,
15 | OldRoot, // used for linear list of hashes to compute the old root in a consistency proof.
16 | }
17 |
18 | public MerkleHash Hash { get; protected set; }
19 | public Branch Direction { get; protected set; }
20 |
21 | public MerkleProofHash(MerkleHash hash, Branch direction)
22 | {
23 | Hash = hash;
24 | Direction = direction;
25 | }
26 |
27 | public override string ToString()
28 | {
29 | return Hash.ToString();
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Marc Clifton
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MerkleTreeDemo/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace MerkleTreeDemo.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("MerkleTree")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("MerkleTree")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("358a7214-d45d-4743-99ed-02a81c4b8617")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/MerkleTests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("MerkleTests")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("MerkleTests")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("8f3cbd5b-032e-4c5e-be50-2488487cdde4")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/MerkleTreeDemo/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("MerkleTreeDemo")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("MerkleTreeDemo")]
13 | [assembly: AssemblyCopyright("Copyright © 2017")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("93f41679-c579-4fc4-adf2-8ead81814aae")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/MerkleTree.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25420.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MerkleTree", "MerkleTree.csproj", "{358A7214-D45D-4743-99ED-02A81C4B8617}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MerkleTests", "MerkleTests\MerkleTests.csproj", "{8F3CBD5B-032E-4C5E-BE50-2488487CDDE4}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MerkleTreeDemo", "MerkleTreeDemo\MerkleTreeDemo.csproj", "{93F41679-C579-4FC4-ADF2-8EAD81814AAE}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {358A7214-D45D-4743-99ED-02A81C4B8617}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {358A7214-D45D-4743-99ED-02A81C4B8617}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {358A7214-D45D-4743-99ED-02A81C4B8617}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {358A7214-D45D-4743-99ED-02A81C4B8617}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {8F3CBD5B-032E-4C5E-BE50-2488487CDDE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {8F3CBD5B-032E-4C5E-BE50-2488487CDDE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {8F3CBD5B-032E-4C5E-BE50-2488487CDDE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {8F3CBD5B-032E-4C5E-BE50-2488487CDDE4}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {93F41679-C579-4FC4-ADF2-8EAD81814AAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {93F41679-C579-4FC4-ADF2-8EAD81814AAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {93F41679-C579-4FC4-ADF2-8EAD81814AAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {93F41679-C579-4FC4-ADF2-8EAD81814AAE}.Release|Any CPU.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | EndGlobal
35 |
--------------------------------------------------------------------------------
/MerkleTreeDemo/DemoMerkleNode.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Marc Clifton
3 | * The Code Project Open License (CPOL) 1.02
4 | * http://www.codeproject.com/info/cpol10.aspx
5 | */
6 |
7 | using Clifton.Blockchain;
8 | using Clifton.Core.ExtensionMethods;
9 |
10 | namespace MerkleTreeDemo
11 | {
12 | public class DemoMerkleNode : MerkleNode
13 | {
14 | public string Text { get; set; } // Useful for diagramming.
15 | public object Tag { get; set; } // Useful for diagramming.
16 |
17 | public DemoMerkleNode(DemoMerkleNode left, DemoMerkleNode right = null) : base(left, right)
18 | {
19 | MergeText(left, right);
20 | }
21 |
22 | public DemoMerkleNode(MerkleHash hash)
23 | {
24 | Hash = hash;
25 | }
26 |
27 | public override string ToString()
28 | {
29 | // Useful for debugging, we use the node text if it exists, otherwise return the hash as a string.
30 | return Text ?? Hash.ToString();
31 | }
32 |
33 | public static DemoMerkleNode Create(string s)
34 | {
35 | return new DemoMerkleNode(MerkleHash.Create(s));
36 | }
37 |
38 | public MerkleNode SetText(string text)
39 | {
40 | Text = text;
41 |
42 | return this;
43 | }
44 |
45 | public MerkleNode SetTag(object tag)
46 | {
47 | Tag = tag;
48 |
49 | return this;
50 | }
51 |
52 | protected void MergeText(MerkleNode left, MerkleNode right)
53 | {
54 | // Useful for debugging, we combine the text of the two nodes.
55 | string text = (((DemoMerkleNode)left)?.Text ?? "") + (((DemoMerkleNode)right)?.Text ?? "");
56 |
57 | if (!string.IsNullOrEmpty(text))
58 | {
59 | if (right == null)
60 | {
61 | text = text.Parens();
62 | }
63 |
64 | Text = text;
65 | }
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/MerkleHash.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Marc Clifton
3 | * The Code Project Open License (CPOL) 1.02
4 | * http://www.codeproject.com/info/cpol10.aspx
5 | */
6 |
7 | using System;
8 | using System.Linq;
9 | using System.Security.Cryptography;
10 | using System.Text;
11 |
12 | namespace Clifton.Blockchain
13 | {
14 | public class MerkleHash
15 | {
16 | public byte[] Value { get; protected set; }
17 |
18 | protected MerkleHash()
19 | {
20 | }
21 |
22 | public static MerkleHash Create(byte[] buffer)
23 | {
24 | MerkleHash hash = new MerkleHash();
25 | hash.ComputeHash(buffer);
26 |
27 | return hash;
28 | }
29 |
30 | public static MerkleHash Create(string buffer)
31 | {
32 | return Create(Encoding.UTF8.GetBytes(buffer));
33 | }
34 |
35 | public static MerkleHash Create(MerkleHash left, MerkleHash right)
36 | {
37 | return Create(left.Value.Concat(right.Value).ToArray());
38 | }
39 |
40 | public static bool operator ==(MerkleHash h1, MerkleHash h2)
41 | {
42 | return h1.Equals(h2);
43 | }
44 |
45 | public static bool operator !=(MerkleHash h1, MerkleHash h2)
46 | {
47 | return !h1.Equals(h2);
48 | }
49 |
50 | public override int GetHashCode()
51 | {
52 | return base.GetHashCode();
53 | }
54 |
55 | public override bool Equals(object obj)
56 | {
57 | MerkleTree.Contract(() => obj is MerkleHash, "rvalue is not a MerkleHash");
58 | return Equals((MerkleHash)obj);
59 | }
60 |
61 | public override string ToString()
62 | {
63 | return BitConverter.ToString(Value).Replace("-", "");
64 | }
65 |
66 | public void ComputeHash(byte[] buffer)
67 | {
68 | SHA256 sha256 = SHA256.Create();
69 | SetHash(sha256.ComputeHash(buffer));
70 | }
71 |
72 | public void SetHash(byte[] hash)
73 | {
74 | MerkleTree.Contract(() => hash.Length == Constants.HASH_LENGTH, "Unexpected hash length.");
75 | Value = hash;
76 | }
77 |
78 | public bool Equals(byte[] hash)
79 | {
80 | return Value.SequenceEqual(hash);
81 | }
82 |
83 | public bool Equals(MerkleHash hash)
84 | {
85 | bool ret = false;
86 |
87 | if (((object)hash) != null)
88 | {
89 | ret = Value.SequenceEqual(hash.Value);
90 | }
91 |
92 | return ret;
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/MerkleTreeDemo/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace MerkleTreeDemo.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MerkleTreeDemo.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/MerkleTreeDemo/Bootstrap.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Marc Clifton
3 | * The Code Project Open License (CPOL) 1.02
4 | * http://www.codeproject.com/info/cpol10.aspx
5 | */
6 |
7 | using System;
8 | using System.Collections.Generic;
9 | using System.IO;
10 | using System.Linq;
11 | using System.Reflection;
12 | using System.Text;
13 | using System.Windows.Forms;
14 | using System.Xml.Linq;
15 |
16 | using Clifton.Core.Assertions;
17 | using Clifton.Core.ExtensionMethods;
18 | using Clifton.Core.Semantics;
19 | using Clifton.Core.ModuleManagement;
20 | using Clifton.Core.ServiceManagement;
21 |
22 | namespace MerkleTreeDemo
23 | {
24 | static partial class Program
25 | {
26 | public static ServiceManager ServiceManager;
27 |
28 | static void Bootstrap(string moduleFilename = "modules.xml")
29 | {
30 | ServiceManager = new ServiceManager();
31 | ServiceManager.RegisterSingleton();
32 |
33 | try
34 | {
35 | IModuleManager moduleMgr = (IModuleManager)ServiceManager.Get();
36 | List modules = GetModuleList(XmlFileName.Create(moduleFilename));
37 | moduleMgr.RegisterModules(modules);
38 | ServiceManager.FinishedInitialization();
39 | }
40 | catch(ReflectionTypeLoadException lex)
41 | {
42 | StringBuilder sb = new StringBuilder();
43 |
44 | foreach (Exception ex in lex.LoaderExceptions)
45 | {
46 | sb.AppendLine(ex.Message);
47 | }
48 |
49 | MessageBox.Show(sb.ToString(), "Initialization Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
50 | }
51 | catch (Exception ex)
52 | {
53 | MessageBox.Show(ex.Message + "\r\n" + ex.StackTrace, "Initialization Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
54 | }
55 | }
56 |
57 | ///
58 | /// Return the list of assembly names specified in the XML file so that
59 | /// we know what assemblies are considered modules as part of the application.
60 | ///
61 | static private List GetModuleList(XmlFileName filename)
62 | {
63 | Assert.That(File.Exists(filename.Value), "Module definition file " + filename.Value + " does not exist.");
64 | XDocument xdoc = XDocument.Load(filename.Value);
65 |
66 | return GetModuleList(xdoc);
67 | }
68 |
69 | ///
70 | /// Returns the list of modules specified in the XML document so we know what
71 | /// modules to instantiate.
72 | ///
73 | static private List GetModuleList(XDocument xdoc)
74 | {
75 | List assemblies = new List();
76 | (from module in xdoc.Element("Modules").Elements("Module")
77 | select module.Attribute("AssemblyName").Value).ForEach(s => assemblies.Add(AssemblyFileName.Create(s)));
78 |
79 | return assemblies;
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/MerkleTree.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {358A7214-D45D-4743-99ED-02A81C4B8617}
8 | Library
9 | Properties
10 | MerkleTree
11 | MerkleTree
12 | v4.6.1
13 | 512
14 | true
15 |
16 |
17 |
18 | AnyCPU
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 |
27 |
28 | AnyCPU
29 | pdbonly
30 | true
31 | bin\Release\
32 | TRACE
33 | prompt
34 | 4
35 |
36 |
37 |
38 |
39 |
40 |
41 | MerkleTreeDemo\Libs\Clifton.Core.dll
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
73 |
--------------------------------------------------------------------------------
/MerkleTreeDemo/WebSocketHelpers.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Marc Clifton
3 | * The Code Project Open License (CPOL) 1.02
4 | * http://www.codeproject.com/info/cpol10.aspx
5 | */
6 |
7 | using System.Collections.Generic;
8 | using System.Drawing;
9 | using System.Linq;
10 | using System.Net;
11 | using System.Net.Sockets;
12 |
13 | using WebSocketSharp;
14 |
15 | using FlowSharpLib;
16 |
17 | namespace MerkleTreeDemo
18 | {
19 | public class MyListener { }
20 |
21 | public static partial class WebSocketHelpers
22 | {
23 | private static WebSocket ws;
24 | private static string response;
25 |
26 | // cmd=CmdUpdateProperty&Name=btnTest&PropertyName=Text&Value=Foobar
27 | public static void UpdateProperty(string name, string propertyName, string value)
28 | {
29 | Connect();
30 | ws.Send(string.Format("cmd=CmdUpdateProperty&Name={0}&PropertyName={1}&Value={2}", name, propertyName, value));
31 | }
32 |
33 | public static void ClearCanvas()
34 | {
35 | Connect();
36 | ws.Send(string.Format("cmd=CmdClearCanvas"));
37 | }
38 |
39 | public static void DropShape(string shapeName, string name, int x, int y, string text = "")
40 | {
41 | Connect();
42 | ws.Send(string.Format("cmd=CmdDropShape&ShapeName={0}&Name={1}&X={2}&Y={3}&Text={4}", shapeName, x, y, text, name));
43 | }
44 |
45 | public static void DropShape(string shapeName, string name, Rectangle r, string text = "")
46 | {
47 | Connect();
48 | ws.Send(string.Format("cmd=CmdDropShape&ShapeName={0}&Name={1}&X={2}&Y={3}&Width={4}&Height={5}&Text={6}", shapeName, name, r.X, r.Y, r.Width, r.Height, text));
49 | }
50 |
51 | public static void DropShape(string shapeName, string name, Rectangle r, Color fillColor, string text = "")
52 | {
53 | Connect();
54 | ws.Send(string.Format("cmd=CmdDropShape&ShapeName={0}&Name={1}&X={2}&Y={3}&Width={4}&Height={5}&Text={6}&FillColor={7}", shapeName, name, r.X, r.Y, r.Width, r.Height, text, fillColor.ToHtmlColor('!')));
55 | }
56 |
57 | public static void DropShape(string shapeName, string name, int x, int y, int w, int h, string text = "")
58 | {
59 | Connect();
60 | ws.Send(string.Format("cmd=CmdDropShape&ShapeName={0}&Name={1}&X={2}&Y={3}&Width={4}&Height={5}&Text={6}", shapeName, name, x, y, w, h, text));
61 | }
62 |
63 | public static void DropConnector(string shapeName, string name, int x1, int y1, int x2, int y2)
64 | {
65 | Connect();
66 | ws.Send(string.Format("cmd=CmdDropConnector&ConnectorName={0}&Name={1}&X1={2}&Y1={3}&X2={4}&Y2={5}", shapeName, name, x1, y1, x2, y2));
67 | }
68 |
69 | private static void Connect()
70 | {
71 | if (ws == null || !ws.IsAlive)
72 | {
73 | // ws = new WebSocket("ws://192.168.1.165:1100/flowsharp", new MyListener());
74 | string localip = GetLocalHostIPs()[0].ToString();
75 | ws = new WebSocket("ws://" + localip + ":1100/flowsharp", new MyListener());
76 |
77 | ws.OnMessage += (sender, e) =>
78 | {
79 | response = e.Data;
80 | };
81 |
82 | ws.Connect();
83 | }
84 | }
85 |
86 | private static List GetLocalHostIPs()
87 | {
88 | IPHostEntry host;
89 | host = Dns.GetHostEntry(Dns.GetHostName());
90 | List ret = host.AddressList.Where(ip => ip.AddressFamily == AddressFamily.InterNetwork).ToList();
91 |
92 | return ret;
93 | }
94 |
95 | }
96 | }
--------------------------------------------------------------------------------
/why merkle trees.txt:
--------------------------------------------------------------------------------
1 | Thoughts:
2 |
3 | As the "client", you're downloading a large file, say 10GB, spread across peers in chuncks of 4096 bytes.
4 |
5 | You need a trusted authority server to tell you whether each block is valid.
6 |
7 | From the server, you can be given the root hash and, as each block comes in, you start
8 | filling in the Merkle tree yourself.
9 |
10 | However, you'll need all the data downloaded before you can complete the tree to verify the root.
11 |
12 | Instead, you can ask the server to send you the path of a hashed chunk (the leaf) to the root hash and verify it yourself. This enables you to verify each chunk and if it is faulty, request it from somewhere else, simultaneously while you are downloading other chunks. Furthermore, you're not maintaining all the hashes of the chunks and building the entire Merkle tree yourself. So it's faster for verifying a chunk, and it's less memory on your part to hold the entire Merkle tree.
13 |
14 | The server (as trusted authority) doesn't hold the data, but it has to have the Merkle tree, including leaf hashes.
15 |
16 | Q1: Why not just ask the server if the leaf exists in tree with the specific root?
17 | Q2: Why should the server even bother with storing a Merkle tree of the leaf hashes? (see answer to Q4 as well)
18 |
19 | A1: You could do that, but the SHA256 hash is only 32 bytes, and while this represents a huge number of unique hashes (2^256), 1.15*10^77, hashing node pair hashes as we walk up the tree strongly validates that the hash under question belongs to the correct tree.
20 |
21 | A2: Data synchronization:
22 |
23 | Let's say you have a large dataset divided into small chuncks. First, you may not be maintaining the entire dataset, but only the portion you are responsible for. If a change occurs in that chunk, you can recalculate the root hash knowing only the left-right branches to the root.
24 |
25 | The side-effect to this is that you also only need to maintain the left-right branches for your chunks, rather than the entire Merkle tree (including all the leaf hashes of chunks that you don't know or care about.)
26 |
27 | To synchronize, another user asks you to verify your root hash against their root hash. If different, the left/right child hashes are requested, and the process iterates the mismatches until the chunks on your end that have changed are identified. Now you only send your changed chunk to the other user, and you are both synchronized again.
28 |
29 | Q3: What happens if more than one user modifies the same chunk (based on the idea that in a distributed system, you want to replicate the data across many peers for resiliency.)
30 |
31 | A1: Only one peer may have the authority to change the data
32 | A2: Hashes can be time stamped, and you accept only the most recent change, discarding anything else
33 | A3: Differences are merged (either automatically or requiring manual intervention.)
34 | A4: You accept only the oldest change, and discard anything more recent
35 | A5: Technically, a chunk should never be changed. If a change is required, this should be logged as a new transaction so that there is an auditable trail of changes (not the same think as a Merkle audit). Given that, only specified authorities should be allowed to modify a transaction.
36 | A6: Some sort of blocking mechanism might be used to prevent data from being modified simultaneously. For example, you could be given a "change key" and when you submit the change, if your change key doesn't match the current change key, your change is rejected, requiring you to syncrhonize your data, acquire a new change key, and submit your changes again.
37 | A7: The salient point to this is that, if the chunk sizes are small, the likelihood that your change will collide with someone else's is small.
38 |
39 | Q4: Why should the server send you the hash audit trail of to verify a chunk, rather than just sending you a "yes/no" response?
40 |
41 | A: This is your way of verifying that the server hasn't been compromised -- if it simply said "yes", how would you know you can trust it? By sending you the left-right hashes, the server is telling you "here is how I verified your request." A server that is trying to get you to believe your chunk has is valid cannot send you "any" audit trail hash because that would give a different root hash.
42 |
43 |
--------------------------------------------------------------------------------
/MerkleTests/MerkleTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | {8F3CBD5B-032E-4C5E-BE50-2488487CDDE4}
7 | Library
8 | Properties
9 | MerkleTests
10 | MerkleTests
11 | v4.6.1
12 | 512
13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
14 | 10.0
15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
17 | False
18 | UnitTest
19 |
20 |
21 |
22 | true
23 | full
24 | false
25 | bin\Debug\
26 | DEBUG;TRACE
27 | prompt
28 | 4
29 |
30 |
31 | pdbonly
32 | true
33 | bin\Release\
34 | TRACE
35 | prompt
36 | 4
37 |
38 |
39 |
40 | False
41 | ..\MerkleTreeDemo\Libs\Clifton.Core.dll
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | {358a7214-d45d-4743-99ed-02a81c4b8617}
64 | MerkleTree
65 |
66 |
67 |
68 |
69 |
70 |
71 | False
72 |
73 |
74 | False
75 |
76 |
77 | False
78 |
79 |
80 | False
81 |
82 |
83 |
84 |
85 |
86 |
87 |
94 |
--------------------------------------------------------------------------------
/MerkleTreeDemo/ExtensionMethods.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Marc Clifton
3 | * The Code Project Open License (CPOL) 1.02
4 | * http://www.codeproject.com/info/cpol10.aspx
5 | */
6 |
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Drawing;
10 | using System.Linq;
11 |
12 | using Clifton.Core.ExtensionMethods;
13 |
14 | namespace FlowSharpLib
15 | {
16 | public static class ExtensionMethods
17 | {
18 | public static void Step2(this int n, int step, Action action)
19 | {
20 | for (int i = 0; i < n + step; i += step)
21 | {
22 | action(i);
23 | }
24 | }
25 |
26 | public static Point Delta(this Point p, Point p2)
27 | {
28 | return new Point(p.X - p2.X, p.Y - p2.Y);
29 | }
30 |
31 | public static Size Size(this Point p, Point p2)
32 | {
33 | return new Size(p.X - p2.X, p.Y - p2.Y);
34 | }
35 |
36 | public static Point Add(this Point p, Point p2)
37 | {
38 | return new Point(p.X + p2.X, p.Y + p2.Y);
39 | }
40 |
41 | public static int Sign(this int n)
42 | {
43 | return Math.Sign(n);
44 | }
45 |
46 | //public static int Min(this int a, int max)
47 | //{
48 | // return (a > max) ? max : a;
49 | //}
50 |
51 | public static int Max(this int a, int min)
52 | {
53 | return (a < min) ? min : a;
54 | }
55 |
56 | public static Rectangle Grow(this Rectangle r, float w)
57 | {
58 | Rectangle ret = r;
59 | ret.Inflate((int)w, (int)w);
60 |
61 | return ret;
62 | }
63 |
64 | public static Point Center(this Rectangle r)
65 | {
66 | return new Point(r.X + r.Width / 2, r.Y + r.Height / 2);
67 | }
68 |
69 | public static bool IsNear(this Point p1, Point p2, int range)
70 | {
71 | return (p1.X - p2.X).Abs() <= range && (p1.Y - p2.Y).Abs() <= range;
72 | }
73 |
74 | public static Rectangle Grow(this Rectangle r, float x, float y)
75 | {
76 | Rectangle ret = r;
77 | ret.Inflate((int)x, (int)y);
78 |
79 | return ret;
80 | }
81 |
82 | public static Rectangle Union(this Rectangle r, Rectangle r2)
83 | {
84 | return Rectangle.Union(r, r2);
85 | }
86 |
87 | ///
88 | /// Return a new rectangle whose position is adjusted by p.
89 | ///
90 | public static Rectangle Add(this Rectangle r, Point p)
91 | {
92 | Rectangle ret = new Rectangle(r.X + p.X, r.Y + p.Y, r.Width, r.Height);
93 |
94 | return ret;
95 | }
96 |
97 | public static Rectangle Move(this Rectangle r, Point p)
98 | {
99 | r.Offset(p);
100 |
101 | return r;
102 | }
103 |
104 | public static Rectangle Move(this Rectangle r, int x, int y)
105 | {
106 | r.Offset(x, y);
107 |
108 | return r;
109 | }
110 |
111 | public static Point Move(this Point r, Point p)
112 | {
113 | r.Offset(p);
114 |
115 | return r;
116 | }
117 |
118 | public static Point Move(this Point r, Size sz)
119 | {
120 | r.Offset(sz.Width, sz.Height);
121 |
122 | return r;
123 | }
124 |
125 | public static Point Move(this Point r, int x, int y)
126 | {
127 | r.Offset(x, y);
128 |
129 | return r;
130 | }
131 |
132 | public static Point ReverseDirection(this Point p)
133 | {
134 | return new Point(-p.X, -p.Y);
135 | }
136 |
137 | public static int to_i(this float f)
138 | {
139 | return (int)f;
140 | }
141 |
142 | public static bool In(this T item, T[] options)
143 | {
144 | return options.Contains(item);
145 | }
146 |
147 | public static void ForEachWithIndex(this IEnumerable collection, Action action)
148 | {
149 | int n = 0;
150 | collection.ForEach(t =>
151 | {
152 | action(n++, t);
153 | });
154 | }
155 |
156 | public static void ForEachReverse(this IList collection, Action action)
157 | {
158 | for (int i = collection.Count - 1; i >= 0; i--)
159 | {
160 | action(collection[i]);
161 | }
162 | }
163 |
164 | public static List Swap(this List list, int indexA, int indexB)
165 | {
166 | T tmp = list[indexA];
167 | list[indexA] = list[indexB];
168 | list[indexB] = tmp;
169 |
170 | return list;
171 | }
172 |
173 | public static string ToHtmlColor(this Color c, char prefix='#')
174 | {
175 | return prefix + c.R.ToString("X2") + c.G.ToString("X2") + c.B.ToString("X2");
176 | }
177 | }
178 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 | *.VC.VC.opendb
85 |
86 | # Visual Studio profiler
87 | *.psess
88 | *.vsp
89 | *.vspx
90 | *.sap
91 |
92 | # TFS 2012 Local Workspace
93 | $tf/
94 |
95 | # Guidance Automation Toolkit
96 | *.gpState
97 |
98 | # ReSharper is a .NET coding add-in
99 | _ReSharper*/
100 | *.[Rr]e[Ss]harper
101 | *.DotSettings.user
102 |
103 | # JustCode is a .NET coding add-in
104 | .JustCode
105 |
106 | # TeamCity is a build add-in
107 | _TeamCity*
108 |
109 | # DotCover is a Code Coverage Tool
110 | *.dotCover
111 |
112 | # NCrunch
113 | _NCrunch_*
114 | .*crunch*.local.xml
115 | nCrunchTemp_*
116 |
117 | # MightyMoose
118 | *.mm.*
119 | AutoTest.Net/
120 |
121 | # Web workbench (sass)
122 | .sass-cache/
123 |
124 | # Installshield output folder
125 | [Ee]xpress/
126 |
127 | # DocProject is a documentation generator add-in
128 | DocProject/buildhelp/
129 | DocProject/Help/*.HxT
130 | DocProject/Help/*.HxC
131 | DocProject/Help/*.hhc
132 | DocProject/Help/*.hhk
133 | DocProject/Help/*.hhp
134 | DocProject/Help/Html2
135 | DocProject/Help/html
136 |
137 | # Click-Once directory
138 | publish/
139 |
140 | # Publish Web Output
141 | *.[Pp]ublish.xml
142 | *.azurePubxml
143 | # TODO: Comment the next line if you want to checkin your web deploy settings
144 | # but database connection strings (with potential passwords) will be unencrypted
145 | *.pubxml
146 | *.publishproj
147 |
148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
149 | # checkin your Azure Web App publish settings, but sensitive information contained
150 | # in these scripts will be unencrypted
151 | PublishScripts/
152 |
153 | # NuGet Packages
154 | *.nupkg
155 | # The packages folder can be ignored because of Package Restore
156 | **/packages/*
157 | # except build/, which is used as an MSBuild target.
158 | !**/packages/build/
159 | # Uncomment if necessary however generally it will be regenerated when needed
160 | #!**/packages/repositories.config
161 | # NuGet v3's project.json files produces more ignoreable files
162 | *.nuget.props
163 | *.nuget.targets
164 |
165 | # Microsoft Azure Build Output
166 | csx/
167 | *.build.csdef
168 |
169 | # Microsoft Azure Emulator
170 | ecf/
171 | rcf/
172 |
173 | # Windows Store app package directories and files
174 | AppPackages/
175 | BundleArtifacts/
176 | Package.StoreAssociation.xml
177 | _pkginfo.txt
178 |
179 | # Visual Studio cache files
180 | # files ending in .cache can be ignored
181 | *.[Cc]ache
182 | # but keep track of directories ending in .cache
183 | !*.[Cc]ache/
184 |
185 | # Others
186 | ClientBin/
187 | ~$*
188 | *~
189 | *.dbmdl
190 | *.dbproj.schemaview
191 | *.pfx
192 | *.publishsettings
193 | node_modules/
194 | orleans.codegen.cs
195 |
196 | # Since there are multiple workflows, uncomment next line to ignore bower_components
197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
198 | #bower_components/
199 |
200 | # RIA/Silverlight projects
201 | Generated_Code/
202 |
203 | # Backup & report files from converting an old project file
204 | # to a newer Visual Studio version. Backup files are not needed,
205 | # because we have git ;-)
206 | _UpgradeReport_Files/
207 | Backup*/
208 | UpgradeLog*.XML
209 | UpgradeLog*.htm
210 |
211 | # SQL Server files
212 | *.mdf
213 | *.ldf
214 |
215 | # Business Intelligence projects
216 | *.rdl.data
217 | *.bim.layout
218 | *.bim_*.settings
219 |
220 | # Microsoft Fakes
221 | FakesAssemblies/
222 |
223 | # GhostDoc plugin setting file
224 | *.GhostDoc.xml
225 |
226 | # Node.js Tools for Visual Studio
227 | .ntvs_analysis.dat
228 |
229 | # Visual Studio 6 build log
230 | *.plg
231 |
232 | # Visual Studio 6 workspace options file
233 | *.opt
234 |
235 | # Visual Studio LightSwitch build output
236 | **/*.HTMLClient/GeneratedArtifacts
237 | **/*.DesktopClient/GeneratedArtifacts
238 | **/*.DesktopClient/ModelManifest.xml
239 | **/*.Server/GeneratedArtifacts
240 | **/*.Server/ModelManifest.xml
241 | _Pvt_Extensions
242 |
243 | # Paket dependency manager
244 | .paket/paket.exe
245 | paket-files/
246 |
247 | # FAKE - F# Make
248 | .fake/
249 |
250 | # JetBrains Rider
251 | .idea/
252 | *.sln.iml
253 |
--------------------------------------------------------------------------------
/MerkleTreeDemo/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/MerkleTreeDemo/Demo.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/MerkleNode.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Marc Clifton
3 | * The Code Project Open License (CPOL) 1.02
4 | * http://www.codeproject.com/info/cpol10.aspx
5 | */
6 |
7 | using System.Collections;
8 | using System.Collections.Generic;
9 | using System.Linq;
10 |
11 | using Clifton.Core.ExtensionMethods;
12 |
13 | namespace Clifton.Blockchain
14 | {
15 | public class MerkleNode : IEnumerable
16 | {
17 | public MerkleHash Hash { get; protected set; }
18 | public MerkleNode LeftNode { get; protected set; }
19 | public MerkleNode RightNode { get; protected set; }
20 | public MerkleNode Parent { get; protected set; }
21 |
22 | public bool IsLeaf { get { return LeftNode == null && RightNode == null; } }
23 |
24 | public MerkleNode()
25 | {
26 | }
27 |
28 | ///
29 | /// Constructor for a base node (leaf), representing the lowest level of the tree.
30 | ///
31 | public MerkleNode(MerkleHash hash)
32 | {
33 | Hash = hash;
34 | }
35 |
36 | ///
37 | /// Constructor for a parent node.
38 | ///
39 | public MerkleNode(MerkleNode left, MerkleNode right = null)
40 | {
41 | LeftNode = left;
42 | RightNode = right;
43 | LeftNode.Parent = this;
44 | RightNode.IfNotNull(r => r.Parent = this);
45 | ComputeHash();
46 | }
47 |
48 | public override string ToString()
49 | {
50 | return Hash.ToString();
51 | }
52 |
53 | IEnumerator IEnumerable.GetEnumerator()
54 | {
55 | return GetEnumerator();
56 | }
57 |
58 | public IEnumerator GetEnumerator()
59 | {
60 | foreach (var n in Iterate(this)) yield return n;
61 | }
62 |
63 | ///
64 | /// Bottom-up/left-right iteration of the tree.
65 | ///
66 | ///
67 | ///
68 | protected IEnumerable Iterate(MerkleNode node)
69 | {
70 | if (node.LeftNode != null)
71 | {
72 | foreach (var n in Iterate(node.LeftNode)) yield return n;
73 | }
74 |
75 | if (node.RightNode != null)
76 | {
77 | foreach (var n in Iterate(node.RightNode)) yield return n;
78 | }
79 |
80 | yield return node;
81 | }
82 |
83 | public MerkleHash ComputeHash(byte[] buffer)
84 | {
85 | Hash = MerkleHash.Create(buffer);
86 |
87 | return Hash;
88 | }
89 |
90 | ///
91 | /// Return the leaves (not all children, just leaves) under this node
92 | ///
93 | public IEnumerable Leaves()
94 | {
95 | return this.Where(n => n.LeftNode == null && n.RightNode == null);
96 | }
97 |
98 | public void SetLeftNode(MerkleNode node)
99 | {
100 | MerkleTree.Contract(() => node.Hash != null, "Node hash must be initialized.");
101 | LeftNode = node;
102 | LeftNode.Parent = this;
103 | ComputeHash();
104 | }
105 |
106 | public void SetRightNode(MerkleNode node)
107 | {
108 | MerkleTree.Contract(() => node.Hash != null, "Node hash must be initialized.");
109 | RightNode = node;
110 | RightNode.Parent = this;
111 |
112 | // Can't compute hash if the left node isn't set yet.
113 | if (LeftNode != null)
114 | {
115 | ComputeHash();
116 | }
117 | }
118 |
119 | ///
120 | /// True if we have enough data to verify our hash, particularly if we have child nodes.
121 | ///
122 | /// True if this node is a leaf or a branch with at least a left node.
123 | public bool CanVerifyHash()
124 | {
125 | return (LeftNode != null && RightNode != null) || (LeftNode != null);
126 | }
127 |
128 | ///
129 | /// Verifies the hash for this node against the computed hash for our child nodes.
130 | /// If we don't have any children, the return is always true because we have nothing to verify against.
131 | ///
132 | public bool VerifyHash()
133 | {
134 | if (LeftNode == null && RightNode == null)
135 | {
136 | return true;
137 | }
138 |
139 | if (RightNode == null)
140 | {
141 | return Hash.Equals(LeftNode.Hash);
142 | }
143 |
144 | MerkleTree.Contract(() => LeftNode != null, "Left branch must be a node if right branch is a node.");
145 | MerkleHash leftRightHash = MerkleHash.Create(LeftNode.Hash, RightNode.Hash);
146 |
147 | return Hash.Equals(leftRightHash);
148 | }
149 |
150 | ///
151 | /// If the hashes are equal, we know the entire node tree is equal.
152 | ///
153 | public bool Equals(MerkleNode node)
154 | {
155 | return Hash.Equals(node.Hash);
156 | }
157 |
158 | protected void ComputeHash()
159 | {
160 | // Repeat the left node if the right node doesn't exist.
161 | // This process breaks the case of doing a consistency check on 3 leaves when there are only 3 leaves in the tree.
162 | //MerkleHash rightHash = RightNode == null ? LeftNode.Hash : RightNode.Hash;
163 | //Hash = MerkleHash.Create(LeftNode.Hash.Value.Concat(rightHash.Value).ToArray());
164 |
165 | // Alternativately, do not repeat the left node, but carry the left node's hash up.
166 | // This process does not break the edge case described above.
167 | // We're implementing this version because the consistency check unit tests pass when we don't simulate
168 | // a right-hand node.
169 | Hash = RightNode == null ?
170 | LeftNode.Hash : //MerkleHash.Create(LeftNode.Hash.Value.Concat(LeftNode.Hash.Value).ToArray()) :
171 | MerkleHash.Create(LeftNode.Hash.Value.Concat(RightNode.Hash.Value).ToArray());
172 | Parent?.ComputeHash(); // Recurse, because out hash has changed.
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/MerkleTreeDemo/MerkleTreeDemo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {93F41679-C579-4FC4-ADF2-8EAD81814AAE}
8 | WinExe
9 | Properties
10 | MerkleTreeDemo
11 | MerkleTreeDemo
12 | v4.6.1
13 | 512
14 | true
15 |
16 |
17 |
18 | AnyCPU
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 |
27 |
28 | AnyCPU
29 | pdbonly
30 | true
31 | bin\Release\
32 | TRACE
33 | prompt
34 | 4
35 |
36 |
37 |
38 | Libs\Clifton.Core.dll
39 |
40 |
41 | Libs\Clifton.SemanticProcessorService.dll
42 |
43 |
44 | Libs\Clifton.WinForm.ServiceInterfaces.dll
45 |
46 |
47 | Libs\FlowSharpCanvasService.dll
48 |
49 |
50 | Libs\FlowSharpCodeShapeInterfaces.dll
51 |
52 |
53 | Libs\FlowSharpLib.dll
54 |
55 |
56 | Libs\FlowSharpMouseControllerService.dll
57 |
58 |
59 | Libs\FlowSharpRestService.dll
60 |
61 |
62 | Libs\FlowSharpService.dll
63 |
64 |
65 | Libs\FlowSharpServiceInterfaces.dll
66 |
67 |
68 | Libs\FlowSharpToolboxService.dll
69 |
70 |
71 | Libs\FlowSharpWebSocketService.dll
72 |
73 |
74 | Libs\Newtonsoft.Json.dll
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | Libs\websocket-sharp.dll
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | Form
99 |
100 |
101 | Demo.cs
102 |
103 |
104 |
105 |
106 |
107 | Demo.cs
108 |
109 |
110 | ResXFileCodeGenerator
111 | Resources.Designer.cs
112 | Designer
113 |
114 |
115 | True
116 | Resources.resx
117 | True
118 |
119 |
120 | SettingsSingleFileGenerator
121 | Settings.Designer.cs
122 |
123 |
124 | True
125 | Settings.settings
126 | True
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 | {358a7214-d45d-4743-99ed-02a81c4b8617}
135 | MerkleTree
136 |
137 |
138 |
139 |
140 |
141 |
142 |
149 |
--------------------------------------------------------------------------------
/MerkleTests/NodeTests.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Marc Clifton
3 | * The Code Project Open License (CPOL) 1.02
4 | * http://www.codeproject.com/info/cpol10.aspx
5 | */
6 |
7 | using System.Collections.Generic;
8 | using System.Text;
9 |
10 | using Microsoft.VisualStudio.TestTools.UnitTesting;
11 |
12 | using Clifton.Blockchain;
13 | using Clifton.Core.ExtensionMethods;
14 |
15 | namespace MerkleTests
16 | {
17 | [TestClass]
18 | public class NodeTests
19 | {
20 | [TestMethod]
21 | public void HashesAreSameTest()
22 | {
23 | MerkleHash h1 = MerkleHash.Create("abc");
24 | MerkleHash h2 = MerkleHash.Create("abc");
25 | Assert.IsTrue(h1 == h2);
26 | }
27 |
28 | [TestMethod]
29 | public void CreateNodeTest()
30 | {
31 | MerkleNode node = new MerkleNode();
32 | Assert.IsNull(node.Parent);
33 | Assert.IsNull(node.LeftNode);
34 | Assert.IsNull(node.RightNode);
35 | }
36 |
37 | ///
38 | /// Tests that after setting the left node, the parent hash verifies.
39 | ///
40 | [TestMethod]
41 | public void LeftHashVerificationTest()
42 | {
43 | MerkleNode parentNode = new MerkleNode();
44 | MerkleNode leftNode = new MerkleNode();
45 | leftNode.ComputeHash(Encoding.UTF8.GetBytes("abc"));
46 | parentNode.SetLeftNode(leftNode);
47 | Assert.IsTrue(parentNode.VerifyHash());
48 | }
49 |
50 | ///
51 | /// Tests that after setting both child nodes (left and right), the parent hash verifies.
52 | ///
53 | [TestMethod]
54 | public void LeftRightHashVerificationTest()
55 | {
56 | MerkleNode parentNode = CreateParentNode("abc", "def");
57 | Assert.IsTrue(parentNode.VerifyHash());
58 | }
59 |
60 | [TestMethod]
61 | public void NodesEqualTest()
62 | {
63 | MerkleNode parentNode1 = CreateParentNode("abc", "def");
64 | MerkleNode parentNode2 = CreateParentNode("abc", "def");
65 | Assert.IsTrue(parentNode1.Equals(parentNode2));
66 | }
67 |
68 | [TestMethod]
69 | public void NodesNotEqualTest()
70 | {
71 | MerkleNode parentNode1 = CreateParentNode("abc", "def");
72 | MerkleNode parentNode2 = CreateParentNode("def", "abc");
73 | Assert.IsFalse(parentNode1.Equals(parentNode2));
74 | }
75 |
76 | [TestMethod]
77 | public void VerifyTwoLevelTree()
78 | {
79 | MerkleNode parentNode1 = CreateParentNode("abc", "def");
80 | MerkleNode parentNode2 = CreateParentNode("123", "456");
81 | MerkleNode rootNode = new MerkleNode();
82 | rootNode.SetLeftNode(parentNode1);
83 | rootNode.SetRightNode(parentNode2);
84 | Assert.IsTrue(rootNode.VerifyHash());
85 | }
86 |
87 | [TestMethod]
88 | public void CreateBalancedTreeTest()
89 | {
90 | MerkleTree tree = new MerkleTree();
91 | tree.AppendLeaf(MerkleHash.Create("abc"));
92 | tree.AppendLeaf(MerkleHash.Create("def"));
93 | tree.AppendLeaf(MerkleHash.Create("123"));
94 | tree.AppendLeaf(MerkleHash.Create("456"));
95 | tree.BuildTree();
96 | Assert.IsNotNull(tree.RootNode);
97 | }
98 |
99 | [TestMethod]
100 | public void CreateUnbalancedTreeTest()
101 | {
102 | MerkleTree tree = new MerkleTree();
103 | tree.AppendLeaf(MerkleHash.Create("abc"));
104 | tree.AppendLeaf(MerkleHash.Create("def"));
105 | tree.AppendLeaf(MerkleHash.Create("123"));
106 | tree.BuildTree();
107 | Assert.IsNotNull(tree.RootNode);
108 | }
109 |
110 | // A Merkle audit path for a leaf in a Merkle Hash Tree is the shortest
111 | // list of additional nodes in the Merkle Tree required to compute the
112 | // Merkle Tree Hash for that tree.
113 | [TestMethod]
114 | public void AuditTest()
115 | {
116 | // Build a tree, and given the root node and a leaf hash, verify that the we can reconstruct the root hash.
117 | MerkleTree tree = new MerkleTree();
118 | MerkleHash l1 = MerkleHash.Create("abc");
119 | MerkleHash l2 = MerkleHash.Create("def");
120 | MerkleHash l3 = MerkleHash.Create("123");
121 | MerkleHash l4 = MerkleHash.Create("456");
122 | tree.AppendLeaves(new MerkleHash[] { l1, l2, l3, l4 });
123 | MerkleHash rootHash = tree.BuildTree();
124 |
125 | List auditTrail = tree.AuditProof(l1);
126 | Assert.IsTrue(MerkleTree.VerifyAudit(rootHash, l1, auditTrail));
127 |
128 | auditTrail = tree.AuditProof(l2);
129 | Assert.IsTrue(MerkleTree.VerifyAudit(rootHash, l2, auditTrail));
130 |
131 | auditTrail = tree.AuditProof(l3);
132 | Assert.IsTrue(MerkleTree.VerifyAudit(rootHash, l3, auditTrail));
133 |
134 | auditTrail = tree.AuditProof(l4);
135 | Assert.IsTrue(MerkleTree.VerifyAudit(rootHash, l4, auditTrail));
136 | }
137 |
138 | [TestMethod]
139 | public void FixingOddNumberOfLeavesByAddingTreeTest()
140 | {
141 | MerkleTree tree = new MerkleTree();
142 | MerkleHash l1 = MerkleHash.Create("abc");
143 | MerkleHash l2 = MerkleHash.Create("def");
144 | MerkleHash l3 = MerkleHash.Create("123");
145 | tree.AppendLeaves(new MerkleHash[] { l1, l2, l3 });
146 | MerkleHash rootHash = tree.BuildTree();
147 | tree.AddTree(new MerkleTree());
148 | MerkleHash rootHashAfterFix = tree.BuildTree();
149 | Assert.IsTrue(rootHash == rootHashAfterFix);
150 | }
151 |
152 | [TestMethod]
153 | public void FixingOddNumberOfLeavesManuallyTest()
154 | {
155 | MerkleTree tree = new MerkleTree();
156 | MerkleHash l1 = MerkleHash.Create("abc");
157 | MerkleHash l2 = MerkleHash.Create("def");
158 | MerkleHash l3 = MerkleHash.Create("123");
159 | tree.AppendLeaves(new MerkleHash[] { l1, l2, l3 });
160 | MerkleHash rootHash = tree.BuildTree();
161 | tree.FixOddNumberLeaves();
162 | MerkleHash rootHashAfterFix = tree.BuildTree();
163 | Assert.IsTrue(rootHash != rootHashAfterFix);
164 | }
165 |
166 | [TestMethod]
167 | public void AddTreeTest()
168 | {
169 | MerkleTree tree = new MerkleTree();
170 | MerkleHash l1 = MerkleHash.Create("abc");
171 | MerkleHash l2 = MerkleHash.Create("def");
172 | MerkleHash l3 = MerkleHash.Create("123");
173 | tree.AppendLeaves(new MerkleHash[] { l1, l2, l3 });
174 | MerkleHash rootHash = tree.BuildTree();
175 |
176 | MerkleTree tree2 = new MerkleTree();
177 | MerkleHash l5 = MerkleHash.Create("456");
178 | MerkleHash l6 = MerkleHash.Create("xyzzy");
179 | MerkleHash l7 = MerkleHash.Create("fizbin");
180 | MerkleHash l8 = MerkleHash.Create("foobar");
181 | tree2.AppendLeaves(new MerkleHash[] { l5, l6, l7, l8 });
182 | MerkleHash tree2RootHash = tree2.BuildTree();
183 | MerkleHash rootHashAfterAddTree = tree.AddTree(tree2);
184 |
185 | Assert.IsTrue(rootHash != rootHashAfterAddTree);
186 | }
187 |
188 | // Merkle consistency proofs prove the append-only property of the tree.
189 | [TestMethod]
190 | public void ConsistencyTest()
191 | {
192 | // Start with a tree with 2 leaves:
193 | MerkleTree tree = new MerkleTree();
194 | var startingNodes = tree.AppendLeaves(new MerkleHash[]
195 | {
196 | MerkleHash.Create("1"),
197 | MerkleHash.Create("2"),
198 | });
199 |
200 | // startingNodes.ForEachWithIndex((n, i) => n.Text = i.ToString());
201 |
202 | MerkleHash firstRoot = tree.BuildTree();
203 |
204 | List oldRoots = new List() { firstRoot };
205 |
206 | // Add a new leaf and verify that each time we add a leaf, we can get a consistency check
207 | // for all the previous leaves.
208 | for (int i = 2; i < 100; i++)
209 | {
210 | tree.AppendLeaf(MerkleHash.Create(i.ToString())); //.Text=i.ToString();
211 | tree.BuildTree();
212 |
213 | // After adding a leaf, verify that all the old root hashes exist.
214 | oldRoots.ForEachWithIndex((oldRootHash, n) =>
215 | {
216 | List proof = tree.ConsistencyProof(n+2);
217 | MerkleHash hash, lhash, rhash;
218 |
219 | if (proof.Count > 1)
220 | {
221 | lhash = proof[proof.Count - 2].Hash;
222 | int hidx = proof.Count - 1;
223 | hash = rhash = MerkleTree.ComputeHash(lhash, proof[hidx].Hash);
224 | hidx -= 2;
225 |
226 | while (hidx >= 0)
227 | {
228 | lhash = proof[hidx].Hash;
229 | hash = rhash = MerkleTree.ComputeHash(lhash, rhash);
230 |
231 | --hidx;
232 | }
233 | }
234 | else
235 | {
236 | hash = proof[0].Hash;
237 | }
238 |
239 | Assert.IsTrue(hash == oldRootHash, "Old root hash not found for index " + i + " m = " + (n+2).ToString());
240 |
241 | });
242 |
243 | // Then we add this root hash as the next old root hash to check.
244 | oldRoots.Add(tree.RootNode.Hash);
245 | }
246 | }
247 |
248 | private MerkleNode CreateParentNode(string leftData, string rightData)
249 | {
250 | MerkleNode parentNode = new MerkleNode();
251 | MerkleNode leftNode = new MerkleNode();
252 | MerkleNode rightNode = new MerkleNode();
253 | leftNode.ComputeHash(Encoding.UTF8.GetBytes(leftData));
254 | rightNode.ComputeHash(Encoding.UTF8.GetBytes(rightData));
255 | parentNode.SetLeftNode(leftNode);
256 | parentNode.SetRightNode(rightNode);
257 |
258 | return parentNode;
259 | }
260 | }
261 | }
262 |
--------------------------------------------------------------------------------
/MerkleTreeDemo/Demo.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Marc Clifton
3 | * The Code Project Open License (CPOL) 1.02
4 | * http://www.codeproject.com/info/cpol10.aspx
5 | */
6 |
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Drawing;
10 | using System.Linq;
11 | using System.Windows.Forms;
12 |
13 | using FlowSharpLib;
14 | using FlowSharpServiceInterfaces;
15 |
16 | using Clifton.Core.ExtensionMethods;
17 | using Clifton.Blockchain;
18 |
19 | namespace MerkleTreeDemo
20 | {
21 | public partial class Demo : Form
22 | {
23 | public const int LEAF_Y = 300;
24 | public const int V_OFFSET = 60;
25 | public const int X_OFFSET = 80;
26 | public const int LEAF_WIDTH = 75;
27 | public const int NODE_WIDTH = 95;
28 | public const int NODE_HEIGHT = 30;
29 | public const string CRLF = "\r\n";
30 | public readonly Color LEAF_COLOR = Color.LightGreen;
31 | public readonly Color NODE_COLOR = Color.LightBlue;
32 | public readonly Color ROOT_COLOR = ControlPaint.LightLight(Color.Red);
33 |
34 | public Demo()
35 | {
36 | InitializeComponent();
37 | InitializeFlowSharp();
38 | Shown += OnShown;
39 | }
40 |
41 | protected void InitializeFlowSharp()
42 | {
43 | var canvasService = Program.ServiceManager.Get();
44 | canvasService.CreateCanvas(pnlFlowSharp);
45 | canvasService.ActiveController.Canvas.EndInit();
46 | canvasService.ActiveController.Canvas.Invalidate();
47 |
48 | // Initialize Toolbox so we can drop shapes
49 | IFlowSharpToolboxService toolboxService = Program.ServiceManager.Get();
50 |
51 | // We don't display the toolbox, but we need a container.
52 | Panel pnlToolbox = new Panel();
53 | pnlToolbox.Visible = false;
54 | Controls.Add(pnlToolbox);
55 |
56 | toolboxService.CreateToolbox(pnlToolbox);
57 | toolboxService.InitializeToolbox();
58 |
59 | var mouseController = Program.ServiceManager.Get();
60 | mouseController.Initialize(canvasService.ActiveController);
61 | }
62 |
63 | private void OnShown(object sender, EventArgs e)
64 | {
65 | WebSocketHelpers.ClearCanvas();
66 | MerkleTree tree = new DemoMerkleTree();
67 | CreateTree(tree, (int)nudNumLeaves.Value);
68 | DrawTree(tree);
69 | }
70 |
71 | public void CreateTree(MerkleTree tree, int numLeaves)
72 | {
73 | List leaves = new List();
74 |
75 | for (int i = 0; i < numLeaves; i++)
76 | {
77 | tree.AppendLeaf(DemoMerkleNode.Create(i.ToString()).SetText(i.ToString("X")));
78 | }
79 |
80 | tree.BuildTree();
81 | }
82 |
83 | public void DrawTree(MerkleTree tree)
84 | {
85 | List leaves = leaves = tree.RootNode.Leaves().ToList();
86 | List shapesLower = DrawLeaves(leaves);
87 | IEnumerable parents = leaves.Select(l => l.Parent).Distinct();
88 |
89 | int level = 1;
90 |
91 | while (parents.Count() > 0)
92 | {
93 | List shapesUpper = DrawParents(parents, level++);
94 | DrawConnectors(shapesLower, shapesUpper);
95 | shapesLower = shapesUpper;
96 | parents = parents.Select(p => p?.Parent).Where(p=>p != null).Distinct();
97 | }
98 | }
99 |
100 | protected void DrawAuditProof(string text, MerkleTree tree)
101 | {
102 | // We use First because a tree with an odd number of leaves will duplicate the last leaf
103 | // when computing the hash.
104 | var node = tree.RootNode.Cast().First(t => t.Text == text);
105 | Highlight(node, "Orange");
106 | List proof = tree.AuditProof(node.Hash);
107 | bool ret = MerkleTree.VerifyAudit(tree.RootNode.Hash, node.Hash, proof);
108 | lblAuditPassFail.Text = ret ? "Pass" : "Fail";
109 | lblAuditPassFail.ForeColor = ret ? Color.Green : Color.Red;
110 | tbAuditTrail.Clear();
111 |
112 | foreach (var auditHash in proof)
113 | {
114 | // We use First because a tree with an odd number of leaves will duplicate the last leaf
115 | // when computing the hash.
116 | var n = tree.RootNode.Cast().First(t => t.Hash == auditHash.Hash);
117 | // tbAuditTrail.AppendText(n.Text + CRLF);
118 | Highlight(n);
119 | }
120 |
121 | var auditPairs = MerkleTree.AuditHashPairs(node.Hash, proof);
122 |
123 | foreach (var pair in auditPairs)
124 | {
125 | DemoMerkleNode left = tree.RootNode.Cast().First(t => t.Hash == pair.Item1);
126 | DemoMerkleNode right = tree.RootNode.Cast().First(t => t.Hash == pair.Item2);
127 | tbAuditTrail.AppendText(left.Text + " + " + right.Text + " = " + left.Text + right.Text + CRLF);
128 | }
129 | }
130 |
131 | protected void DrawConsistencyProof(MerkleTree tree, DemoMerkleNode oldTreeRoot, int m, MerkleHash newRootHash)
132 | {
133 | tree.RootNode.Leaves().Cast().Take(m).ForEach(n => Highlight(n, "Orange"));
134 |
135 | List proofToOldRoot = tree.ConsistencyProof(m);
136 | bool ret = MerkleTree.VerifyConsistency(oldTreeRoot.Hash, proofToOldRoot);
137 | lblConsistencyPassFail.Text = ret ? "Pass" : "Fail";
138 | lblConsistencyPassFail.ForeColor = ret ? Color.Green : Color.Red;
139 |
140 | tbConsistencyTrail.Clear();
141 |
142 | proofToOldRoot.ForEach(hash =>
143 | {
144 | var node = tree.RootNode.Cast().First(t => t.Hash == hash.Hash);
145 | Highlight(node);
146 | tbConsistencyTrail.AppendText(node.Text + CRLF);
147 | });
148 |
149 | tbConsistencyTrail.AppendText(oldTreeRoot.Text + " = old root" + CRLF);
150 |
151 | if (!ckOnlyToOldRoot.Checked)
152 | {
153 | // The remainder: consistency audit proof.
154 |
155 | var lastNode = proofToOldRoot.Last();
156 | List proofToNewRoot = tree.ConsistencyAuditProof(lastNode.Hash);
157 |
158 | foreach (var auditHash in proofToNewRoot)
159 | {
160 | // We use First because a tree with an odd number of leaves will duplicate the last leaf
161 | // when computing the hash.
162 | var n = tree.RootNode.Cast().First(t => t.Hash == auditHash.Hash);
163 | // tbAuditTrail.AppendText(n.Text + CRLF);
164 |
165 | if (!proofToOldRoot.Any(ph => ph.Hash == n.Hash))
166 | {
167 | Highlight(n, "Purple");
168 | }
169 | }
170 |
171 | var auditPairs = MerkleTree.AuditHashPairs(lastNode.Hash, proofToNewRoot);
172 | string lastHash = "";
173 |
174 | foreach (var pair in auditPairs)
175 | {
176 | var left = tree.RootNode.Cast().First(t => t.Hash == pair.Item1);
177 | var right = tree.RootNode.Cast().First(t => t.Hash == pair.Item2);
178 | lastHash = left.Text + right.Text;
179 | tbConsistencyTrail.AppendText(left.Text + " + " + right.Text + " = " + lastHash + CRLF);
180 | }
181 |
182 | tbConsistencyTrail.AppendText(lastHash + " = new root" + CRLF);
183 | }
184 | }
185 |
186 | protected List DrawLeaves(List leaves)
187 | {
188 | List rects = new List();
189 | int i = 0;
190 |
191 | foreach (var leaf in leaves.Cast())
192 | {
193 | var leafRect = new Rectangle(i * X_OFFSET, LEAF_Y, LEAF_WIDTH, NODE_HEIGHT);
194 | WebSocketHelpers.DropShape("Box", leaf.Text, leafRect, LEAF_COLOR, leaf.Text);
195 | rects.Add(leafRect);
196 | leaf.Tag = leafRect;
197 | ++i;
198 | }
199 |
200 | return rects;
201 | }
202 |
203 | protected List DrawParents(IEnumerable parents, int level)
204 | {
205 | List rects = new List();
206 | int i = 0;
207 | int l0 = level - 1;
208 | int indent;
209 | int spacing;
210 | int width = NODE_WIDTH;
211 |
212 | // Leaves and branches have different widths.
213 | if (level == 1)
214 | {
215 | indent = LEAF_WIDTH / 2 - 5;
216 | spacing = LEAF_WIDTH * 2 + 10;
217 | }
218 | else if (level == 2)
219 | {
220 | indent = ((int)Math.Pow(2, l0) - 1) * LEAF_WIDTH + LEAF_WIDTH / 2;
221 | spacing = NODE_WIDTH * (int)Math.Pow(2, level) - NODE_WIDTH / 2 - 13;
222 | }
223 | else if (level == 3)
224 | {
225 | indent = ((int)Math.Pow(2, l0) - 1) * LEAF_WIDTH + LEAF_WIDTH / 2 + 10;
226 | spacing = NODE_WIDTH * (int)Math.Pow(2, level) - NODE_WIDTH / 2 - 75;
227 | }
228 | else
229 | {
230 | indent = ((int)Math.Pow(2, l0) - 1) * LEAF_WIDTH + LEAF_WIDTH / 2 - 20;
231 | spacing = NODE_WIDTH * (int)Math.Pow(2, level) - NODE_WIDTH / 2 - 30;
232 | width = NODE_WIDTH * 2;
233 | }
234 |
235 | Color nodeColor = parents.Count() == 1 ? ROOT_COLOR : NODE_COLOR;
236 |
237 | foreach (var node in parents.Cast())
238 | {
239 | var nodeRect = new Rectangle(indent + i * spacing, LEAF_Y - (V_OFFSET * level), width, NODE_HEIGHT);
240 | WebSocketHelpers.DropShape("Box", node.Text, nodeRect, nodeColor, node.Text);
241 | rects.Add(nodeRect);
242 | node.Tag = nodeRect;
243 | ++i;
244 | }
245 |
246 | return rects;
247 | }
248 |
249 | protected void DrawConnectors(List shapesLower, List shapesUpper)
250 | {
251 | int n = 0;
252 | int n2 = 0;
253 |
254 | foreach (Rectangle rlower in shapesLower)
255 | {
256 | Rectangle rupper = shapesUpper[n2];
257 | var topMiddle = rlower.TopMiddle();
258 | var bottomMiddle = rupper.BottomMiddle();
259 | WebSocketHelpers.DropConnector("DynamicConnectorUD", "", bottomMiddle.X, bottomMiddle.Y, topMiddle.X, topMiddle.Y);
260 | n++;
261 | n2 = n2 + ((n % 2) == 0 ? 1 : 0);
262 | }
263 | }
264 |
265 | protected void Highlight(DemoMerkleNode node, string color = "Yellow")
266 | {
267 | Rectangle r = (Rectangle)node.Tag;
268 | WebSocketHelpers.UpdateProperty(node.Text, "FillColor", color);
269 | }
270 |
271 | private void NumLeavesChanged(object sender, EventArgs e)
272 | {
273 | WebSocketHelpers.ClearCanvas();
274 | MerkleTree tree = new DemoMerkleTree();
275 | CreateTree(tree, (int)nudNumLeaves.Value);
276 | DrawTree(tree);
277 |
278 | // Adjust maximums of our "proof" explorers.
279 | nudAuditProofNodeNumber.Maximum = nudNumLeaves.Value - 1;
280 | nudConsistencyProofNumLeaves.Maximum = nudNumLeaves.Value - 1;
281 | }
282 |
283 | private void btnAuditProof_Click(object sender, EventArgs e)
284 | {
285 | WebSocketHelpers.ClearCanvas();
286 | MerkleTree tree = new DemoMerkleTree();
287 | CreateTree(tree, (int)nudNumLeaves.Value);
288 | DrawTree(tree);
289 | int leafNum = (int)nudAuditProofNodeNumber.Value;
290 | Highlight(tree.RootNode.Leaves().Cast().Single(n=>n.Text == leafNum.ToString("X")), "Orange");
291 | DrawAuditProof(leafNum.ToString("X"), tree);
292 | }
293 |
294 | private void btnConsistencyProof_Click(object sender, EventArgs e)
295 | {
296 | WebSocketHelpers.ClearCanvas();
297 | MerkleTree tree = new DemoMerkleTree();
298 | CreateTree(tree, (int)nudNumLeaves.Value);
299 | DrawTree(tree);
300 | int numLeaves = (int)nudConsistencyProofNumLeaves.Value;
301 |
302 | // Reconstruct the old root hash by creating a tree of "m" leaves.
303 | // We do this because in the demo, we haven't given the user the ability to append trees, so we
304 | // simulate that process here.
305 | MerkleTree oldTree = new DemoMerkleTree();
306 | CreateTree(oldTree, numLeaves);
307 |
308 | // For demo purposes, remove any () that were created by left-only branches.
309 | ((DemoMerkleNode)oldTree.RootNode).Text = ((DemoMerkleNode)oldTree.RootNode).Text.Replace("(", "").Replace(")", "");
310 |
311 | DrawConsistencyProof(tree, (DemoMerkleNode)oldTree.RootNode, numLeaves, null);
312 | }
313 | }
314 | }
315 |
316 |
317 |
318 |
--------------------------------------------------------------------------------
/MerkleTreeDemo/Demo.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace MerkleTreeDemo
2 | {
3 | partial class Demo
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.pnlFlowSharp = new System.Windows.Forms.Panel();
32 | this.label1 = new System.Windows.Forms.Label();
33 | this.nudNumLeaves = new System.Windows.Forms.NumericUpDown();
34 | this.groupBox1 = new System.Windows.Forms.GroupBox();
35 | this.tbAuditTrail = new System.Windows.Forms.TextBox();
36 | this.lblAuditPassFail = new System.Windows.Forms.Label();
37 | this.btnAuditProof = new System.Windows.Forms.Button();
38 | this.nudAuditProofNodeNumber = new System.Windows.Forms.NumericUpDown();
39 | this.label2 = new System.Windows.Forms.Label();
40 | this.groupBox2 = new System.Windows.Forms.GroupBox();
41 | this.tbConsistencyTrail = new System.Windows.Forms.TextBox();
42 | this.lblConsistencyPassFail = new System.Windows.Forms.Label();
43 | this.btnConsistencyProof = new System.Windows.Forms.Button();
44 | this.nudConsistencyProofNumLeaves = new System.Windows.Forms.NumericUpDown();
45 | this.label4 = new System.Windows.Forms.Label();
46 | this.ckOnlyToOldRoot = new System.Windows.Forms.CheckBox();
47 | ((System.ComponentModel.ISupportInitialize)(this.nudNumLeaves)).BeginInit();
48 | this.groupBox1.SuspendLayout();
49 | ((System.ComponentModel.ISupportInitialize)(this.nudAuditProofNodeNumber)).BeginInit();
50 | this.groupBox2.SuspendLayout();
51 | ((System.ComponentModel.ISupportInitialize)(this.nudConsistencyProofNumLeaves)).BeginInit();
52 | this.SuspendLayout();
53 | //
54 | // pnlFlowSharp
55 | //
56 | this.pnlFlowSharp.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
57 | | System.Windows.Forms.AnchorStyles.Left)
58 | | System.Windows.Forms.AnchorStyles.Right)));
59 | this.pnlFlowSharp.Location = new System.Drawing.Point(282, 13);
60 | this.pnlFlowSharp.Name = "pnlFlowSharp";
61 | this.pnlFlowSharp.Size = new System.Drawing.Size(610, 475);
62 | this.pnlFlowSharp.TabIndex = 0;
63 | //
64 | // label1
65 | //
66 | this.label1.AutoSize = true;
67 | this.label1.Location = new System.Drawing.Point(13, 13);
68 | this.label1.Name = "label1";
69 | this.label1.Size = new System.Drawing.Size(97, 13);
70 | this.label1.TabIndex = 1;
71 | this.label1.Text = "Number of Leaves:";
72 | //
73 | // nudNumLeaves
74 | //
75 | this.nudNumLeaves.Location = new System.Drawing.Point(116, 11);
76 | this.nudNumLeaves.Maximum = new decimal(new int[] {
77 | 16,
78 | 0,
79 | 0,
80 | 0});
81 | this.nudNumLeaves.Minimum = new decimal(new int[] {
82 | 3,
83 | 0,
84 | 0,
85 | 0});
86 | this.nudNumLeaves.Name = "nudNumLeaves";
87 | this.nudNumLeaves.Size = new System.Drawing.Size(49, 20);
88 | this.nudNumLeaves.TabIndex = 2;
89 | this.nudNumLeaves.Value = new decimal(new int[] {
90 | 3,
91 | 0,
92 | 0,
93 | 0});
94 | this.nudNumLeaves.ValueChanged += new System.EventHandler(this.NumLeavesChanged);
95 | //
96 | // groupBox1
97 | //
98 | this.groupBox1.Controls.Add(this.tbAuditTrail);
99 | this.groupBox1.Controls.Add(this.lblAuditPassFail);
100 | this.groupBox1.Controls.Add(this.btnAuditProof);
101 | this.groupBox1.Controls.Add(this.nudAuditProofNodeNumber);
102 | this.groupBox1.Controls.Add(this.label2);
103 | this.groupBox1.Location = new System.Drawing.Point(16, 46);
104 | this.groupBox1.Name = "groupBox1";
105 | this.groupBox1.Size = new System.Drawing.Size(260, 201);
106 | this.groupBox1.TabIndex = 3;
107 | this.groupBox1.TabStop = false;
108 | this.groupBox1.Text = "Audit Proof";
109 | //
110 | // tbAuditTrail
111 | //
112 | this.tbAuditTrail.Location = new System.Drawing.Point(10, 73);
113 | this.tbAuditTrail.Multiline = true;
114 | this.tbAuditTrail.Name = "tbAuditTrail";
115 | this.tbAuditTrail.ReadOnly = true;
116 | this.tbAuditTrail.Size = new System.Drawing.Size(244, 115);
117 | this.tbAuditTrail.TabIndex = 7;
118 | //
119 | // lblAuditPassFail
120 | //
121 | this.lblAuditPassFail.AutoSize = true;
122 | this.lblAuditPassFail.Location = new System.Drawing.Point(203, 54);
123 | this.lblAuditPassFail.Name = "lblAuditPassFail";
124 | this.lblAuditPassFail.Size = new System.Drawing.Size(51, 13);
125 | this.lblAuditPassFail.TabIndex = 6;
126 | this.lblAuditPassFail.Text = "Pass/Fail";
127 | //
128 | // btnAuditProof
129 | //
130 | this.btnAuditProof.Location = new System.Drawing.Point(10, 44);
131 | this.btnAuditProof.Name = "btnAuditProof";
132 | this.btnAuditProof.Size = new System.Drawing.Size(70, 23);
133 | this.btnAuditProof.TabIndex = 5;
134 | this.btnAuditProof.Text = "Show Me";
135 | this.btnAuditProof.UseVisualStyleBackColor = true;
136 | this.btnAuditProof.Click += new System.EventHandler(this.btnAuditProof_Click);
137 | //
138 | // nudAuditProofNodeNumber
139 | //
140 | this.nudAuditProofNodeNumber.Location = new System.Drawing.Point(59, 18);
141 | this.nudAuditProofNodeNumber.Maximum = new decimal(new int[] {
142 | 2,
143 | 0,
144 | 0,
145 | 0});
146 | this.nudAuditProofNodeNumber.Name = "nudAuditProofNodeNumber";
147 | this.nudAuditProofNodeNumber.Size = new System.Drawing.Size(49, 20);
148 | this.nudAuditProofNodeNumber.TabIndex = 4;
149 | //
150 | // label2
151 | //
152 | this.label2.AutoSize = true;
153 | this.label2.Location = new System.Drawing.Point(7, 20);
154 | this.label2.Name = "label2";
155 | this.label2.Size = new System.Drawing.Size(41, 13);
156 | this.label2.TabIndex = 0;
157 | this.label2.Text = "Leaf #:";
158 | //
159 | // groupBox2
160 | //
161 | this.groupBox2.Controls.Add(this.ckOnlyToOldRoot);
162 | this.groupBox2.Controls.Add(this.tbConsistencyTrail);
163 | this.groupBox2.Controls.Add(this.lblConsistencyPassFail);
164 | this.groupBox2.Controls.Add(this.btnConsistencyProof);
165 | this.groupBox2.Controls.Add(this.nudConsistencyProofNumLeaves);
166 | this.groupBox2.Controls.Add(this.label4);
167 | this.groupBox2.Location = new System.Drawing.Point(16, 253);
168 | this.groupBox2.Name = "groupBox2";
169 | this.groupBox2.Size = new System.Drawing.Size(260, 235);
170 | this.groupBox2.TabIndex = 8;
171 | this.groupBox2.TabStop = false;
172 | this.groupBox2.Text = "Consistency Proof";
173 | //
174 | // tbConsistencyTrail
175 | //
176 | this.tbConsistencyTrail.Location = new System.Drawing.Point(10, 97);
177 | this.tbConsistencyTrail.Multiline = true;
178 | this.tbConsistencyTrail.Name = "tbConsistencyTrail";
179 | this.tbConsistencyTrail.ReadOnly = true;
180 | this.tbConsistencyTrail.Size = new System.Drawing.Size(244, 132);
181 | this.tbConsistencyTrail.TabIndex = 7;
182 | //
183 | // lblConsistencyPassFail
184 | //
185 | this.lblConsistencyPassFail.AutoSize = true;
186 | this.lblConsistencyPassFail.Location = new System.Drawing.Point(203, 75);
187 | this.lblConsistencyPassFail.Name = "lblConsistencyPassFail";
188 | this.lblConsistencyPassFail.Size = new System.Drawing.Size(51, 13);
189 | this.lblConsistencyPassFail.TabIndex = 6;
190 | this.lblConsistencyPassFail.Text = "Pass/Fail";
191 | //
192 | // btnConsistencyProof
193 | //
194 | this.btnConsistencyProof.Location = new System.Drawing.Point(10, 44);
195 | this.btnConsistencyProof.Name = "btnConsistencyProof";
196 | this.btnConsistencyProof.Size = new System.Drawing.Size(70, 23);
197 | this.btnConsistencyProof.TabIndex = 5;
198 | this.btnConsistencyProof.Text = "Show Me";
199 | this.btnConsistencyProof.UseVisualStyleBackColor = true;
200 | this.btnConsistencyProof.Click += new System.EventHandler(this.btnConsistencyProof_Click);
201 | //
202 | // nudConsistencyProofNumLeaves
203 | //
204 | this.nudConsistencyProofNumLeaves.Location = new System.Drawing.Point(69, 18);
205 | this.nudConsistencyProofNumLeaves.Maximum = new decimal(new int[] {
206 | 2,
207 | 0,
208 | 0,
209 | 0});
210 | this.nudConsistencyProofNumLeaves.Minimum = new decimal(new int[] {
211 | 1,
212 | 0,
213 | 0,
214 | 0});
215 | this.nudConsistencyProofNumLeaves.Name = "nudConsistencyProofNumLeaves";
216 | this.nudConsistencyProofNumLeaves.Size = new System.Drawing.Size(49, 20);
217 | this.nudConsistencyProofNumLeaves.TabIndex = 4;
218 | this.nudConsistencyProofNumLeaves.Value = new decimal(new int[] {
219 | 1,
220 | 0,
221 | 0,
222 | 0});
223 | //
224 | // label4
225 | //
226 | this.label4.AutoSize = true;
227 | this.label4.Location = new System.Drawing.Point(7, 20);
228 | this.label4.Name = "label4";
229 | this.label4.Size = new System.Drawing.Size(55, 13);
230 | this.label4.TabIndex = 0;
231 | this.label4.Text = "# Leaves:";
232 | //
233 | // ckOnlyToOldRoot
234 | //
235 | this.ckOnlyToOldRoot.AutoSize = true;
236 | this.ckOnlyToOldRoot.Location = new System.Drawing.Point(10, 74);
237 | this.ckOnlyToOldRoot.Name = "ckOnlyToOldRoot";
238 | this.ckOnlyToOldRoot.Size = new System.Drawing.Size(97, 17);
239 | this.ckOnlyToOldRoot.TabIndex = 8;
240 | this.ckOnlyToOldRoot.Text = "Only to old root";
241 | this.ckOnlyToOldRoot.UseVisualStyleBackColor = true;
242 | //
243 | // Demo
244 | //
245 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
246 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
247 | this.ClientSize = new System.Drawing.Size(904, 500);
248 | this.Controls.Add(this.groupBox2);
249 | this.Controls.Add(this.groupBox1);
250 | this.Controls.Add(this.nudNumLeaves);
251 | this.Controls.Add(this.label1);
252 | this.Controls.Add(this.pnlFlowSharp);
253 | this.Name = "Demo";
254 | this.Text = "Merkle Tree Demo";
255 | ((System.ComponentModel.ISupportInitialize)(this.nudNumLeaves)).EndInit();
256 | this.groupBox1.ResumeLayout(false);
257 | this.groupBox1.PerformLayout();
258 | ((System.ComponentModel.ISupportInitialize)(this.nudAuditProofNodeNumber)).EndInit();
259 | this.groupBox2.ResumeLayout(false);
260 | this.groupBox2.PerformLayout();
261 | ((System.ComponentModel.ISupportInitialize)(this.nudConsistencyProofNumLeaves)).EndInit();
262 | this.ResumeLayout(false);
263 | this.PerformLayout();
264 |
265 | }
266 |
267 | #endregion
268 |
269 | private System.Windows.Forms.Panel pnlFlowSharp;
270 | private System.Windows.Forms.Label label1;
271 | private System.Windows.Forms.NumericUpDown nudNumLeaves;
272 | private System.Windows.Forms.GroupBox groupBox1;
273 | private System.Windows.Forms.Button btnAuditProof;
274 | private System.Windows.Forms.NumericUpDown nudAuditProofNodeNumber;
275 | private System.Windows.Forms.Label label2;
276 | private System.Windows.Forms.TextBox tbAuditTrail;
277 | private System.Windows.Forms.Label lblAuditPassFail;
278 | private System.Windows.Forms.GroupBox groupBox2;
279 | private System.Windows.Forms.TextBox tbConsistencyTrail;
280 | private System.Windows.Forms.Label lblConsistencyPassFail;
281 | private System.Windows.Forms.Button btnConsistencyProof;
282 | private System.Windows.Forms.NumericUpDown nudConsistencyProofNumLeaves;
283 | private System.Windows.Forms.Label label4;
284 | private System.Windows.Forms.CheckBox ckOnlyToOldRoot;
285 | }
286 | }
287 |
288 |
--------------------------------------------------------------------------------
/MerkleTree.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) Marc Clifton
3 | * The Code Project Open License (CPOL) 1.02
4 | * http://www.codeproject.com/info/cpol10.aspx
5 | */
6 |
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Linq;
10 |
11 | using Clifton.Core.ExtensionMethods;
12 |
13 | namespace Clifton.Blockchain
14 | {
15 | public class MerkleTree
16 | {
17 | public MerkleNode RootNode { get; protected set; }
18 |
19 | protected List nodes;
20 | protected List leaves;
21 |
22 | public static void Contract(Func action, string msg)
23 | {
24 | if (!action())
25 | {
26 | throw new MerkleException(msg);
27 | }
28 | }
29 |
30 | public MerkleTree()
31 | {
32 | nodes = new List();
33 | leaves = new List();
34 | }
35 |
36 | public MerkleNode AppendLeaf(MerkleNode node)
37 | {
38 | nodes.Add(node);
39 | leaves.Add(node);
40 |
41 | return node;
42 | }
43 |
44 | public void AppendLeaves(MerkleNode[] nodes)
45 | {
46 | nodes.ForEach(n => AppendLeaf(n));
47 | }
48 |
49 | public MerkleNode AppendLeaf(MerkleHash hash)
50 | {
51 | var node = CreateNode(hash);
52 | nodes.Add(node);
53 | leaves.Add(node);
54 |
55 | return node;
56 | }
57 |
58 | public List AppendLeaves(MerkleHash[] hashes)
59 | {
60 | List nodes = new List();
61 | hashes.ForEach(h => nodes.Add(AppendLeaf(h)));
62 |
63 | return nodes;
64 | }
65 |
66 | public MerkleHash AddTree(MerkleTree tree)
67 | {
68 | Contract(() => leaves.Count > 0, "Cannot add to a tree with no leaves.");
69 | tree.leaves.ForEach(l => AppendLeaf(l));
70 |
71 | return BuildTree();
72 | }
73 |
74 | ///
75 | /// If we have an odd number of leaves, add a leaf that
76 | /// is a duplicate of the last leaf hash so that when we add the leaves of the new tree,
77 | /// we don't change the root hash of the current tree.
78 | /// This method should only be used if you have a specific reason that you need to balance
79 | /// the last node with it's right branch, for example as a pre-step to computing an audit trail
80 | /// on the last leaf of an odd number of leaves in the tree.
81 | ///
82 | public void FixOddNumberLeaves()
83 | {
84 | if ((leaves.Count & 1) == 1)
85 | {
86 | var lastLeaf = leaves.Last();
87 | var l = AppendLeaf(lastLeaf.Hash);
88 | // l.Text = lastLeaf.Text;
89 | }
90 | }
91 |
92 | ///
93 | /// Builds the tree for leaves and returns the root node.
94 | ///
95 | public MerkleHash BuildTree()
96 | {
97 | // We do not call FixOddNumberLeaves because we want the ability to append
98 | // leaves and add additional trees without creating unecessary wasted space in the tree.
99 | Contract(() => leaves.Count > 0, "Cannot build a tree with no leaves.");
100 | BuildTree(leaves);
101 |
102 | return RootNode.Hash;
103 | }
104 |
105 | // Why would we need this?
106 | //public void RegisterRoot(MerkleNode node)
107 | //{
108 | // Contract(() => node.Parent == null, "Node is not a root node.");
109 | // rootNode = node;
110 | //}
111 |
112 | ///
113 | /// Returns the audit proof hashes to reconstruct the root hash.
114 | ///
115 | /// The leaf hash we want to verify exists in the tree.
116 | /// The audit trail of hashes needed to create the root, or an empty list if the leaf hash doesn't exist.
117 | public List AuditProof(MerkleHash leafHash)
118 | {
119 | List auditTrail = new List();
120 |
121 | var leafNode = FindLeaf(leafHash);
122 |
123 | if (leafNode != null)
124 | {
125 | Contract(() => leafNode.Parent != null, "Expected leaf to have a parent.");
126 | var parent = leafNode.Parent;
127 | BuildAuditTrail(auditTrail, parent, leafNode);
128 | }
129 |
130 | return auditTrail;
131 | }
132 |
133 | ///
134 | /// Verifies ordering and consistency of the first n leaves, such that we reach the expected subroot.
135 | /// This verifies that the prior data has not been changed and that leaf order has been preserved.
136 | /// m is the number of leaves for which to do a consistency check.
137 | ///
138 | public List ConsistencyProof(int m)
139 | {
140 | // Rule 1:
141 | // Find the leftmost node of the tree from which we can start our consistency proof.
142 | // Set k, the number of leaves for this node.
143 | List hashNodes = new List();
144 | int idx = (int)Math.Log(m, 2);
145 |
146 | // Get the leftmost node.
147 | MerkleNode node = leaves[0];
148 |
149 | // Traverse up the tree until we get to the node specified by idx.
150 | while (idx > 0)
151 | {
152 | node = node.Parent;
153 | --idx;
154 | }
155 |
156 | int k = node.Leaves().Count();
157 | hashNodes.Add(new MerkleProofHash(node.Hash, MerkleProofHash.Branch.OldRoot));
158 |
159 | if (m == k)
160 | {
161 | // Continue with Rule 3 -- the remainder is the audit proof
162 | }
163 | else
164 | {
165 | // Rule 2:
166 | // Set the initial sibling node (SN) to the sibling of the node acquired by Rule 1.
167 | // if m-k == # of SN's leaves, concatenate the hash of the sibling SN and exit Rule 2, as this represents the hash of the old root.
168 | // if m - k < # of SN's leaves, set SN to SN's left child node and repeat Rule 2.
169 |
170 | // sibling node:
171 | MerkleNode sn = node.Parent.RightNode;
172 | bool traverseTree = true;
173 |
174 | while (traverseTree)
175 | {
176 | Contract(() => sn != null, "Sibling node must exist because m != k");
177 | int sncount = sn.Leaves().Count();
178 |
179 | if (m - k == sncount)
180 | {
181 | hashNodes.Add(new MerkleProofHash(sn.Hash, MerkleProofHash.Branch.OldRoot));
182 | break;
183 | }
184 |
185 | if (m - k > sncount)
186 | {
187 | hashNodes.Add(new MerkleProofHash(sn.Hash, MerkleProofHash.Branch.OldRoot));
188 | sn = sn.Parent.RightNode;
189 | k += sncount;
190 | }
191 | else // (m - k < sncount)
192 | {
193 | sn = sn.LeftNode;
194 | }
195 | }
196 | }
197 |
198 | // Rule 3: Apply ConsistencyAuditProof below.
199 |
200 | return hashNodes;
201 | }
202 |
203 | ///
204 | /// Completes the consistency proof with an audit proof using the last node in the consistency proof.
205 | ///
206 | public List ConsistencyAuditProof(MerkleHash nodeHash)
207 | {
208 | List auditTrail = new List();
209 |
210 | var node = RootNode.Single(n => n.Hash == nodeHash);
211 | var parent = node.Parent;
212 | BuildAuditTrail(auditTrail, parent, node);
213 |
214 | return auditTrail;
215 | }
216 |
217 | ///
218 | /// Verify that if we walk up the tree from a particular leaf, we encounter the expected root hash.
219 | ///
220 | public static bool VerifyAudit(MerkleHash rootHash, MerkleHash leafHash, List auditTrail)
221 | {
222 | Contract(() => auditTrail.Count > 0, "Audit trail cannot be empty.");
223 | MerkleHash testHash = leafHash;
224 |
225 | // TODO: Inefficient - compute hashes directly.
226 | foreach (MerkleProofHash auditHash in auditTrail)
227 | {
228 | testHash = auditHash.Direction == MerkleProofHash.Branch.Left ?
229 | MerkleHash.Create(testHash.Value.Concat(auditHash.Hash.Value).ToArray()) :
230 | MerkleHash.Create(auditHash.Hash.Value.Concat(testHash.Value).ToArray());
231 | }
232 |
233 | return rootHash == testHash;
234 | }
235 |
236 | ///
237 | /// For demo / debugging purposes, we return the pairs of hashes used to verify the audit proof.
238 | ///
239 | public static List> AuditHashPairs(MerkleHash leafHash, List auditTrail)
240 | {
241 | Contract(() => auditTrail.Count > 0, "Audit trail cannot be empty.");
242 | var auditPairs = new List>();
243 | MerkleHash testHash = leafHash;
244 |
245 | // TODO: Inefficient - compute hashes directly.
246 | foreach (MerkleProofHash auditHash in auditTrail)
247 | {
248 | switch (auditHash.Direction)
249 | {
250 | case MerkleProofHash.Branch.Left:
251 | auditPairs.Add(new Tuple(testHash, auditHash.Hash));
252 | testHash = MerkleHash.Create(testHash.Value.Concat(auditHash.Hash.Value).ToArray());
253 | break;
254 |
255 | case MerkleProofHash.Branch.Right:
256 | auditPairs.Add(new Tuple(auditHash.Hash, testHash));
257 | testHash = MerkleHash.Create(auditHash.Hash.Value.Concat(testHash.Value).ToArray());
258 | break;
259 | }
260 | }
261 |
262 | return auditPairs;
263 | }
264 |
265 | public static bool VerifyConsistency(MerkleHash oldRootHash, List proof)
266 | {
267 | MerkleHash hash, lhash, rhash;
268 |
269 | if (proof.Count > 1)
270 | {
271 | lhash = proof[proof.Count - 2].Hash;
272 | int hidx = proof.Count - 1;
273 | hash = rhash = MerkleTree.ComputeHash(lhash, proof[hidx].Hash);
274 | hidx -= 2;
275 |
276 | // foreach (var nextHashNode in proof.Skip(1))
277 | while (hidx >= 0)
278 | {
279 | lhash = proof[hidx].Hash;
280 | hash = rhash = MerkleTree.ComputeHash(lhash, rhash);
281 |
282 | --hidx;
283 | }
284 | }
285 | else
286 | {
287 | hash = proof[0].Hash;
288 | }
289 |
290 | return hash == oldRootHash;
291 | }
292 |
293 | public static MerkleHash ComputeHash(MerkleHash left, MerkleHash right)
294 | {
295 | return MerkleHash.Create(left.Value.Concat(right.Value).ToArray());
296 | }
297 |
298 | protected void BuildAuditTrail(List auditTrail, MerkleNode parent, MerkleNode child)
299 | {
300 | if (parent != null)
301 | {
302 | Contract(() => child.Parent == parent, "Parent of child is not expected parent.");
303 | var nextChild = parent.LeftNode == child ? parent.RightNode : parent.LeftNode;
304 | var direction = parent.LeftNode == child ? MerkleProofHash.Branch.Left : MerkleProofHash.Branch.Right;
305 |
306 | // For the last leaf, the right node may not exist. In that case, we ignore it because it's
307 | // the hash we are given to verify.
308 | if (nextChild != null)
309 | {
310 | auditTrail.Add(new MerkleProofHash(nextChild.Hash, direction));
311 | }
312 |
313 | BuildAuditTrail(auditTrail, child.Parent.Parent, child.Parent);
314 | }
315 | }
316 |
317 | protected MerkleNode FindLeaf(MerkleHash leafHash)
318 | {
319 | // TODO: We can improve the search for the leaf hash by maintaining a sorted list of leaf hashes.
320 | // We use First because a tree with an odd number of leaves will duplicate the last leaf
321 | // and will therefore have the same hash.
322 | return leaves.FirstOrDefault(l => l.Hash == leafHash);
323 | }
324 |
325 | ///
326 | /// Reduce the current list of n nodes to n/2 parents.
327 | ///
328 | ///
329 | protected void BuildTree(List nodes)
330 | {
331 | Contract(() => nodes.Count > 0, "node list not expected to be empty.");
332 |
333 | if (nodes.Count == 1)
334 | {
335 | RootNode = nodes[0];
336 | }
337 | else
338 | {
339 | List parents = new List();
340 |
341 | for (int i = 0; i < nodes.Count; i += 2)
342 | {
343 | MerkleNode right = (i + 1 < nodes.Count) ? nodes[i + 1] : null;
344 | // Constructing the MerkleNode resolves the right node being null.
345 | MerkleNode parent = CreateNode(nodes[i], right);
346 | parents.Add(parent);
347 | }
348 |
349 | BuildTree(parents);
350 | }
351 | }
352 |
353 | // Override in derived class to extend the behavior.
354 | // Alternatively, we could implement a factory pattern.
355 |
356 | protected virtual MerkleNode CreateNode(MerkleHash hash)
357 | {
358 | return new MerkleNode(hash);
359 | }
360 |
361 | protected virtual MerkleNode CreateNode(MerkleNode left, MerkleNode right)
362 | {
363 | return new MerkleNode(left, right);
364 | }
365 | }
366 | }
367 |
--------------------------------------------------------------------------------
/Article/tree-3.fsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 420
7 | 158
8 |
9 |
10 | 30
11 | 26
12 |
13 | 420
14 | 158
15 | 30
16 | 26
17 |
18 |
19 | 0
20 | 0
21 |
22 |
23 | 0
24 | 0
25 |
26 | 0
27 | 0
28 | 1
29 | None
30 | None
31 | 00000000-0000-0000-0000-000000000000
32 | 00000000-0000-0000-0000-000000000000
33 | Microsoft Sans Serif
34 | 12
35 | false
36 | false
37 | false
38 | true
39 | {}
40 |
41 |
42 | MiddleRight
43 | false
44 | -16777088
45 | -1
46 | -1
47 |
48 |
49 |
50 |
51 | 260
52 | 218
53 |
54 |
55 | 30
56 | 26
57 |
58 | 260
59 | 218
60 | 30
61 | 26
62 |
63 |
64 | 0
65 | 0
66 |
67 |
68 | 0
69 | 0
70 |
71 | 0
72 | 0
73 | 1
74 | None
75 | None
76 | 00000000-0000-0000-0000-000000000000
77 | 00000000-0000-0000-0000-000000000000
78 | Microsoft Sans Serif
79 | 12
80 | false
81 | false
82 | false
83 | true
84 | {}
85 |
86 |
87 | MiddleRight
88 | false
89 | -16777088
90 | -1
91 | -1
92 |
93 |
94 |
95 |
96 | 180
97 | 278
98 |
99 |
100 | 30
101 | 26
102 |
103 | 180
104 | 278
105 | 30
106 | 26
107 |
108 |
109 | 0
110 | 0
111 |
112 |
113 | 0
114 | 0
115 |
116 | 0
117 | 0
118 | 1
119 | None
120 | None
121 | 00000000-0000-0000-0000-000000000000
122 | 00000000-0000-0000-0000-000000000000
123 | Microsoft Sans Serif
124 | 12
125 | false
126 | false
127 | false
128 | true
129 | {}
130 |
131 |
132 | MiddleRight
133 | false
134 | -16777088
135 | -1
136 | -1
137 |
138 |
139 |
140 |
141 | 489
142 | 186
143 |
144 |
145 | 160
146 | 30
147 |
148 | 489
149 | 186
150 | 160
151 | 30
152 |
153 |
154 | 489
155 | 186
156 |
157 |
158 | 649
159 | 216
160 |
161 | 0
162 | 0
163 | 1
164 | None
165 | None
166 | 00000000-0000-0000-0000-000000000000
167 | 00000000-0000-0000-0000-000000000000
168 | Microsoft Sans Serif
169 | 10
170 | false
171 | false
172 | false
173 | false
174 | {}
175 |
176 |
177 | MiddleCenter
178 | false
179 | -16777216
180 | -16777216
181 | -1
182 |
183 |
184 |
185 |
186 | 329
187 | 186
188 |
189 |
190 | 160
191 | 30
192 |
193 | 329
194 | 186
195 | 160
196 | 30
197 |
198 |
199 | 489
200 | 186
201 |
202 |
203 | 329
204 | 216
205 |
206 | 0
207 | 0
208 | 1
209 | None
210 | None
211 | 00000000-0000-0000-0000-000000000000
212 | 00000000-0000-0000-0000-000000000000
213 | Microsoft Sans Serif
214 | 10
215 | false
216 | false
217 | false
218 | false
219 | {}
220 |
221 |
222 | MiddleCenter
223 | false
224 | -16777216
225 | -16777216
226 | -1
227 |
228 |
229 |
230 |
231 | 452
232 | 156
233 |
234 |
235 | 75
236 | 30
237 |
238 | 452
239 | 156
240 | 75
241 | 30
242 |
243 |
244 | 0
245 | 0
246 |
247 |
248 | 0
249 | 0
250 |
251 | 0
252 | 0
253 | 1
254 | None
255 | None
256 | 00000000-0000-0000-0000-000000000000
257 | 00000000-0000-0000-0000-000000000000
258 | Microsoft Sans Serif
259 | 10
260 | false
261 | false
262 | false
263 | false
264 | {}
265 |
266 |
267 | MiddleCenter
268 | false
269 | -16777216
270 | -16777216
271 | -32640
272 |
273 |
274 |
275 |
276 | 649
277 | 246
278 |
279 |
280 | 80
281 | 30
282 |
283 | 649
284 | 246
285 | 80
286 | 30
287 |
288 |
289 | 649
290 | 246
291 |
292 |
293 | 729
294 | 276
295 |
296 | 0
297 | 0
298 | 1
299 | None
300 | None
301 | 00000000-0000-0000-0000-000000000000
302 | 00000000-0000-0000-0000-000000000000
303 | Microsoft Sans Serif
304 | 10
305 | false
306 | false
307 | false
308 | false
309 | {}
310 |
311 |
312 | MiddleCenter
313 | false
314 | -16777216
315 | -16777216
316 | -1
317 |
318 |
319 |
320 |
321 | 569
322 | 246
323 |
324 |
325 | 80
326 | 30
327 |
328 | 569
329 | 246
330 | 80
331 | 30
332 |
333 |
334 | 649
335 | 246
336 |
337 |
338 | 569
339 | 276
340 |
341 | 0
342 | 0
343 | 1
344 | None
345 | None
346 | 00000000-0000-0000-0000-000000000000
347 | 00000000-0000-0000-0000-000000000000
348 | Microsoft Sans Serif
349 | 10
350 | false
351 | false
352 | false
353 | false
354 | {}
355 |
356 |
357 | MiddleCenter
358 | false
359 | -16777216
360 | -16777216
361 | -1
362 |
363 |
364 |
365 |
366 | 329
367 | 246
368 |
369 |
370 | 80
371 | 30
372 |
373 | 329
374 | 246
375 | 80
376 | 30
377 |
378 |
379 | 329
380 | 246
381 |
382 |
383 | 409
384 | 276
385 |
386 | 0
387 | 0
388 | 1
389 | None
390 | None
391 | 00000000-0000-0000-0000-000000000000
392 | 00000000-0000-0000-0000-000000000000
393 | Microsoft Sans Serif
394 | 10
395 | false
396 | false
397 | false
398 | false
399 | {}
400 |
401 |
402 | MiddleCenter
403 | false
404 | -16777216
405 | -16777216
406 | -1
407 |
408 |
409 |
410 |
411 | 249
412 | 246
413 |
414 |
415 | 80
416 | 30
417 |
418 | 249
419 | 246
420 | 80
421 | 30
422 |
423 |
424 | 329
425 | 246
426 |
427 |
428 | 249
429 | 276
430 |
431 | 0
432 | 0
433 | 1
434 | None
435 | None
436 | 00000000-0000-0000-0000-000000000000
437 | 00000000-0000-0000-0000-000000000000
438 | Microsoft Sans Serif
439 | 10
440 | false
441 | false
442 | false
443 | false
444 | {}
445 |
446 |
447 | MiddleCenter
448 | false
449 | -16777216
450 | -16777216
451 | -1
452 |
453 |
454 |
455 |
456 | 612
457 | 216
458 |
459 |
460 | 75
461 | 30
462 |
463 | 612
464 | 216
465 | 75
466 | 30
467 |
468 |
469 | 0
470 | 0
471 |
472 |
473 | 0
474 | 0
475 |
476 | 0
477 | 0
478 | 1
479 | None
480 | None
481 | 00000000-0000-0000-0000-000000000000
482 | 00000000-0000-0000-0000-000000000000
483 | Microsoft Sans Serif
484 | 10
485 | false
486 | false
487 | false
488 | false
489 | {}
490 |
491 |
492 | MiddleCenter
493 | false
494 | -16777216
495 | -16777216
496 | -5383962
497 |
498 |
499 |
500 |
501 | 292
502 | 216
503 |
504 |
505 | 75
506 | 30
507 |
508 | 292
509 | 216
510 | 75
511 | 30
512 |
513 |
514 | 0
515 | 0
516 |
517 |
518 | 0
519 | 0
520 |
521 | 0
522 | 0
523 | 1
524 | None
525 | None
526 | 00000000-0000-0000-0000-000000000000
527 | 00000000-0000-0000-0000-000000000000
528 | Microsoft Sans Serif
529 | 10
530 | false
531 | false
532 | false
533 | false
534 | {}
535 |
536 |
537 | MiddleCenter
538 | false
539 | -16777216
540 | -16777216
541 | -5383962
542 |
543 |
544 |
545 |
546 | 689
547 | 306
548 |
549 |
550 | 40
551 | 30
552 |
553 | 689
554 | 306
555 | 40
556 | 30
557 |
558 |
559 | 729
560 | 306
561 |
562 |
563 | 689
564 | 336
565 |
566 | 0
567 | 0
568 | 1
569 | None
570 | None
571 | 00000000-0000-0000-0000-000000000000
572 | 00000000-0000-0000-0000-000000000000
573 | Microsoft Sans Serif
574 | 10
575 | false
576 | false
577 | false
578 | false
579 | {}
580 |
581 |
582 | MiddleCenter
583 | false
584 | -16777216
585 | -16777216
586 | -1
587 |
588 |
589 |
590 |
591 | 569
592 | 306
593 |
594 |
595 | 40
596 | 30
597 |
598 | 569
599 | 306
600 | 40
601 | 30
602 |
603 |
604 | 569
605 | 306
606 |
607 |
608 | 609
609 | 336
610 |
611 | 0
612 | 0
613 | 1
614 | None
615 | None
616 | 00000000-0000-0000-0000-000000000000
617 | 00000000-0000-0000-0000-000000000000
618 | Microsoft Sans Serif
619 | 10
620 | false
621 | false
622 | false
623 | false
624 | {}
625 |
626 |
627 | MiddleCenter
628 | false
629 | -16777216
630 | -16777216
631 | -1
632 |
633 |
634 |
635 |
636 | 529
637 | 306
638 |
639 |
640 | 40
641 | 30
642 |
643 | 529
644 | 306
645 | 40
646 | 30
647 |
648 |
649 | 569
650 | 306
651 |
652 |
653 | 529
654 | 336
655 |
656 | 0
657 | 0
658 | 1
659 | None
660 | None
661 | 00000000-0000-0000-0000-000000000000
662 | 00000000-0000-0000-0000-000000000000
663 | Microsoft Sans Serif
664 | 10
665 | false
666 | false
667 | false
668 | false
669 | {}
670 |
671 |
672 | MiddleCenter
673 | false
674 | -16777216
675 | -16777216
676 | -1
677 |
678 |
679 |
680 |
681 | 409
682 | 306
683 |
684 |
685 | 40
686 | 30
687 |
688 | 409
689 | 306
690 | 40
691 | 30
692 |
693 |
694 | 409
695 | 306
696 |
697 |
698 | 449
699 | 336
700 |
701 | 0
702 | 0
703 | 1
704 | None
705 | None
706 | 00000000-0000-0000-0000-000000000000
707 | 00000000-0000-0000-0000-000000000000
708 | Microsoft Sans Serif
709 | 10
710 | false
711 | false
712 | false
713 | false
714 | {}
715 |
716 |
717 | MiddleCenter
718 | false
719 | -16777216
720 | -16777216
721 | -1
722 |
723 |
724 |
725 |
726 | 369
727 | 306
728 |
729 |
730 | 40
731 | 30
732 |
733 | 369
734 | 306
735 | 40
736 | 30
737 |
738 |
739 | 409
740 | 306
741 |
742 |
743 | 369
744 | 336
745 |
746 | 0
747 | 0
748 | 1
749 | None
750 | None
751 | 00000000-0000-0000-0000-000000000000
752 | 00000000-0000-0000-0000-000000000000
753 | Microsoft Sans Serif
754 | 10
755 | false
756 | false
757 | false
758 | false
759 | {}
760 |
761 |
762 | MiddleCenter
763 | false
764 | -16777216
765 | -16777216
766 | -1
767 |
768 |
769 |
770 |
771 | 249
772 | 306
773 |
774 |
775 | 40
776 | 30
777 |
778 | 249
779 | 306
780 | 40
781 | 30
782 |
783 |
784 | 249
785 | 306
786 |
787 |
788 | 289
789 | 336
790 |
791 | 0
792 | 0
793 | 1
794 | None
795 | None
796 | 00000000-0000-0000-0000-000000000000
797 | 00000000-0000-0000-0000-000000000000
798 | Microsoft Sans Serif
799 | 10
800 | false
801 | false
802 | false
803 | false
804 | {}
805 |
806 |
807 | MiddleCenter
808 | false
809 | -16777216
810 | -16777216
811 | -1
812 |
813 |
814 |
815 |
816 | 209
817 | 306
818 |
819 |
820 | 40
821 | 30
822 |
823 | 209
824 | 306
825 | 40
826 | 30
827 |
828 |
829 | 249
830 | 306
831 |
832 |
833 | 209
834 | 336
835 |
836 | 0
837 | 0
838 | 1
839 | None
840 | None
841 | 00000000-0000-0000-0000-000000000000
842 | 00000000-0000-0000-0000-000000000000
843 | Microsoft Sans Serif
844 | 10
845 | false
846 | false
847 | false
848 | false
849 | {}
850 |
851 |
852 | MiddleCenter
853 | false
854 | -16777216
855 | -16777216
856 | -1
857 |
858 |
859 |
860 |
861 | 692
862 | 276
863 |
864 |
865 | 75
866 | 30
867 |
868 | 692
869 | 276
870 | 75
871 | 30
872 |
873 |
874 | 0
875 | 0
876 |
877 |
878 | 0
879 | 0
880 |
881 | 0
882 | 0
883 | 1
884 | None
885 | None
886 | 00000000-0000-0000-0000-000000000000
887 | 00000000-0000-0000-0000-000000000000
888 | Microsoft Sans Serif
889 | 10
890 | false
891 | false
892 | false
893 | false
894 | {}
895 |
896 |
897 | MiddleCenter
898 | false
899 | -16777216
900 | -16777216
901 | -5383962
902 |
903 |
904 |
905 |
906 | 532
907 | 276
908 |
909 |
910 | 75
911 | 30
912 |
913 | 532
914 | 276
915 | 75
916 | 30
917 |
918 |
919 | 0
920 | 0
921 |
922 |
923 | 0
924 | 0
925 |
926 | 0
927 | 0
928 | 1
929 | None
930 | None
931 | 00000000-0000-0000-0000-000000000000
932 | 00000000-0000-0000-0000-000000000000
933 | Microsoft Sans Serif
934 | 10
935 | false
936 | false
937 | false
938 | false
939 | {}
940 |
941 |
942 | MiddleCenter
943 | false
944 | -16777216
945 | -16777216
946 | -5383962
947 |
948 |
949 |
950 |
951 | 372
952 | 276
953 |
954 |
955 | 75
956 | 30
957 |
958 | 372
959 | 276
960 | 75
961 | 30
962 |
963 |
964 | 0
965 | 0
966 |
967 |
968 | 0
969 | 0
970 |
971 | 0
972 | 0
973 | 1
974 | None
975 | None
976 | 00000000-0000-0000-0000-000000000000
977 | 00000000-0000-0000-0000-000000000000
978 | Microsoft Sans Serif
979 | 10
980 | false
981 | false
982 | false
983 | false
984 | {}
985 |
986 |
987 | MiddleCenter
988 | false
989 | -16777216
990 | -16777216
991 | -5383962
992 |
993 |
994 |
995 |
996 | 212
997 | 276
998 |
999 |
1000 | 75
1001 | 30
1002 |
1003 | 212
1004 | 276
1005 | 75
1006 | 30
1007 |
1008 |
1009 | 0
1010 | 0
1011 |
1012 |
1013 | 0
1014 | 0
1015 |
1016 | 0
1017 | 0
1018 | 1
1019 | None
1020 | None
1021 | 00000000-0000-0000-0000-000000000000
1022 | 00000000-0000-0000-0000-000000000000
1023 | Microsoft Sans Serif
1024 | 10
1025 | false
1026 | false
1027 | false
1028 | false
1029 | {}
1030 |
1031 |
1032 | MiddleCenter
1033 | false
1034 | -16777216
1035 | -16777216
1036 | -5383962
1037 |
1038 |
1039 |
1040 |
1041 | 652
1042 | 336
1043 |
1044 |
1045 | 75
1046 | 30
1047 |
1048 | 652
1049 | 336
1050 | 75
1051 | 30
1052 |
1053 |
1054 | 0
1055 | 0
1056 |
1057 |
1058 | 0
1059 | 0
1060 |
1061 | 0
1062 | 0
1063 | 1
1064 | None
1065 | None
1066 | 00000000-0000-0000-0000-000000000000
1067 | 00000000-0000-0000-0000-000000000000
1068 | Microsoft Sans Serif
1069 | 10
1070 | false
1071 | false
1072 | false
1073 | false
1074 | {}
1075 |
1076 |
1077 | MiddleCenter
1078 | false
1079 | -16777216
1080 | -16777216
1081 | -7278960
1082 |
1083 |
1084 |
1085 |
1086 | 572
1087 | 336
1088 |
1089 |
1090 | 75
1091 | 30
1092 |
1093 | 572
1094 | 336
1095 | 75
1096 | 30
1097 |
1098 |
1099 | 0
1100 | 0
1101 |
1102 |
1103 | 0
1104 | 0
1105 |
1106 | 0
1107 | 0
1108 | 1
1109 | None
1110 | None
1111 | 00000000-0000-0000-0000-000000000000
1112 | 00000000-0000-0000-0000-000000000000
1113 | Microsoft Sans Serif
1114 | 10
1115 | false
1116 | false
1117 | false
1118 | false
1119 | {}
1120 |
1121 |
1122 | MiddleCenter
1123 | false
1124 | -16777216
1125 | -16777216
1126 | -7278960
1127 |
1128 |
1129 |
1130 |
1131 | 492
1132 | 336
1133 |
1134 |
1135 | 75
1136 | 30
1137 |
1138 | 492
1139 | 336
1140 | 75
1141 | 30
1142 |
1143 |
1144 | 0
1145 | 0
1146 |
1147 |
1148 | 0
1149 | 0
1150 |
1151 | 0
1152 | 0
1153 | 1
1154 | None
1155 | None
1156 | 00000000-0000-0000-0000-000000000000
1157 | 00000000-0000-0000-0000-000000000000
1158 | Microsoft Sans Serif
1159 | 10
1160 | false
1161 | false
1162 | false
1163 | false
1164 | {}
1165 |
1166 |
1167 | MiddleCenter
1168 | false
1169 | -16777216
1170 | -16777216
1171 | -7278960
1172 |
1173 |
1174 |
1175 |
1176 | 412
1177 | 336
1178 |
1179 |
1180 | 75
1181 | 30
1182 |
1183 | 412
1184 | 336
1185 | 75
1186 | 30
1187 |
1188 |
1189 | 0
1190 | 0
1191 |
1192 |
1193 | 0
1194 | 0
1195 |
1196 | 0
1197 | 0
1198 | 1
1199 | None
1200 | None
1201 | 00000000-0000-0000-0000-000000000000
1202 | 00000000-0000-0000-0000-000000000000
1203 | Microsoft Sans Serif
1204 | 10
1205 | false
1206 | false
1207 | false
1208 | false
1209 | {}
1210 |
1211 |
1212 | MiddleCenter
1213 | false
1214 | -16777216
1215 | -16777216
1216 | -7278960
1217 |
1218 |
1219 |
1220 |
1221 | 332
1222 | 336
1223 |
1224 |
1225 | 75
1226 | 30
1227 |
1228 | 332
1229 | 336
1230 | 75
1231 | 30
1232 |
1233 |
1234 | 0
1235 | 0
1236 |
1237 |
1238 | 0
1239 | 0
1240 |
1241 | 0
1242 | 0
1243 | 1
1244 | None
1245 | None
1246 | 00000000-0000-0000-0000-000000000000
1247 | 00000000-0000-0000-0000-000000000000
1248 | Microsoft Sans Serif
1249 | 10
1250 | false
1251 | false
1252 | false
1253 | false
1254 | {}
1255 |
1256 |
1257 | MiddleCenter
1258 | false
1259 | -16777216
1260 | -16777216
1261 | -7278960
1262 |
1263 |
1264 |
1265 |
1266 | 252
1267 | 336
1268 |
1269 |
1270 | 75
1271 | 30
1272 |
1273 | 252
1274 | 336
1275 | 75
1276 | 30
1277 |
1278 |
1279 | 0
1280 | 0
1281 |
1282 |
1283 | 0
1284 | 0
1285 |
1286 | 0
1287 | 0
1288 | 1
1289 | None
1290 | None
1291 | 00000000-0000-0000-0000-000000000000
1292 | 00000000-0000-0000-0000-000000000000
1293 | Microsoft Sans Serif
1294 | 10
1295 | false
1296 | false
1297 | false
1298 | false
1299 | {}
1300 |
1301 |
1302 | MiddleCenter
1303 | false
1304 | -16777216
1305 | -16777216
1306 | -7278960
1307 |
1308 |
1309 |
1310 |
1311 | 172
1312 | 336
1313 |
1314 |
1315 | 75
1316 | 30
1317 |
1318 | 172
1319 | 336
1320 | 75
1321 | 30
1322 |
1323 |
1324 | 0
1325 | 0
1326 |
1327 |
1328 | 0
1329 | 0
1330 |
1331 | 0
1332 | 0
1333 | 1
1334 | None
1335 | None
1336 | 00000000-0000-0000-0000-000000000000
1337 | 00000000-0000-0000-0000-000000000000
1338 | Microsoft Sans Serif
1339 | 10
1340 | false
1341 | false
1342 | false
1343 | false
1344 | {}
1345 |
1346 |
1347 | MiddleCenter
1348 | false
1349 | -16777216
1350 | -16777216
1351 | -7278960
1352 |
1353 |
--------------------------------------------------------------------------------