├── 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 | ![FlowSharp](https://github.com/cliftonm/MerkleTree/blob/master/Article/demo1.png) 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 | --------------------------------------------------------------------------------