diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
deleted file mode 100644
index 3d02d05d..00000000
--- a/.github/workflows/release.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-name: Release
-on:
- push:
- tags:
- - "v[0-9]+.[0-9]+.[0-9]+"
- - "v[0-9]+.[0-9]+.[0-9]+-rc.[0-9]+"
-
-permissions:
- contents: write
- packages: write
-
-jobs:
- release-binaries-github:
- name: Release Binaries to Github
- runs-on: ubuntu-20.04
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- - name: Make release
- run: |
- make release
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Clean release folder
- run: |
- sudo rm -rf dist
-
\ No newline at end of file
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 00000000..1eacb7ad
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,143 @@
+name: Test Workflow
+
+on:
+ pull_request:
+ branches:
+ - development
+ - main
+
+jobs:
+ test-linux:
+ name: "Test - Linux Environment"
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Setup Golang v1.21.9
+ uses: actions/setup-go@v5
+ with:
+ go-version: '1.21.9'
+
+ - name: Setup Python v3.11
+ uses: actions/setup-python@v5
+ with:
+ python-version: '3.11'
+
+ - name: Install test dependency
+ run: |
+ pip3 install requests
+
+ - name: Run tests
+ run: |
+ cd tests && ls && python3 -u run.py
+
+ - name: Packing Quorum and Non-Quorum node logs
+ if: always()
+ run: |
+ cd tests && python3 -u pack_node_logs.py
+
+ - name: Set Timestamp for Node Logs
+ if: always()
+ run: echo "TIMESTAMP=$(date -u +'%Y-%m-%dT%H-%M-%SZ' | sed 's/:/-/g')" >> $GITHUB_ENV
+
+ - name: Uploading Quorum and Non-Quorum node logs as Artifacts
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: node-logs-linux-${{ env.TIMESTAMP }}
+ path: tests/node_logs
+
+ test-macos:
+ name: "Test - MacOS Environment"
+ runs-on: macos-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Setup Golang v1.21.9
+ uses: actions/setup-go@v5
+ with:
+ go-version: '1.21.9'
+
+ - name: Setup Python v3.11
+ uses: actions/setup-python@v5
+ with:
+ python-version: '3.11'
+
+ - name: Install test dependency
+ run: |
+ pip3 install requests
+
+ - name: MacOS install tmux
+ run: brew install tmux
+
+ - name: Run tests
+ run: |
+ cd tests && ls && python3 -u run.py
+
+ - name: Packing Quorum and Non-Quorum node logs
+ if: always()
+ run: |
+ cd tests && python3 -u pack_node_logs.py
+
+ - name: Set Timestamp for Node Logs
+ if: always()
+ run: echo "TIMESTAMP=$(date -u +'%Y-%m-%dT%H-%M-%SZ' | sed 's/:/-/g')" >> $GITHUB_ENV
+
+ - name: Uploading Quorum and Non-Quorum node logs as Artifacts
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: node-logs-macos-${{ env.TIMESTAMP }}
+ path: tests/node_logs
+
+ test-windows:
+ name: "Test - Windows Environment"
+ runs-on: windows-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Setup Golang v1.21.9
+ uses: actions/setup-go@v5
+ with:
+ go-version: '1.21.9'
+
+ - name: Setup Python v3.11
+ uses: actions/setup-python@v5
+ with:
+ python-version: '3.11'
+
+ - name: Install test dependency
+ run: |
+ pip3 install requests
+
+ - name: Run tests
+ run: |
+ cd tests && ls && python3 -u run.py
+
+ - name: Packing Quorum and Non-Quorum node logs
+ if: always()
+ run: |
+ cd tests && python3 -u pack_node_logs.py
+
+ - name: Set Timestamp for Node Logs
+ if: always()
+ shell: pwsh
+ run: |
+ $timestamp = Get-Date -Format "yyyy-MM-ddTHH-mm-ssZ"
+ echo "TIMESTAMP=$timestamp" >> $env:GITHUB_ENV
+
+ - name: Uploading Quorum and Non-Quorum node logs as Artifacts
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: node-logs-windows-${{ env.TIMESTAMP }}
+ path: tests/node_logs
diff --git a/.gitignore b/.gitignore
index 58b5bf90..9f8b4b91 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,7 @@ api_config.json
*.json
*.pdf
.vscode
+__pycache__
# Dependency directories (remove the comment below to include it)
# vendor/
@@ -27,3 +28,6 @@ linux/
mac/
windows/
dist/
+
+# used for testing purpose
+!node_registry.json
\ No newline at end of file
diff --git a/.goreleaser.yaml b/.goreleaser.yaml
deleted file mode 100644
index 0df6513e..00000000
--- a/.goreleaser.yaml
+++ /dev/null
@@ -1,104 +0,0 @@
-project_name: rubixgoplatform
-
-env:
- - CGO_ENABLED=1
-
-before:
- hooks:
- - go mod tidy -compat=1.20
-
-builds:
-
- - id: ubuntu-amd64
- main: ./
- binary: rubixgoplatform
- env:
- - CC=x86_64-linux-gnu-gcc
- goos:
- - linux
- goarch:
- - amd64
-
- - id: ubuntu-arm64
- main: ./
- binary: rubixgoplatform
- env:
- - CC=aarch64-linux-gnu-gcc
- goos:
- - linux
- goarch:
- - arm64
-
- - id: darwin-amd64
- main: ./
- binary: rubixgoplatform
- env:
- - CC=o64-clang
- - CGO_LDFLAGS=-L/lib
- goos:
- - darwin
- goarch:
- - amd64
-
- - id: darwin-arm64
- main: ./
- binary: rubixgoplatform
- env:
- - CC=oa64-clang
- - CGO_LDFLAGS=-L/lib
- goos:
- - darwin
- goarch:
- - arm64
-
- - id: windows-amd64
- main: ./
- binary: rubixgoplatform
- env:
- - CC=x86_64-w64-mingw32-gcc
- goos:
- - windows
- goarch:
- - amd64
-
-archives:
- - id: linux-darwin-archive
- builds:
- - ubuntu-amd64
- - ubuntu-arm64
- - darwin-amd64
- - darwin-arm64
- format: tar.gz
- wrap_in_directory: false
- name_template: "{{ .Binary }}-v{{ .Version }}-{{ .Os }}-{{ .Arch }}"
-
- - id: windows-archive
- builds:
- - windows-amd64
- format: zip
- wrap_in_directory: false
- name_template: "{{ .Binary }}-v{{ .Version }}-{{ .Os }}-{{ .Arch }}"
-
-checksum:
- name_template: 'checksums.txt'
- algorithm: sha256
-
-snapshot:
- name_template: "{{ incpatch .Version }}-next"
-
-changelog:
- sort: asc
- filters:
- exclude:
- - '^docs:'
- - '^test:'
-
-release:
- github:
- owner: rubixchain
- name: rubixgoplatform
-
- draft: false
- mode: append
- header: |
- # Release Notes
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index f288702d..00000000
--- a/LICENSE
+++ /dev/null
@@ -1,674 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- Copyright (C)
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-.
diff --git a/Makefile b/Makefile
index 1972f840..87d463d6 100644
--- a/Makefile
+++ b/Makefile
@@ -19,39 +19,4 @@ compile-mac:
clean:
rm -f linux/rubixgoplatform windows/rubixgoplatform.exe mac/rubixgoplatform
-all: compile-linux compile-windows compile-mac
-
-############################ Release ##################################
-
-GORELEASER_VERSION := 1.20
-GORELEASER_IMAGE := ghcr.io/goreleaser/goreleaser-cross:v$(GORELEASER_VERSION)
-
-# Publish binaries to Gitbub. It is used in `release` Github Action Workflow
-ifdef GITHUB_TOKEN
-release:
- docker run \
- --rm \
- -e GITHUB_TOKEN=$(GITHUB_TOKEN) \
- -v /var/run/docker.sock:/var/run/docker.sock \
- -v `pwd`:/go/src/rubixgoplatform \
- -w /go/src/rubixgoplatform \
- $(GORELEASER_IMAGE) \
- release \
- --clean
-else
-release:
- @echo "Error: GITHUB_TOKEN is not defined. Please define it before running 'make release'."
-endif
-
-# Generate binaries in local
-release-dry-run:
- docker run \
- --rm \
- -v /var/run/docker.sock:/var/run/docker.sock \
- -v `pwd`:/go/src/rubixgoplatform \
- -w /go/src/rubixgoplatform \
- $(GORELEASER_IMAGE) \
- release \
- --clean \
- --skip-publish \
- --skip-validate
+all: compile-linux compile-windows compile-mac
\ No newline at end of file
diff --git a/README.md b/README.md
index 7817a9d8..f457f546 100644
--- a/README.md
+++ b/README.md
@@ -102,7 +102,7 @@ This following options are used for this command
-port string
Server/Host port (default "20000")
-didType int
- DID type (0-Basic Mode, 1-Standard Mode, 2-Wallet Mode) (default 0)
+ DID type (0-Basic Mode, 1-Standard Mode, 2-Wallet Mode, 3-Child Mode, 4-Light Mode) (default 0)
-didSecret string
DID secret (default "My DID Secret")
-privPWD string
@@ -121,8 +121,14 @@ This following options are used for this command
DID private key file name (default "pvtKey.pem")
-pubKeyFile string
DID public key file name (default "pubKey.pem")
+ -mnemonicKeyFile string
+ Mnemonic key file (default "mnemonic.txt")
+ -ChildPath int
+ BIP Child Path (default 0)
-fp forcepassword
This flag prompts to enter the password in terminal
+
+ _Note: Use Light mode for PKI based authentication with backward compatiblity to PKI+NLSS based sign, and Basic mode for PKI+NLSS based authentication._
```
Get All DID Command
: To get all DID use this command.
@@ -311,4 +317,35 @@ This following options are used for this command
Server/Host port (default "20000")
-fp
Force password to be entered on the terminal
+```
+To Add explorer url
+: To add explorer url where to send the transaction data.
+```
+./rubixgoplatform addexplorer
+
+This following options are used for this command
+ -links string
+ URLs, mutiple URLs will be seprated by comma
+ -port string
+ Server/Host port (default "20000")
+```
+To remove explorer url
+: To remove explorer url where not to send the transaction data.
+```
+./rubixgoplatform removeexplorer
+
+This following options are used for this command
+ -links string
+ URLs, mutiple URLs will be seprated by comma
+ -port string
+ Server/Host port (default "20000")
+```
+To get all explorer urls
+: To get explorer urls where the transaction data is being sent.
+```
+./rubixgoplatform getallexplorer
+
+This following options are used for this command
+ -port string
+ Server/Host port (default "20000")
```
\ No newline at end of file
diff --git a/block/block.go b/block/block.go
index cf3dcf4c..19227f7e 100644
--- a/block/block.go
+++ b/block/block.go
@@ -181,6 +181,7 @@ func (b *Block) blkDecode() error {
return fmt.Errorf("invalid block, missing block content")
}
hb := util.CalculateHash(bc.([]byte), "SHA3-256")
+
var tcb map[string]interface{}
err = cbor.Unmarshal(bc.([]byte), &tcb)
if err != nil {
@@ -194,7 +195,9 @@ func (b *Block) blkDecode() error {
}
tcb[TCSignatureKey] = ksb
}
+
tcb[TCBlockHashKey] = util.HexToStr(hb)
+
b.bm = tcb
return nil
}
@@ -214,7 +217,9 @@ func (b *Block) blkEncode() error {
return err
}
hb := util.CalculateHash(bc, "SHA3-256")
+
b.bm[TCBlockHashKey] = util.HexToStr(hb)
+
m := make(map[string]interface{})
m[TCBlockContentKey] = bc
if sok {
@@ -328,6 +333,7 @@ func (b *Block) GetSigner() ([]string, error) {
func (b *Block) GetHashSig(did string) (string, string, error) {
h, ok := b.bm[TCBlockHashKey]
+
if !ok {
return "", "", fmt.Errorf("invalid token chain block, missing block hash")
}
diff --git a/client/did.go b/client/did.go
index 1522c7ab..ea51b372 100644
--- a/client/did.go
+++ b/client/did.go
@@ -48,10 +48,12 @@ func (c *Client) GetAllDIDs() (*model.GetAccountInfo, error) {
}
func (c *Client) CreateDID(cfg *did.DIDCreate) (string, bool) {
- if cfg.Type < did.BasicDIDMode && cfg.Type > did.WalletDIDMode {
+ if cfg.Type < did.LiteDIDMode && cfg.Type > did.WalletDIDMode {
return "Invalid DID mode", false
}
switch cfg.Type {
+ case did.LiteDIDMode:
+ cfg.PubKeyFile = ""
case did.BasicDIDMode:
if cfg.ImgFile == "" {
c.log.Error("Image file requried")
@@ -123,15 +125,19 @@ func (c *Client) CreateDID(cfg *did.DIDCreate) (string, bool) {
fields := make(map[string]string)
fields[setup.DIDConfigField] = string(jd)
files := make(map[string]string)
- if cfg.ImgFile != "" {
- files["image"] = cfg.ImgFile
- }
- if cfg.DIDImgFileName != "" {
- files["did_image"] = cfg.DIDImgFileName
- }
- if cfg.PubImgFile != "" {
- files["pub_image"] = cfg.PubImgFile
+
+ if cfg.Type != did.LiteDIDMode {
+ if cfg.ImgFile != "" {
+ files["image"] = cfg.ImgFile
+ }
+ if cfg.DIDImgFileName != "" {
+ files["did_image"] = cfg.DIDImgFileName
+ }
+ if cfg.PubImgFile != "" {
+ files["pub_image"] = cfg.PubImgFile
+ }
}
+
if cfg.PubKeyFile != "" {
files["pub_key"] = cfg.PubKeyFile
}
@@ -145,29 +151,39 @@ func (c *Client) CreateDID(cfg *did.DIDCreate) (string, bool) {
c.log.Error("Failed to create DID", "message", dr.Message)
return "Failed to create DID, " + dr.Message, false
}
- c.log.Info("DID Created successfully")
+ c.log.Info(fmt.Sprintf("DID %v Created successfully", dr.Result.DID))
return dr.Result.DID, true
}
func (c *Client) SetupDID(dc *did.DIDCreate) (string, bool) {
- if dc.Type < did.BasicDIDMode && dc.Type > did.WalletDIDMode {
+ if dc.Type < did.BasicDIDMode && dc.Type > did.LiteDIDMode {
return "Invalid DID mode", false
}
- if !strings.Contains(dc.PubImgFile, did.PubShareFileName) ||
- !strings.Contains(dc.DIDImgFileName, did.DIDImgFileName) ||
- !strings.Contains(dc.PubKeyFile, did.PubKeyFileName) ||
- !strings.Contains(dc.QuorumPubKeyFile, did.QuorumPubKeyFileName) ||
- !strings.Contains(dc.QuorumPrivKeyFile, did.QuorumPvtKeyFileName) {
- return "Required files are missing", false
- }
+
switch dc.Type {
+ case did.LiteDIDMode:
+ if !strings.Contains(dc.PubKeyFile, did.PubKeyFileName) ||
+ !strings.Contains(dc.PrivKeyFile, did.PvtKeyFileName) ||
+ !strings.Contains(dc.MnemonicFile, did.MnemonicFileName) {
+ return "Required files are missing", false
+ }
case did.BasicDIDMode:
if !strings.Contains(dc.PrivImgFile, did.PvtShareFileName) ||
+ !strings.Contains(dc.PubImgFile, did.PubShareFileName) ||
+ !strings.Contains(dc.DIDImgFileName, did.DIDImgFileName) ||
+ !strings.Contains(dc.PubKeyFile, did.PubKeyFileName) ||
+ !strings.Contains(dc.QuorumPubKeyFile, did.QuorumPubKeyFileName) ||
+ !strings.Contains(dc.QuorumPrivKeyFile, did.QuorumPvtKeyFileName) ||
!strings.Contains(dc.PrivKeyFile, did.PvtKeyFileName) {
return "Required files are missing", false
}
case did.StandardDIDMode:
- if !strings.Contains(dc.PrivImgFile, did.PvtShareFileName) {
+ if !strings.Contains(dc.PubImgFile, did.PubShareFileName) ||
+ !strings.Contains(dc.DIDImgFileName, did.DIDImgFileName) ||
+ !strings.Contains(dc.PrivImgFile, did.PvtShareFileName) ||
+ !strings.Contains(dc.PubKeyFile, did.PubKeyFileName) ||
+ !strings.Contains(dc.QuorumPubKeyFile, did.QuorumPubKeyFileName) ||
+ !strings.Contains(dc.QuorumPrivKeyFile, did.QuorumPvtKeyFileName) {
return "Required files are missing", false
}
}
@@ -179,27 +195,32 @@ func (c *Client) SetupDID(dc *did.DIDCreate) (string, bool) {
fields := make(map[string]string)
fields[setup.DIDConfigField] = string(jd)
files := make(map[string]string)
- if dc.PubImgFile != "" {
- files["pub_image"] = dc.PubImgFile
- }
- if dc.DIDImgFileName != "" {
- files["did_image"] = dc.DIDImgFileName
- }
- if dc.PrivImgFile != "" {
- files["priv_image"] = dc.PrivImgFile
+
+ if dc.Type != did.LiteDIDMode {
+ if dc.PubImgFile != "" {
+ files["pub_image"] = dc.PubImgFile
+ }
+ if dc.DIDImgFileName != "" {
+ files["did_image"] = dc.DIDImgFileName
+ }
+ if dc.PrivImgFile != "" {
+ files["priv_image"] = dc.PrivImgFile
+ }
+ if dc.QuorumPubKeyFile != "" {
+ files["quorum_pub_key"] = dc.QuorumPubKeyFile
+ }
+ if dc.QuorumPrivKeyFile != "" {
+ files["quorum_priv_key"] = dc.QuorumPrivKeyFile
+ }
}
+
if dc.PubKeyFile != "" {
files["pub_key"] = dc.PubKeyFile
}
if dc.PrivKeyFile != "" {
files["priv_key"] = dc.PrivKeyFile
}
- if dc.QuorumPubKeyFile != "" {
- files["quorum_pub_key"] = dc.QuorumPubKeyFile
- }
- if dc.QuorumPrivKeyFile != "" {
- files["quorum_priv_key"] = dc.QuorumPrivKeyFile
- }
+
var br model.BasicResponse
err = c.sendMutiFormRequest("POST", setup.APISetupDID, nil, fields, files, &br)
if err != nil {
diff --git a/client/explorer.go b/client/explorer.go
new file mode 100644
index 00000000..6bcd2b17
--- /dev/null
+++ b/client/explorer.go
@@ -0,0 +1,39 @@
+package client
+
+import (
+ "github.com/rubixchain/rubixgoplatform/core/model"
+ "github.com/rubixchain/rubixgoplatform/setup"
+)
+
+func (c *Client) AddExplorer(links []string) (string, bool) {
+ m := model.ExplorerLinks{
+ Links: links,
+ }
+ var rm model.BasicResponse
+ err := c.sendJSONRequest("POST", setup.APIAddExplorer, nil, &m, &rm)
+ if err != nil {
+ return err.Error(), false
+ }
+ return rm.Message, rm.Status
+}
+
+func (c *Client) RemoveExplorer(links []string) (string, bool) {
+ m := model.ExplorerLinks{
+ Links: links,
+ }
+ var rm model.BasicResponse
+ err := c.sendJSONRequest("POST", setup.APIRemoveExplorer, nil, &m, &rm)
+ if err != nil {
+ return err.Error(), false
+ }
+ return rm.Message, rm.Status
+}
+
+func (c *Client) GetAllExplorer() ([]string, string, bool) {
+ var rm model.ExplorerResponse
+ err := c.sendJSONRequest("GET", setup.APIGetAllExplorer, nil, nil, &rm)
+ if err != nil {
+ return nil, err.Error(), false
+ }
+ return rm.Result.Links, rm.Message, rm.Status
+}
diff --git a/client/node.go b/client/node.go
index 02604409..0ef5334d 100644
--- a/client/node.go
+++ b/client/node.go
@@ -13,3 +13,12 @@ func (c *Client) Shutdown() (string, bool) {
}
return rm.Message, rm.Status
}
+
+func (c *Client) PeerID() (string, bool) {
+ var rm model.BasicResponse
+ err := c.sendJSONRequest("GET", setup.APIPeerID, nil, nil, &rm)
+ if err != nil {
+ return "Failed to fetch peer ID of node, error: " + err.Error(), false
+ }
+ return rm.Message, rm.Status
+}
diff --git a/command/command.go b/command/command.go
index 650d8d51..e2d9e850 100644
--- a/command/command.go
+++ b/command/command.go
@@ -33,7 +33,7 @@ const (
)
const (
- version string = "0.0.16"
+ version string = "0.0.17"
)
const (
VersionCmd string = "-v"
@@ -76,8 +76,12 @@ const (
DumpSmartContractTokenChainCmd string = "dumpsmartcontracttokenchain"
GetTokenBlock string = "gettokenblock"
GetSmartContractData string = "getsmartcontractdata"
+ GetPeerID string = "get-peer-id"
ReleaseAllLockedTokensCmd string = "releaseAllLockedTokens"
CheckQuorumStatusCmd string = "checkQuorumStatus"
+ AddExplorerCmd string = "addexplorer"
+ RemoveExplorerCmd string = "removeexplorer"
+ GetAllExplorerCmd string = "getallexplorer"
)
var commands = []string{VersionCmd,
@@ -122,6 +126,7 @@ var commands = []string{VersionCmd,
DumpSmartContractTokenChainCmd,
GetTokenBlock,
GetSmartContractData,
+ GetPeerID,
CheckQuorumStatusCmd,
}
var commandsHelp = []string{"To get tool version",
@@ -165,7 +170,8 @@ var commandsHelp = []string{"To get tool version",
"This command will subscribe to a smart contract token",
"This command will dump the smartcontract token chain",
"This command gets token block",
- "This command gets the smartcontract data from latest block"}
+ "This command gets the smartcontract data from latest block",
+ "This command will fetch the peer ID of the node"}
type Command struct {
cfg config.Config
@@ -240,6 +246,9 @@ type Command struct {
executorAddr string
latest bool
quorumAddr string
+ links []string
+ mnemonicFile string
+ ChildPath int
}
func showVersion() {
@@ -364,6 +373,7 @@ func Run(args []string) {
cmd := &Command{}
var peers string
var timeout int
+ var links string
flag.StringVar(&cmd.runDir, "p", "./", "Working directory path")
flag.StringVar(&cmd.logFile, "logFile", "", "Log file name")
@@ -380,6 +390,7 @@ func Run(args []string) {
flag.StringVar(&peers, "peers", "", "Bootstrap peers, mutiple peers will be seprated by comma")
flag.BoolVar(&cmd.didRoot, "didRoot", false, "Root DID")
flag.IntVar(&cmd.didType, "didType", 0, "DID Creation type")
+ flag.IntVar(&cmd.ChildPath, "ChildPath", 0, "BIP child Path")
flag.StringVar(&cmd.didSecret, "didSecret", "My DID Secret", "DID creation secret")
flag.BoolVar(&cmd.forcePWD, "fp", false, "Force password entry")
flag.StringVar(&cmd.privPWD, "privPWD", "mypassword", "Private key password")
@@ -388,6 +399,7 @@ func Run(args []string) {
flag.StringVar(&cmd.didImgFile, "didImgFile", did.DIDImgFileName, "DID image")
flag.StringVar(&cmd.privImgFile, "privImgFile", did.PvtShareFileName, "DID public share image")
flag.StringVar(&cmd.pubImgFile, "pubImgFile", did.PubShareFileName, "DID public share image")
+ flag.StringVar(&cmd.mnemonicFile, "mnemonicKeyFile", did.MnemonicFileName, "Mnemonic key file")
flag.StringVar(&cmd.privKeyFile, "privKeyFile", did.PvtKeyFileName, "Private key file")
flag.StringVar(&cmd.pubKeyFile, "pubKeyFile", did.PubKeyFileName, "Public key file")
flag.StringVar(&cmd.quorumList, "quorumList", "quorumlist.json", "Quorum list")
@@ -432,6 +444,7 @@ func Run(args []string) {
flag.StringVar(&cmd.executorAddr, "executorAddr", "", "Smart contract Executor Address")
flag.BoolVar(&cmd.latest, "latest", false, "flag to set latest")
flag.StringVar(&cmd.quorumAddr, "quorumAddr", "", "Quorum Node Address to check the status of the Quorum")
+ flag.StringVar(&links, "links", "", "Explorer url")
if len(os.Args) < 2 {
fmt.Println("Invalid Command")
@@ -450,6 +463,11 @@ func Run(args []string) {
cmd.peers = strings.Split(peers, ",")
}
+ if links != "" {
+ links = strings.ReplaceAll(links, " ", "")
+ cmd.links = strings.Split(links, ",")
+ }
+
cmd.timeout = time.Duration(timeout) * time.Minute
if !cmd.validateOptions() {
@@ -572,10 +590,18 @@ func Run(args []string) {
cmd.getSmartContractData()
case ExecuteSmartcontractCmd:
cmd.executeSmartcontract()
+ case GetPeerID:
+ cmd.peerIDCmd()
case ReleaseAllLockedTokensCmd:
cmd.releaseAllLockedTokens()
case CheckQuorumStatusCmd:
cmd.checkQuorumStatus()
+ case AddExplorerCmd:
+ cmd.addExplorer()
+ case RemoveExplorerCmd:
+ cmd.removeExplorer()
+ case GetAllExplorerCmd:
+ cmd.getAllExplorer()
default:
cmd.log.Error("Invalid command")
}
diff --git a/command/did.go b/command/did.go
index c6a84a05..d139543e 100644
--- a/command/did.go
+++ b/command/did.go
@@ -50,7 +50,12 @@ func (cmd *Command) CreateDID() {
}
cmd.quorumPWD = pwd
}
- if cmd.didType == did.WalletDIDMode {
+ if cmd.didType == did.LiteDIDMode {
+ if cmd.privKeyFile == "" || cmd.pubKeyFile == "" {
+ cmd.log.Error("private key & public key file names required")
+ return
+ }
+ } else if cmd.didType == did.WalletDIDMode {
f, err := os.Open(cmd.imgFile)
if err != nil {
cmd.log.Error("failed to open image", "err", err)
@@ -115,8 +120,7 @@ func (cmd *Command) CreateDID() {
cmd.log.Error("failed to create image", "err", err)
return
}
- }
- if cmd.didType != did.BasicDIDMode {
+ } else if cmd.didType != did.BasicDIDMode {
if cmd.privKeyFile == "" || cmd.pubKeyFile == "" {
cmd.log.Error("private key & public key file names required")
return
@@ -147,13 +151,15 @@ func (cmd *Command) CreateDID() {
DIDImgFileName: cmd.didImgFile,
PubImgFile: cmd.pubImgFile,
PubKeyFile: cmd.pubKeyFile,
+ MnemonicFile: cmd.mnemonicFile,
+ ChildPath: cmd.ChildPath,
}
msg, status := cmd.c.CreateDID(&cfg)
if !status {
cmd.log.Error("Failed to create DID", "message", msg)
return
}
- cmd.log.Info("DID Created successfully")
+ cmd.log.Info(fmt.Sprintf("DID %v created successfully", msg))
}
func (cmd *Command) GetAllDID() {
@@ -248,6 +254,8 @@ func (cmd *Command) SignatureResponse(br *model.BasicResponse, timeout ...time.D
Mode: sr.Mode,
}
switch sr.Mode {
+ case did.LiteDIDMode:
+ sresp.Password = password
case did.BasicDIDMode:
sresp.Password = password
case did.StandardDIDMode:
diff --git a/command/explorer.go b/command/explorer.go
new file mode 100644
index 00000000..01b1552b
--- /dev/null
+++ b/command/explorer.go
@@ -0,0 +1,43 @@
+package command
+
+import "fmt"
+
+func (cmd *Command) addExplorer() {
+ if len(cmd.links) == 0 {
+ cmd.log.Error("links required for Explorer")
+ return
+ }
+ msg, status := cmd.c.AddExplorer(cmd.links)
+
+ if !status {
+ cmd.log.Error("Add Explorer command failed, " + msg)
+ } else {
+ cmd.log.Info("Add Explorer command finished, " + msg)
+ }
+}
+
+func (cmd *Command) removeExplorer() {
+ if len(cmd.links) == 0 {
+ cmd.log.Error("links required for Explorer")
+ return
+ }
+ msg, status := cmd.c.RemoveExplorer(cmd.links)
+ if !status {
+ cmd.log.Error("Remove Explorer command failed, " + msg)
+ } else {
+ cmd.log.Info("Remove Explorer command finished, " + msg)
+ }
+}
+
+func (cmd *Command) getAllExplorer() {
+ links, msg, status := cmd.c.GetAllExplorer()
+ if !status {
+ cmd.log.Error("Get all Explorer command failed, " + msg)
+ } else {
+ cmd.log.Info("Get all Explorer command finished, " + msg)
+ cmd.log.Info("Explorer links", "links", links)
+ for i, q := range links {
+ fmt.Printf("URL %d: %s\n", i, q)
+ }
+ }
+}
diff --git a/command/node.go b/command/node.go
index 87975cac..e343eb34 100644
--- a/command/node.go
+++ b/command/node.go
@@ -1,5 +1,10 @@
package command
+import (
+ "fmt"
+ "os"
+)
+
func (cmd *Command) ShutDownCmd() {
msg, status := cmd.c.Shutdown()
if !status {
@@ -8,3 +13,15 @@ func (cmd *Command) ShutDownCmd() {
}
cmd.log.Info("Shutdown initiated successfully, " + msg)
}
+
+func (cmd *Command) peerIDCmd() {
+ msg, status := cmd.c.PeerID()
+ if !status {
+ cmd.log.Error("Failed to fetch peer ID of the node", "msg", msg)
+ return
+ }
+ _, err := fmt.Fprint(os.Stdout, msg, "\n")
+ if err != nil {
+ cmd.log.Error(err.Error())
+ }
+}
diff --git a/contract/contract.go b/contract/contract.go
index 6da75c2e..d19334e3 100644
--- a/contract/contract.go
+++ b/contract/contract.go
@@ -106,6 +106,7 @@ func (c *Contract) blkDecode() error {
if err != nil {
return err
}
+
tcb[SCBlockHashKey] = util.HexToStr(hb)
if sok {
var ksb map[string]interface{}
@@ -149,6 +150,7 @@ func (c *Contract) blkEncode() error {
return err
}
hb := util.CalculateHash(bc, "SHA3-256")
+
c.sm[SCBlockHashKey] = util.HexToStr(hb)
m := make(map[string]interface{})
m[SCBlockContentKey] = bc
@@ -232,7 +234,8 @@ func (c *Contract) GetHashSig(did string) (string, string, string, error) {
ss := util.GetStringFromMap(ssi, did)
ks := util.GetStringFromMap(ksi, did)
- if ss == "" || ks == "" {
+ // ss == "" ||
+ if ks == "" {
return "", "", "", fmt.Errorf("invalid smart contract, share/key signature block is missing")
}
return hi.(string), ss, ks, nil
@@ -387,6 +390,7 @@ func (c *Contract) UpdateSignature(dc did.DIDCrypto) error {
if err != nil {
return fmt.Errorf("Failed to get signature, " + err.Error())
}
+
if c.sm[SCShareSignatureKey] == nil {
ksm := make(map[string]interface{})
ksm[did] = util.HexToStr(ssig)
@@ -428,19 +432,39 @@ func (c *Contract) UpdateSignature(dc did.DIDCrypto) error {
return c.blkEncode()
}
+// This function is used by the quorums to verify sender's signature
func (c *Contract) VerifySignature(dc did.DIDCrypto) error {
- did := dc.GetDID()
- hs, ss, ps, err := c.GetHashSig(did)
+ //fetch sender's did
+ didstr := dc.GetDID()
+
+ //fetch sender's signature
+ hs, ss, ps, err := c.GetHashSig(didstr)
if err != nil {
c.log.Error("err", err)
return err
}
- ok, err := dc.Verify(hs, util.StrToHex(ss), util.StrToHex(ps))
- if err != nil {
- return err
- }
- if !ok {
- return fmt.Errorf("did signature verification failed")
+
+ //If the ss i.e., share signature is empty, then its a Pki sign, so call PvtVerify
+ //Else it is NLSS based sign, so call NlssVerify
+ didType := dc.GetSignType()
+ if didType == did.BIPVersion {
+ ok, err := dc.PvtVerify([]byte(hs), util.StrToHex(ps))
+
+ if err != nil {
+ return err
+ }
+ if !ok {
+ return fmt.Errorf("did Pki signature verification failed")
+ }
+ } else {
+ ok, err := dc.NlssVerify(hs, util.StrToHex(ss), util.StrToHex(ps))
+ if err != nil {
+ return err
+ }
+ if !ok {
+ return fmt.Errorf("did Nlss signature verification failed")
+ }
}
+
return nil
}
diff --git a/core/core.go b/core/core.go
index 9a5ced93..f608c287 100644
--- a/core/core.go
+++ b/core/core.go
@@ -46,7 +46,9 @@ const (
APIGetTokenNumber string = "/api/get-token-number"
APIGetMigratedTokenStatus string = "/api/get-Migrated-token-status"
APISyncDIDArbitration string = "/api/sync-did-arbitration"
+ APIUnlockTokens string = "/api/unlock-tokens"
APICheckQuorumStatusPath string = "/api/check-quorum-status"
+ APIGetPeerDIDTypePath string = "/api/get-peer-didType"
)
const (
@@ -320,6 +322,7 @@ func (c *Core) SetupCore() error {
c.w.SetupWallet(c.ipfs)
c.PingSetup()
c.CheckQuorumStatusSetup()
+ c.GetPeerdidTypeSetup()
c.peerSetup()
c.w.AddDIDLastChar()
c.SetupToken()
@@ -352,7 +355,6 @@ func (c *Core) Start() (bool, string) {
return false, "Failed to start ping port"
}
//c.w.ReleaseAllLockedTokens()
-
// exp := model.ExploreModel{
// Cmd: ExpPeerStatusCmd,
// PeerID: c.peerID,
@@ -509,6 +511,8 @@ func (c *Core) SetupDID(reqID string, didStr string) (did.DIDCrypto, error) {
return nil, fmt.Errorf("faield to get did channel")
}
switch dt.Type {
+ case did.LiteDIDMode:
+ return did.InitDIDLite(didStr, c.didDir, dc), nil
case did.BasicDIDMode:
return did.InitDIDBasic(didStr, c.didDir, dc), nil
case did.StandardDIDMode:
@@ -522,20 +526,70 @@ func (c *Core) SetupDID(reqID string, didStr string) (did.DIDCrypto, error) {
}
}
+// Initializes the did in it's corresponding did mode (basic/ lite)
func (c *Core) SetupForienDID(didStr string) (did.DIDCrypto, error) {
err := c.FetchDID(didStr)
if err != nil {
+ c.log.Error("couldn't fetch did")
return nil, err
}
- return did.InitDIDBasic(didStr, c.didDir, nil), nil
+
+ // Fetching peer's did type from PeerDIDTable using GetPeerDIDType function
+ // and own did type from DIDTable using GetDID function
+ didtype, err := c.w.GetPeerDIDType(didStr)
+ if err != nil {
+ dt, err1 := c.w.GetDID(didStr)
+ if err1 != nil {
+ return nil, fmt.Errorf("couldn't fetch did type")
+ }
+ didtype = dt.Type
+ }
+ return c.InitialiseDID(didStr, didtype)
}
+// Initializes the quorum in it's corresponding did mode (basic/ lite)
func (c *Core) SetupForienDIDQuorum(didStr string) (did.DIDCrypto, error) {
err := c.FetchDID(didStr)
if err != nil {
return nil, err
}
- return did.InitDIDQuorumc(didStr, c.didDir, ""), nil
+
+ // Fetching peer's did type from PeerDIDTable using GetPeerDIDType function
+ // and own did type from DIDTable using GetDID function
+ didtype, err := c.w.GetPeerDIDType(didStr)
+ if err != nil || didtype == -1 {
+ dt, err1 := c.w.GetDID(didStr)
+
+ if err1 != nil || dt.Type == -1 {
+ peerId := c.w.GetPeerID(didStr)
+
+ if peerId == "" {
+ return nil, err
+ }
+ didtype_, msg, err2 := c.GetPeerdidType_fromPeer(peerId, didStr)
+ if err2 != nil {
+ c.log.Error(msg)
+ return nil, err2
+ }
+ didtype = didtype_
+ peerUpdateResult, err3 := c.w.UpdatePeerDIDType(didStr, didtype)
+ if !peerUpdateResult {
+ c.log.Error("couldn't update did type in peer did table", err3)
+ }
+ } else {
+ didtype = dt.Type
+ }
+
+ }
+
+ switch didtype {
+ case did.BasicDIDMode:
+ return did.InitDIDQuorumc(didStr, c.didDir, ""), nil
+ case did.LiteDIDMode:
+ return did.InitDIDQuorum_Lt(didStr, c.didDir, ""), nil
+ default:
+ return nil, fmt.Errorf("invalid did type")
+ }
}
func (c *Core) FetchDID(did string) error {
@@ -565,3 +619,19 @@ func (c *Core) FetchDID(did string) error {
func (c *Core) GetPeerID() string {
return c.peerID
}
+
+// Initializes the did in it's corresponding did mode (basic/ lite)
+func (c *Core) InitialiseDID(didStr string, didType int) (did.DIDCrypto, error) {
+ err := c.FetchDID(didStr)
+ if err != nil {
+ return nil, err
+ }
+ switch didType {
+ case did.LiteDIDMode:
+ return did.InitDIDLite(didStr, c.didDir, nil), nil
+ case did.BasicDIDMode:
+ return did.InitDIDBasic(didStr, c.didDir, nil), nil
+ default:
+ return nil, fmt.Errorf("invalid did type, couldn't initialise")
+ }
+}
diff --git a/core/did.go b/core/did.go
index 3adacdb3..5818135c 100644
--- a/core/did.go
+++ b/core/did.go
@@ -30,6 +30,23 @@ func (c *Core) GetDIDAccess(req *model.GetDIDAccess) *model.DIDAccessResponse {
resp.Message = "Password does not match"
return resp
}
+ } else if dt.Type == did.LiteDIDMode {
+ _, ok := c.ValidateDIDToken(req.Token, setup.ChanllegeTokenType, req.DID)
+ if !ok {
+ resp.Message = "Invalid token"
+ return resp
+ }
+ dc := did.InitDIDLite(req.DID, c.didDir, nil)
+ ok, err := dc.PvtVerify([]byte(req.Token), req.Signature)
+ if err != nil {
+ c.log.Error("Failed to verify DID signature", "err", err)
+ resp.Message = "Failed to verify DID signature"
+ return resp
+ }
+ if !ok {
+ resp.Message = "Invalid signature"
+ return resp
+ }
} else {
_, ok := c.ValidateDIDToken(req.Token, setup.ChanllegeTokenType, req.DID)
if !ok {
@@ -197,11 +214,17 @@ func (c *Core) registerDID(reqID string, did string) error {
if err != nil {
return fmt.Errorf("register did, failed to do signature")
}
+
+ dt, err := c.w.GetDID(did)
+ if err != nil {
+ return fmt.Errorf("DID does not exist")
+ }
pm := &PeerMap{
PeerID: c.peerID,
DID: did,
Signature: sig,
Time: t,
+ DIDType: dt.Type,
}
err = c.publishPeerMap(pm)
if err != nil {
diff --git a/core/explorer.go b/core/explorer.go
index 9ecf6bef..4f9a32a7 100644
--- a/core/explorer.go
+++ b/core/explorer.go
@@ -3,7 +3,9 @@ package core
import (
"fmt"
"net/http"
+ "strings"
+ "github.com/rubixchain/rubixgoplatform/core/storage"
"github.com/rubixchain/rubixgoplatform/wrapper/config"
"github.com/rubixchain/rubixgoplatform/wrapper/ensweb"
"github.com/rubixchain/rubixgoplatform/wrapper/helper/jsonutil"
@@ -16,11 +18,13 @@ const (
ExplorerTransactionAPI string = "CreateOrUpdateRubixTransaction"
ExplorerCreateDataTransAPI string = "create-datatokens"
ExplorerMapDIDAPI string = "map-did"
+ ExplorerURLTable string = "ExplorerURLTable"
)
type ExplorerClient struct {
ensweb.Client
log logger.Logger
+ es storage.Storage
}
type ExplorerDID struct {
@@ -67,11 +71,33 @@ type ExplorerResponse struct {
Status bool `json:"Status"`
}
+type ExplorerURL struct {
+ URL string `gorm:"column:url;primaryKey" json:"ExplorerURL"`
+ Port int `gorm:"column:port" json:"Explorerport"`
+}
+
func (c *Core) InitRubixExplorer() error {
+
+ err := c.s.Init(ExplorerURLTable, &ExplorerURL{}, true)
+ if err != nil {
+ c.log.Error("Failed to initialise storage ExplorerURL ", "err", err)
+ return err
+ }
+
url := "deamon-explorer.azurewebsites.net"
if c.testNet {
url = "rubix-deamon-api.ensurity.com"
}
+
+ err = c.s.Read(ExplorerURLTable, &ExplorerURL{}, "url=?", url)
+ if err != nil {
+ err = c.s.Write(ExplorerURLTable, &ExplorerURL{URL: url, Port: 443})
+ }
+
+ if err != nil {
+ return err
+ }
+
cl, err := ensweb.NewClient(&config.Config{ServerAddress: url, ServerPort: "443", Production: "true"}, c.log)
if err != nil {
return err
@@ -79,33 +105,44 @@ func (c *Core) InitRubixExplorer() error {
c.ec = &ExplorerClient{
Client: cl,
log: c.log.Named("explorerclient"),
+ es: c.s,
}
return nil
}
func (ec *ExplorerClient) SendExploerJSONRequest(method string, path string, input interface{}, output interface{}) error {
- req, err := ec.JSONRequest(method, ExplorerBasePath+path, input)
- if err != nil {
- return err
- }
- resp, err := ec.Do(req)
+
+ var urls []string
+ urls, err := ec.GetAllExplorer()
if err != nil {
- ec.log.Error("Failed r get response from explorer", "err", err)
return err
}
- defer resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- str := fmt.Sprintf("Http Request failed with status %d", resp.StatusCode)
- ec.log.Error(str)
- return fmt.Errorf(str)
- }
- if output == nil {
- return nil
- }
- err = jsonutil.DecodeJSONFromReader(resp.Body, output)
- if err != nil {
- ec.log.Error("Invalid response from the node", "err", err)
- return err
+
+ for _, url := range urls {
+ req, err := ec.JSONRequestForExplorer(method, ExplorerBasePath+path, input, url)
+ if err != nil {
+ ec.log.Error("Request could not be sent to : "+url, "err", err)
+ continue
+ }
+ resp, err := ec.Do(req)
+ if err != nil {
+ ec.log.Error("Failed to get response from explorer : "+url, "err", err)
+ continue
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode != http.StatusOK {
+ str := fmt.Sprintf("Http Request failed with status %d for "+url, resp.StatusCode)
+ ec.log.Error(str)
+ continue
+ }
+ if output == nil {
+ continue
+ }
+ err = jsonutil.DecodeJSONFromReader(resp.Body, output)
+ if err != nil {
+ ec.log.Error("Invalid response from the node", "err", err)
+ continue
+ }
}
return nil
}
@@ -170,3 +207,66 @@ func (ec *ExplorerClient) ExplorerDataTransaction(et *ExplorerDataTrans) error {
}
return nil
}
+
+func (c *Core) AddExplorer(links []string) error {
+
+ var eurl []ExplorerURL
+
+ for _, url := range links {
+ if strings.HasPrefix(url, "https") {
+ url = strings.TrimPrefix(url, "https://")
+ } else if strings.HasPrefix(url, "http") {
+ url = strings.TrimPrefix(url, "http://")
+ }
+ eur := ExplorerURL{
+ URL: url,
+ Port: 0,
+ }
+ eurl = append(eurl, eur)
+ }
+
+ err := c.s.Write(ExplorerURLTable, eurl)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (c *Core) RemoveExplorer(links []string) error {
+
+ for _, url := range links {
+ if strings.HasPrefix(url, "https") {
+ url = strings.TrimPrefix(url, "https://")
+ } else if strings.HasPrefix(url, "http") {
+ url = strings.TrimPrefix(url, "http://")
+ }
+ err := c.s.Delete(ExplorerURLTable, &ExplorerURL{}, "url=?", url)
+
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (c *Core) GetAllExplorer() ([]string, error) {
+ var urls []string
+ urls, err := c.ec.GetAllExplorer()
+ if err != nil {
+ return nil, err
+ }
+ return urls, nil
+}
+
+func (ec *ExplorerClient) GetAllExplorer() ([]string, error) {
+ var eurl []ExplorerURL
+ var urls []string
+ err := ec.es.Read(ExplorerURLTable, &eurl, "url!=?", "")
+ if err != nil {
+ return nil, err
+ }
+ for _, url := range eurl {
+ urls = append(urls, fmt.Sprintf("https://%s", url.URL))
+ }
+ return urls, nil
+}
diff --git a/core/ipfsport/peer.go b/core/ipfsport/peer.go
index d5c68fb8..cee13a43 100644
--- a/core/ipfsport/peer.go
+++ b/core/ipfsport/peer.go
@@ -3,6 +3,7 @@ package ipfsport
import (
"context"
"fmt"
+ "net"
"net/http"
"path"
"sync"
@@ -65,13 +66,34 @@ func (pm *PeerManager) getPeerPort() uint16 {
defer pm.lock.Unlock()
for i, status := range pm.ps {
if !status {
- pm.ps[i] = true
- return pm.startPort + uint16(i)
+ port := pm.startPort + uint16(i)
+ availability := isPortAvailable(port)
+
+ if availability {
+ pm.ps[i] = true
+ return pm.startPort + uint16(i)
+ }
}
}
return 0
}
+func isPortAvailable(port uint16) bool {
+ // Convert uint16 port to int
+ portInt := int(port)
+
+ // Attempt to listen on the port
+ listener, err := net.Listen("tcp", fmt.Sprintf(":%d", portInt))
+ if err != nil {
+ // Port is not available
+ return false
+ }
+ defer listener.Close()
+
+ // Port is available
+ return true
+}
+
func (pm *PeerManager) releasePeerPort(port uint16) bool {
pm.lock.Lock()
defer pm.lock.Unlock()
@@ -159,7 +181,6 @@ func (pm *PeerManager) OpenPeerConn(peerID string, did string, appname string) (
defer resp.Close()
if resp.Error != nil {
pm.log.Error("error in forward request")
- pm.releasePeerPort(portNum)
return nil, resp.Error
}
p.Client, err = ensweb.NewClient(scfg, p.log)
diff --git a/core/migrate.go b/core/migrate.go
index 317ed005..cd55f0b0 100644
--- a/core/migrate.go
+++ b/core/migrate.go
@@ -158,25 +158,31 @@ func (c *Core) migrateNode(reqID string, m *MigrateRequest, didDir string) error
}
if !resume {
didCreate := didm.DIDCreate{
- Dir: didDir,
- Type: m.DIDType,
- PrivPWD: m.PrivPWD,
- QuorumPWD: m.QuorumPWD,
- DIDImgFileName: rubixDir + "DATA/" + d[0].DID + "/DID.png",
- PubImgFile: rubixDir + "DATA/" + d[0].DID + "/PublicShare.png",
- PrivImgFile: rubixDir + "DATA/" + d[0].DID + "/PrivateShare.png",
+ Dir: didDir,
+ Type: m.DIDType,
+ PrivPWD: m.PrivPWD,
+ QuorumPWD: m.QuorumPWD,
}
- _, err = os.Stat(didCreate.DIDImgFileName)
- if err != nil {
- c.log.Error("Failed to migrate, missing DID.png file", "err", err)
- return fmt.Errorf("failed to migrate, missing DID.png file")
- }
- _, err = os.Stat(didCreate.PubImgFile)
- if err != nil {
- c.log.Error("Failed to migrate, missing PublicShare.png file", "err", err)
- return fmt.Errorf("failed to migrate, missing PublicShare.png file")
+ if didCreate.Type != didm.LiteDIDMode {
+ didCreate = didm.DIDCreate{
+ DIDImgFileName: rubixDir + "DATA/" + d[0].DID + "/DID.png",
+ PubImgFile: rubixDir + "DATA/" + d[0].DID + "/PublicShare.png",
+ PrivImgFile: rubixDir + "DATA/" + d[0].DID + "/PrivateShare.png",
+ }
+
+ _, err = os.Stat(didCreate.DIDImgFileName)
+ if err != nil {
+ c.log.Error("Failed to migrate, missing DID.png file", "err", err)
+ return fmt.Errorf("failed to migrate, missing DID.png file")
+ }
+ _, err = os.Stat(didCreate.PubImgFile)
+ if err != nil {
+ c.log.Error("Failed to migrate, missing PublicShare.png file", "err", err)
+ return fmt.Errorf("failed to migrate, missing PublicShare.png file")
+ }
}
+
did, err = c.d.MigrateDID(&didCreate)
if err != nil {
c.log.Error("Failed to migrate, failed in creation of new DID address", "err", err, "msg", did)
@@ -646,6 +652,7 @@ func (c *Core) migrateNode(reqID string, m *MigrateRequest, didDir string) error
fp.Close()
}
}
+
// if len(migrateTokens) > 0 {
// fp, err := os.Open("migratedtokens.txt")
// if err == nil {
@@ -655,6 +662,7 @@ func (c *Core) migrateNode(reqID string, m *MigrateRequest, didDir string) error
// fp.Close()
// }
// }
+
creditFiles, err := util.GetAllFiles(rubixDir + "Wallet/WALLET_DATA/Credits/")
if err != nil {
c.log.Error("Failed to migrate, failed to read credit files", "err", err)
diff --git a/core/model/basic.go b/core/model/basic.go
index 65493651..eb70deaa 100644
--- a/core/model/basic.go
+++ b/core/model/basic.go
@@ -20,3 +20,9 @@ type MigratedTokenStatus struct {
Message string `json:"message"`
MigratedStatus []int `json:"migratedstatus"`
}
+
+// GetDIDTypeResponse is the model for response of peer while fetching did type
+type GetDIDTypeResponse struct {
+ DidType int
+ BasicResponse
+}
diff --git a/core/model/explorer.go b/core/model/explorer.go
new file mode 100644
index 00000000..6956e7cc
--- /dev/null
+++ b/core/model/explorer.go
@@ -0,0 +1,13 @@
+package model
+
+// ExplorerLinks
+type ExplorerLinks struct {
+ Links []string `json:"links"`
+}
+
+// ExplorerResponse used as model for the API responses
+type ExplorerResponse struct {
+ Status bool `json:"status"`
+ Message string `json:"message"`
+ Result ExplorerLinks `json:"result"`
+}
diff --git a/core/peer.go b/core/peer.go
index 52456864..553cd878 100644
--- a/core/peer.go
+++ b/core/peer.go
@@ -18,6 +18,7 @@ const (
type PeerMap struct {
PeerID string `json:"peer_id"`
DID string `json:"did"`
+ DIDType int `json:"did_type"`
Signature []byte `json:"signature"`
Time string `json:"time"`
}
@@ -48,7 +49,7 @@ func (c *Core) peerCallback(peerID string, topic string, data []byte) {
return
}
h := util.CalculateHashString(m.PeerID+m.DID+m.Time, "SHA3-256")
- dc, err := c.SetupForienDID(m.DID)
+ dc, err := c.InitialiseDID(m.DID, m.DIDType)
if err != nil {
return
}
@@ -56,7 +57,7 @@ func (c *Core) peerCallback(peerID string, topic string, data []byte) {
if err != nil || !st {
return
}
- c.w.AddDIDPeerMap(m.DID, m.PeerID)
+ c.w.AddDIDPeerMap(m.DID, m.PeerID, m.DIDType)
}
func (c *Core) peerStatus(req *ensweb.Request) *ensweb.Result {
diff --git a/core/ping.go b/core/ping.go
index 35b0e729..4d0731f0 100644
--- a/core/ping.go
+++ b/core/ping.go
@@ -29,6 +29,11 @@ func (c *Core) CheckQuorumStatusSetup() {
c.l.AddRoute(APICheckQuorumStatusPath, "GET", c.CheckQuorumStatusResponse)
}
+// GetPeerdidTypeSetup will setup the ping route
+func (c *Core) GetPeerdidTypeSetup() {
+ c.l.AddRoute(APIGetPeerDIDTypePath, "GET", c.GetPeerdidTypeResponse)
+}
+
// PingRecevied is the handler for ping request
func (c *Core) PingRecevied(req *ensweb.Request) *ensweb.Result {
c.log.Info("Ping Received")
@@ -98,3 +103,48 @@ func (c *Core) CheckQuorumStatus(peerID string, did string) (string, bool, error
}
return checkQuorumStatusResponse.Message, checkQuorumStatusResponse.Status, nil
}
+
+// CheckQuorumStatusResponse is the handler for CheckQuorumStatus request
+func (c *Core) GetPeerdidTypeResponse(req *ensweb.Request) *ensweb.Result { //PingRecevied
+ did := c.l.GetQuerry(req, "did")
+ c.log.Info("Fetching peer did type from peer")
+ resp := &model.GetDIDTypeResponse{
+ BasicResponse: model.BasicResponse{
+ Status: false,
+ },
+ }
+
+ dt, err := c.w.GetDID(did)
+ if err != nil {
+ c.log.Error("Couldn't fetch did type from DID Table", "error", err)
+ resp.Message = "Couldn't fetch did type for did: " + did
+ resp.Status = false
+ resp.DidType = -1
+ return c.l.RenderJSON(req, &resp, http.StatusOK)
+ } else {
+ resp.DidType = dt.Type
+ resp.Status = true
+ resp.Message = "successfully fetched did type"
+ return c.l.RenderJSON(req, &resp, http.StatusOK)
+ }
+
+}
+
+// GetPeerdidType will ping the peer & get the did type
+func (c *Core) GetPeerdidType_fromPeer(peerID string, did string) (int, string, error) {
+ q := make(map[string]string)
+ p, err := c.pm.OpenPeerConn(peerID, did, c.getCoreAppName(peerID))
+ if err != nil {
+ return -1, "Quorum Connection Error", fmt.Errorf("quorum connection error")
+ }
+
+ // Close the p2p before exit
+ defer p.Close()
+ q["did"] = did
+ var getPeerdidTypeResponse model.GetDIDTypeResponse
+ err = p.SendJSONRequest("GET", APIGetPeerDIDTypePath, q, nil, &getPeerdidTypeResponse, false, 2*time.Minute)
+ if err != nil {
+ return -1, "Send Json Request error ", err
+ }
+ return getPeerdidTypeResponse.DidType, getPeerdidTypeResponse.Message, nil
+}
diff --git a/core/quorum_initiator.go b/core/quorum_initiator.go
index 1416bf78..237cc0b4 100644
--- a/core/quorum_initiator.go
+++ b/core/quorum_initiator.go
@@ -60,9 +60,10 @@ type ConensusReply struct {
}
type ConsensusResult struct {
- RunningCount int
- SuccessCount int
- FailedCount int
+ RunningCount int
+ SuccessCount int
+ FailedCount int
+ PledgingDIDSignStatus bool
}
type ConsensusStatus struct {
@@ -128,6 +129,7 @@ type CreditSignature struct {
PrivSignature string `json:"priv_signature"`
DID string `json:"did"`
Hash string `json:"hash"`
+ SignVersion string `json:"sign_version"` //represents sign version (PkiSign == 0 or NlssSign==1)
}
type TokenArbitrationReq struct {
@@ -141,6 +143,11 @@ type ArbitaryStatus struct {
status bool
}
+type TokenList struct {
+ Tokens []string
+ DID string
+}
+
// PingSetup will setup the ping route
func (c *Core) QuroumSetup() {
c.l.AddRoute(APICreditStatus, "GET", c.creditStatus)
@@ -150,6 +157,7 @@ func (c *Core) QuroumSetup() {
c.l.AddRoute(APIUpdatePledgeToken, "POST", c.updatePledgeToken)
c.l.AddRoute(APISignatureRequest, "POST", c.signatureRequest)
c.l.AddRoute(APISendReceiverToken, "POST", c.updateReceiverToken)
+ c.l.AddRoute(APIUnlockTokens, "POST", c.unlockTokens)
if c.arbitaryMode {
c.l.AddRoute(APIMapDIDArbitration, "POST", c.mapDIDArbitration)
c.l.AddRoute(APICheckDIDArbitration, "GET", c.chekDIDArbitration)
@@ -165,20 +173,52 @@ func (c *Core) SetupQuorum(didStr string, pwd string, pvtKeyPwd string) error {
c.log.Error("DID does not exist", "did", didStr)
return fmt.Errorf("DID does not exist")
}
- dc := did.InitDIDQuorumc(didStr, c.didDir, pwd)
- if dc == nil {
- c.log.Error("Failed to setup quorum")
- return fmt.Errorf("failed to setup quorum")
+
+ dt, err := c.w.GetDID(didStr)
+ if err != nil {
+ c.log.Error("DID could not fetch", "did", didStr)
+ return fmt.Errorf("DID does not exist")
}
- c.qc[didStr] = dc
- if pvtKeyPwd != "" {
- dc := did.InitDIDBasicWithPassword(didStr, c.didDir, pvtKeyPwd)
+
+ //To support NLSS backward compatibility,
+ //If the Quorum's did is created in lite mode,
+ //it will initiate DIDQuorum_Lt, and if it is in basic mode,
+ //it will initiate DIDQuorumc
+ switch dt.Type {
+ case did.LiteDIDMode:
+ dc := did.InitDIDQuorum_Lt(didStr, c.didDir, pwd)
if dc == nil {
c.log.Error("Failed to setup quorum")
return fmt.Errorf("failed to setup quorum")
}
- c.pqc[didStr] = dc
+ c.qc[didStr] = dc
+ if pvtKeyPwd != "" {
+ dc := did.InitDIDLiteWithPassword(didStr, c.didDir, pvtKeyPwd)
+ if dc == nil {
+ c.log.Error("Failed to setup quorum as dc is nil")
+ return fmt.Errorf("failed to setup quorum")
+ }
+ c.pqc[didStr] = dc
+ }
+ case did.BasicDIDMode:
+ dc := did.InitDIDQuorumc(didStr, c.didDir, pwd)
+ if dc == nil {
+ c.log.Error("Failed to setup quorum")
+ return fmt.Errorf("failed to setup quorum")
+ }
+ c.qc[didStr] = dc
+ if pvtKeyPwd != "" {
+ dc := did.InitDIDBasicWithPassword(didStr, c.didDir, pvtKeyPwd)
+ if dc == nil {
+ c.log.Error("Failed to setup quorum")
+ return fmt.Errorf("failed to setup quorum")
+ }
+ c.pqc[didStr] = dc
+ }
+ default:
+ return fmt.Errorf("DID Type is not supported")
}
+
c.up.RunUnpledge()
return nil
}
@@ -234,9 +274,10 @@ func (c *Core) initiateConsensus(cr *ConensusRequest, sc *contract.Contract, dc
},
P: make(map[string]*ipfsport.Peer),
Result: ConsensusResult{
- RunningCount: 0,
- SuccessCount: 0,
- FailedCount: 0,
+ RunningCount: 0,
+ SuccessCount: 0,
+ FailedCount: 0,
+ PledgingDIDSignStatus: false,
},
}
reqPledgeTokens := float64(0)
@@ -337,6 +378,10 @@ func (c *Core) initiateConsensus(cr *ConensusRequest, sc *contract.Contract, dc
}
}
if err != nil {
+ unlockErr := c.checkLokedTokens(cr, ql)
+ if unlockErr != nil {
+ c.log.Error(unlockErr.Error() + "Locked tokens could not be unlocked")
+ }
return nil, nil, err
}
@@ -673,29 +718,65 @@ func (c *Core) finishConsensus(id string, qt int, p *ipfsport.Peer, status bool,
defer c.qlock.Unlock()
cs, ok := c.quorumRequest[id]
if !ok {
+ fmt.Println("failed to get quorum consensus")
if p != nil {
p.Close()
}
return
}
+ pd, ok := c.pd[id] //getting details of quorums who pledged
+ if !ok {
+ fmt.Println("failed to get pledged token details")
+ if p != nil {
+ p.Close()
+ }
+ return
+ }
+ pledgingQuorumDID := make([]string, 0, len(pd.PledgedTokens))
+ for k := range pd.PledgedTokens {
+ pledgingQuorumDID = append(pledgingQuorumDID, k)
+ }
+ var pledgingDID string
+ if len(pledgingQuorumDID) > 0 {
+ pledgingDID = pledgingQuorumDID[0]
+ }
+
+ var signVersion string
+
+ //signVersion = 0 => Pki based sign in lite mode
+ //signVersion = 1 => Nlss based sign in basic mode
+ if util.HexToStr(ss) == "" {
+ signVersion = "0"
+ } else {
+ signVersion = "1"
+ }
+
switch qt {
case 0:
cs.Result.RunningCount--
if status {
did := p.GetPeerDID()
- if cs.Result.SuccessCount < MinConsensusRequired {
- csig := CreditSignature{
- Signature: util.HexToStr(ss),
- PrivSignature: util.HexToStr(ps),
- DID: did,
- Hash: hash,
- }
+ csig := CreditSignature{
+ Signature: util.HexToStr(ss),
+ PrivSignature: util.HexToStr(ps),
+ DID: did,
+ Hash: hash,
+ SignVersion: signVersion,
+ }
+ if cs.Result.SuccessCount < MinConsensusRequired-1 {
cs.P[did] = p
cs.Credit.Credit = append(cs.Credit.Credit, csig)
+ cs.Result.SuccessCount++
+ if did == pledgingDID {
+ cs.Result.PledgingDIDSignStatus = true
+ }
+ } else if (did == pledgingDID || cs.Result.PledgingDIDSignStatus) && cs.Result.SuccessCount == MinConsensusRequired-1 {
+ cs.P[did] = p
+ cs.Credit.Credit = append(cs.Credit.Credit, csig)
+ cs.Result.SuccessCount++
} else {
p.Close()
}
- cs.Result.SuccessCount++
} else {
cs.Result.FailedCount++
if p != nil {
@@ -1260,3 +1341,38 @@ func (c *Core) createCommitedTokensBlock(newBlock *block.Block, smartContractTok
}
return nil
}
+
+func (c *Core) checkLokedTokens(cr *ConensusRequest, quorumList []string) error {
+ // var err error
+ pd := c.pd[cr.ReqID]
+
+ pledgingQuorumDID := make([]string, 0, len(pd.PledgedTokens))
+ for k := range pd.PledgedTokens {
+ pledgingQuorumDID = append(pledgingQuorumDID, k)
+ }
+ var pledgingDID string
+ if len(pledgingQuorumDID) > 0 {
+ pledgingDID = pledgingQuorumDID[0]
+ }
+ var br model.BasicResponse
+ for _, addr := range quorumList {
+ peerID, did, _ := util.ParseAddress(addr)
+ if did == pledgingDID {
+ p, err := c.pm.OpenPeerConn(peerID, did, c.getCoreAppName(peerID))
+ if err != nil {
+ c.log.Error("Failed to get peer connection", "err", err)
+ return err
+ }
+ tokenList := TokenList{
+ Tokens: pd.PledgedTokens[pledgingDID],
+ DID: pledgingDID,
+ }
+ err = p.SendJSONRequest("POST", APIUnlockTokens, nil, &tokenList, &br, true)
+ if err != nil {
+ c.log.Error("Invalid response for pledge request", "err", err)
+ return err
+ }
+ }
+ }
+ return nil
+}
diff --git a/core/quorum_recv.go b/core/quorum_recv.go
index 284d09a6..141b296f 100644
--- a/core/quorum_recv.go
+++ b/core/quorum_recv.go
@@ -46,7 +46,7 @@ func (c *Core) verifyContract(cr *ConensusRequest) (bool, *contract.Contract) {
}
err = sc.VerifySignature(dc)
if err != nil {
- c.log.Error("Failed to verify sender signature", "err", err)
+ c.log.Error("Failed to verify sender signature in verifyContract", "err", err)
return false, nil
}
return true, sc
@@ -570,6 +570,14 @@ func (c *Core) reqPledgeToken(req *ensweb.Request) *ensweb.Result {
crep.Message = "Failed to parse json request"
return c.l.RenderJSON(req, &crep, http.StatusOK)
}
+
+ _, ok := c.qc[did]
+ if !ok {
+ c.log.Error("Quorum is not setup")
+ crep.Message = "Quorum is not setup"
+ return c.l.RenderJSON(req, &crep, http.StatusOK)
+ }
+
if (pr.TokensRequired) < MinTrnxAmt {
c.log.Error("Pledge amount is less than ", MinTrnxAmt)
crep.Message = "Pledge amount is less than minimum transcation amount"
@@ -1316,3 +1324,26 @@ func (c *Core) getProofverificationDetails(tokenID string, senderAddr string) (s
}
return receiverDID, txnId, nil
}
+
+func (c *Core) unlockTokens(req *ensweb.Request) *ensweb.Result {
+ var tokenList TokenList
+ err := c.l.ParseJSON(req, &tokenList)
+ crep := model.BasicResponse{
+ Status: false,
+ }
+ if err != nil {
+ c.log.Error("Failed to parse json request", "err", err)
+ crep.Message = "Failed to parse json request"
+ return c.l.RenderJSON(req, &crep, http.StatusOK)
+ }
+ err = c.w.UnlockLockedTokens(tokenList.DID, tokenList.Tokens)
+ if err != nil {
+ c.log.Error("Failed to update token status", "err", err)
+ return c.l.RenderJSON(req, &crep, http.StatusOK)
+ }
+ crep.Status = true
+ crep.Message = "Tokens Unlocked Successfully."
+ c.log.Info("Tokens Unclocked")
+ return c.l.RenderJSON(req, &crep, http.StatusOK)
+
+}
diff --git a/core/wallet/did.go b/core/wallet/did.go
index 224971dd..798bd84a 100644
--- a/core/wallet/did.go
+++ b/core/wallet/did.go
@@ -10,6 +10,7 @@ type DIDType struct {
type DIDPeerMap struct {
DID string `gorm:"column:did;primaryKey"`
+ DIDType *int `gorm:"column:did_type"`
PeerID string `gorm:"column:peer_id"`
DIDLastChar string `gorm:"column:did_last_char"`
}
@@ -82,7 +83,7 @@ func (w *Wallet) IsDIDExist(did string) bool {
return true
}
-func (w *Wallet) AddDIDPeerMap(did string, peerID string) error {
+func (w *Wallet) AddDIDPeerMap(did string, peerID string, didType int) error {
lastChar := string(did[len(did)-1])
var dm DIDPeerMap
err := w.s.Read(DIDStorage, &dm, "did=?", did)
@@ -94,12 +95,17 @@ func (w *Wallet) AddDIDPeerMap(did string, peerID string) error {
dm.DID = did
dm.PeerID = peerID
dm.DIDLastChar = lastChar
+ dm.DIDType = &didType
return w.s.Write(DIDPeerStorage, &dm)
}
if dm.PeerID != peerID {
dm.PeerID = peerID
return w.s.Update(DIDPeerStorage, &dm, "did=?", did)
}
+ if dm.DIDType == nil {
+ dm.DIDType = &didType
+ return w.s.Update(DIDPeerStorage, &dm, "did=?", did)
+ }
return nil
}
@@ -130,3 +136,36 @@ func (w *Wallet) GetPeerID(did string) string {
}
return dm.PeerID
}
+
+// Fetches did type of the given did from PeerDIDTable
+func (w *Wallet) GetPeerDIDType(did string) (int, error) {
+ var dm DIDPeerMap
+ err := w.s.Read(DIDPeerStorage, &dm, "did=?", did)
+ if err != nil {
+ w.log.Error("couldn't fetch did type from peer did table")
+ return -1, err
+ }
+ if dm.DIDType == nil {
+ return -1, nil
+ }
+ return *dm.DIDType, nil
+}
+
+// Updates did type of the given did in PeerDIDTable
+func (w *Wallet) UpdatePeerDIDType(did string, didtype int) (bool, error) {
+ var dm DIDPeerMap
+ err := w.s.Read(DIDPeerStorage, &dm, "did=?", did)
+ if err != nil {
+ w.log.Error("couldn't read from peer did table")
+ return false, err
+ }
+
+ dm.DIDType = &didtype
+
+ err1 := w.s.Update(DIDPeerStorage, &dm, "did=?", did)
+ if err1 != nil {
+ w.log.Error("couldn't update did type in peer did table")
+ return false, err1
+ }
+ return true, nil
+}
diff --git a/core/wallet/token.go b/core/wallet/token.go
index 09249119..7bfad11b 100644
--- a/core/wallet/token.go
+++ b/core/wallet/token.go
@@ -575,3 +575,21 @@ func (w *Wallet) ReleaseAllLockedTokens() error {
}
return nil
}
+
+func (w *Wallet) UnlockLockedTokens(did string, tokenList []string) error {
+ for _, tid := range tokenList {
+ var t Token
+ err := w.s.Read(TokenStorage, &t, "did=? AND token_id=?", did, tid)
+ if err != nil {
+ w.log.Error("Failed to update token status", "err", err)
+ return err
+ }
+ t.TokenStatus = TokenIsFree
+ err = w.s.Update(TokenStorage, &t, "did=? AND token_id=?", did, tid)
+ if err != nil {
+ w.log.Error("Failed to update token status", "err", err)
+ return err
+ }
+ }
+ return nil
+}
diff --git a/core/wallet/token_test.go b/core/wallet/token_test.go
index 237ff76b..1fd7b90c 100644
--- a/core/wallet/token_test.go
+++ b/core/wallet/token_test.go
@@ -49,7 +49,7 @@ func (d *DIDDummy) Sign(hash string) ([]byte, []byte, error) {
}
// Sign will verifyt he signature
-func (d *DIDDummy) Verify(hash string, pvtShareSig []byte, pvtKeySIg []byte) (bool, error) {
+func (d *DIDDummy) NlssVerify(hash string, pvtShareSig []byte, pvtKeySIg []byte) (bool, error) {
// read senderDID
_, pubKeyByte, err := crypto.DecodeKeyPair("", nil, d.pubKey)
diff --git a/crypto/bip39.go b/crypto/bip39.go
new file mode 100644
index 00000000..2b4af1b3
--- /dev/null
+++ b/crypto/bip39.go
@@ -0,0 +1,158 @@
+package crypto
+
+import (
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/rand"
+ "encoding/pem"
+ "fmt"
+
+ "github.com/tyler-smith/go-bip32"
+ "github.com/tyler-smith/go-bip39"
+
+ secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4"
+)
+
+const (
+ BIP39 = iota
+)
+
+// Generate child private and public keys for BIP39 masterkey and given path
+// The child private key is regenerated on demand from master key hence never stored
+// The child public key need to shared with other peers for verification
+// Make sure the path of child is also stored along with public key
+func BIPGenerateChild(masterKey string, childPath int, pwd string) ([]byte, []byte, error) {
+ masterKeybip32, err := bip32.B58Deserialize(masterKey)
+ if err != nil {
+ return nil, nil, err
+ }
+ privateKey, err := masterKeybip32.NewChildKey(uint32(childPath))
+ if err != nil {
+ return nil, nil, err
+ }
+
+ privKey := secp256k1.PrivKeyFromBytes(privateKey.Key)
+ privkeybyte := privKey.Serialize()
+
+ pubkeybyte := privKey.PubKey().SerializeUncompressed()
+ var pemEncPriv []byte
+ if pwd != "" {
+ encBlock, err := Seal(pwd, privkeybyte)
+ if err != nil {
+ return nil, nil, err
+ }
+ _, err = UnSeal(pwd, encBlock)
+ if err != nil {
+ return nil, nil, err
+ }
+ pemEncPriv = pem.EncodeToMemory(&pem.Block{Type: "ENCRYPTED PRIVATE KEY", Bytes: encBlock})
+ } else {
+ pemEncPriv = pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privkeybyte})
+ }
+ pemEncPub := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: pubkeybyte})
+
+ return pemEncPriv, pemEncPub, nil
+
+}
+
+// GenerateKeyPair will generate key pair based on the configuration
+func DecodeBIPKeyPair(pwd string, privKey []byte, pubKey []byte) ([]byte, []byte, error) {
+ var cryptoPrivKey []byte
+ var cryptoPubKey []byte
+ if privKey != nil {
+ pemBlock, _ := pem.Decode(privKey)
+ if pemBlock == nil {
+ return nil, nil, fmt.Errorf("invalid private key")
+ }
+ if pemBlock.Type == "ENCRYPTED PRIVATE KEY" {
+ if pwd == "" {
+ return nil, nil, fmt.Errorf("key is encrypted need password to decrypt")
+ }
+ decData, err := UnSeal(pwd, pemBlock.Bytes)
+ if err != nil {
+ return nil, nil, fmt.Errorf("key is invalid or password is wrong")
+ }
+ cryptoPrivKey = decData
+ } else {
+ cryptoPrivKey = pemBlock.Bytes
+ }
+ }
+ if pubKey != nil {
+ pemBlock, _ := pem.Decode(pubKey)
+ if pemBlock == nil {
+ return nil, nil, fmt.Errorf("invalid public key")
+ }
+ cryptoPubKey = pemBlock.Bytes
+ }
+
+ return cryptoPrivKey, cryptoPubKey, nil
+}
+
+// Generate BIPMasterKey from Mnemonic and user provided password
+// Useful in key recovery / device migration through mnemonics
+func BIPGenerateMasterKeyFromMnemonic(mnemonic string, pwd string) ([]byte, error) {
+ var masterkeySeralise string
+ seed := bip39.NewSeed(mnemonic, pwd)
+ masterKey, _ := bip32.NewMasterKey(seed)
+ masterkeySeralise = masterKey.B58Serialize()
+ return []byte(masterkeySeralise), nil
+}
+
+// Generate a random BIP mnemonic in rubix
+func BIPGenerateMnemonic() string {
+ entropy, _ := bip39.NewEntropy(256)
+ mnemonic, _ := bip39.NewMnemonic(entropy)
+ return mnemonic
+}
+
+// Generate a Bip32 HD wallet MasteKey for the mnemonic and a user provided randomness
+// here we are reusing the password used for sealing masterkey as source of randomness also
+func BIPGenerateMasterKey(cfg *CryptoConfig) ([]byte, error) {
+ var pemEncPriv []byte
+ var err error
+ if cfg.Alg == 0 {
+ mnemonic := BIPGenerateMnemonic()
+ pemEncPriv, err = BIPGenerateMasterKeyFromMnemonic(mnemonic, cfg.Pwd)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ return nil, fmt.Errorf("unsupported algorithm")
+ }
+ return pemEncPriv, nil
+}
+
+// Decode the master public key
+func BIPDecodeMasterKey(pwd string, privKey []byte) ([]byte, error) {
+ var cryptoPrivKey []byte
+ var err error
+ if privKey != nil {
+ pemBlock, _ := pem.Decode(privKey)
+ if pemBlock == nil {
+ return nil, fmt.Errorf("invalid private key")
+ }
+ if pemBlock.Type == "ENCRYPTED PRIVATE KEY" {
+ if pwd == "" {
+ return nil, fmt.Errorf("key is encrypted need password to decrypt")
+ }
+ cryptoPrivKey, err = UnSeal(pwd, pemBlock.Bytes)
+ if err != nil {
+ return nil, fmt.Errorf("key is invalid or password is wrong")
+ }
+ }
+ }
+ return cryptoPrivKey, nil
+}
+
+func BIPSign(priv PrivateKey, data []byte) ([]byte, error) {
+ return priv.(crypto.Signer).Sign(rand.Reader, data, crypto.SHA256)
+}
+
+func BIPVerify(pub PublicKey, data []byte, sig []byte) bool {
+ switch pub.(type) {
+ case *ecdsa.PublicKey:
+ return ecdsa.VerifyASN1(pub.(*ecdsa.PublicKey), data, sig)
+ default:
+ return false
+ }
+}
diff --git a/crypto/bip39_test.go b/crypto/bip39_test.go
new file mode 100644
index 00000000..2f2456db
--- /dev/null
+++ b/crypto/bip39_test.go
@@ -0,0 +1,49 @@
+package crypto
+
+import (
+ "crypto/rand"
+ "testing"
+
+ secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4"
+)
+
+func BIPtest(t *testing.T, mnemonic string, pwd string) {
+
+ masterKey, err := BIPGenerateMasterKeyFromMnemonic(mnemonic, pwd)
+ if err != nil {
+ t.Fatal("failed to generate key pair", "err", err)
+ }
+
+ priv, pub, err := BIPGenerateChild(string(masterKey), 0, pwd)
+ if err != nil {
+ t.Fatal("failed to generate child", "err", err)
+ }
+
+ privkey, pubkey, err := DecodeBIPKeyPair(pwd, priv, pub)
+ if err != nil {
+ t.Fatal("failed to decode key pair", "err", err)
+ }
+
+ data, err := GetRandBytes(rand.Reader, 20)
+ if err != nil {
+ t.Fatal("failed to generate random number", "err", err)
+ }
+
+ privkeyback := secp256k1.PrivKeyFromBytes(privkey)
+ privKeySer := privkeyback.ToECDSA()
+ pubkeyback, _ := secp256k1.ParsePubKey(pubkey)
+ pubKeySer := pubkeyback.ToECDSA()
+
+ sig, err := BIPSign(privKeySer, data)
+ if err != nil {
+ t.Fatal("failed to do signature", "err", err)
+ }
+
+ if !BIPVerify(pubKeySer, data, sig) {
+ t.Fatal("failed to do verify signature", "err", err)
+ }
+}
+func TestBIPKeyGeneration(t *testing.T) {
+ BIPtest(t, "cup symbol flee find decline market tube border artist clever make plastic unfold chaos float artwork sustain suspect risk process fox decrease west seven", "test")
+ BIPtest(t, "cub symbol flee find decline market tube border artist clever make plastic unfold chaos float artwork sustain suspect risk process fox decrease west seven", "test")
+}
diff --git a/crypto/crypto.go b/crypto/crypto.go
index dead956e..5bcb6e12 100644
--- a/crypto/crypto.go
+++ b/crypto/crypto.go
@@ -6,9 +6,13 @@ import (
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
+ "crypto/sha512"
"crypto/x509"
"encoding/pem"
"fmt"
+
+ "filippo.io/keygen"
+ "golang.org/x/crypto/hkdf"
)
// PublicKey represents a public key using an unspecified algorithm.
@@ -23,6 +27,7 @@ type CryptoAlgType int
const (
RSA2048 CryptoAlgType = iota
ECDSAP256
+ ECDSADET
)
// CryptoConfig is configuration for the crypto
@@ -43,6 +48,10 @@ func GenerateKeyPair(cfg *CryptoConfig) ([]byte, []byte, error) {
case ECDSAP256:
privKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
pubKey = &privKey.(*ecdsa.PrivateKey).PublicKey
+ case ECDSADET:
+ r := hkdf.New(sha512.New, []byte(cfg.Pwd), nil, nil)
+ privKey, _ = keygen.ECDSALegacy(elliptic.P256(), r)
+ pubKey = &privKey.(*ecdsa.PrivateKey).PublicKey
default:
return nil, nil, fmt.Errorf("unsupported algorithm")
}
diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go
index 968e3e7e..2f3d3170 100644
--- a/crypto/crypto_test.go
+++ b/crypto/crypto_test.go
@@ -32,5 +32,6 @@ func testKeyGeneration(t *testing.T, alg CryptoAlgType, pwd string) {
func TestKeyGeneration(t *testing.T) {
testKeyGeneration(t, ECDSAP256, "")
+ testKeyGeneration(t, ECDSADET, "test")
testKeyGeneration(t, ECDSAP256, "TestPassword")
}
diff --git a/did/basic.go b/did/basic.go
index d3f83290..51aa080a 100644
--- a/did/basic.go
+++ b/did/basic.go
@@ -63,6 +63,10 @@ func (d *DIDBasic) GetDID() string {
return d.did
}
+func (d *DIDBasic) GetSignType() int {
+ return NlssVersion
+}
+
// Sign will return the singature of the DID
func (d *DIDBasic) Sign(hash string) ([]byte, []byte, error) {
byteImg, err := util.GetPNGImagePixels(d.dir + PvtShareFileName)
@@ -107,7 +111,7 @@ func (d *DIDBasic) Sign(hash string) ([]byte, []byte, error) {
}
// Sign will verifyt he signature
-func (d *DIDBasic) Verify(hash string, pvtShareSig []byte, pvtKeySIg []byte) (bool, error) {
+func (d *DIDBasic) NlssVerify(hash string, pvtShareSig []byte, pvtKeySIg []byte) (bool, error) {
// read senderDID
didImg, err := util.GetPNGImagePixels(d.dir + DIDImgFileName)
if err != nil {
@@ -154,7 +158,7 @@ func (d *DIDBasic) Verify(hash string, pvtShareSig []byte, pvtKeySIg []byte) (bo
}
hashPvtSign := util.HexToStr(util.CalculateHash([]byte(pSig), "SHA3-256"))
if !crypto.Verify(pubKeyByte, []byte(hashPvtSign), pvtKeySIg) {
- return false, fmt.Errorf("failed to verify private key singature")
+ return false, fmt.Errorf("failed to verify nlss private key singature")
}
return true, nil
}
diff --git a/did/child.go b/did/child.go
index 229a8afd..e97da41d 100644
--- a/did/child.go
+++ b/did/child.go
@@ -64,6 +64,10 @@ func (d *DIDChild) GetDID() string {
return d.did
}
+func (d *DIDChild) GetSignType() int {
+ return NlssVersion
+}
+
// Sign will return the singature of the DID
func (d *DIDChild) Sign(hash string) ([]byte, []byte, error) {
@@ -113,7 +117,7 @@ func (d *DIDChild) Sign(hash string) ([]byte, []byte, error) {
}
// Sign will verifyt he signature
-func (d *DIDChild) Verify(hash string, pvtShareSig []byte, pvtKeySIg []byte) (bool, error) {
+func (d *DIDChild) NlssVerify(hash string, pvtShareSig []byte, pvtKeySIg []byte) (bool, error) {
rb, err := ioutil.ReadFile(d.dir + MasterDIDFileName)
if err != nil {
return false, err
diff --git a/did/did.go b/did/did.go
index 432dd889..7edf5ba7 100644
--- a/did/did.go
+++ b/did/did.go
@@ -7,10 +7,12 @@ import (
"fmt"
"image"
"io"
+ "io/ioutil"
"os"
"path/filepath"
"time"
+ secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4"
ipfsnode "github.com/ipfs/go-ipfs-api"
files "github.com/ipfs/go-ipfs-files"
"github.com/rubixchain/rubixgoplatform/crypto"
@@ -38,8 +40,9 @@ type DID struct {
type DIDCrypto interface {
GetDID() string
+ GetSignType() int
Sign(hash string) ([]byte, []byte, error)
- Verify(hash string, didSig []byte, pvtSig []byte) (bool, error)
+ NlssVerify(hash string, didSig []byte, pvtSig []byte) (bool, error)
PvtSign(hash []byte) ([]byte, error)
PvtVerify(hash []byte, sign []byte) (bool, error)
}
@@ -69,6 +72,95 @@ func (d *DID) CreateDID(didCreate *DIDCreate) (string, error) {
return "", err
}
+ //In lite mode, did is simply the SHA-256 hash of the public key
+ if didCreate.Type == LiteDIDMode {
+ if didCreate.PrivPWD == "" {
+ d.log.Error("password required for creating", "err", err)
+ return "", err
+ }
+
+ _mnemonic, err := os.ReadFile(didCreate.MnemonicFile)
+ if err != nil {
+ d.log.Debug("failed to read mnemonic file", "err", err)
+ }
+ var mnemonic string
+ if _mnemonic == nil {
+ mnemonic = crypto.BIPGenerateMnemonic()
+ } else {
+ mnemonic = string(_mnemonic)
+ }
+
+ masterKey, err := crypto.BIPGenerateMasterKeyFromMnemonic(mnemonic, didCreate.PrivPWD)
+ if err != nil {
+ d.log.Error("failed to create keypair", "err", err)
+ }
+
+ //generating private and public key pair
+ pvtKey, pubKey, err := crypto.BIPGenerateChild(string(masterKey), didCreate.ChildPath, didCreate.PrivPWD)
+ if err != nil {
+ d.log.Error("failed to create child", "err", err)
+ }
+
+ err = util.FileWrite(dirName+"/private/"+MnemonicFileName, []byte(mnemonic))
+ if err != nil {
+ d.log.Error("failed to write mnemonic file", "err", err)
+ return "", err
+ }
+
+ err = util.FileWrite(dirName+"/private/"+PvtKeyFileName, pvtKey)
+ if err != nil {
+ return "", err
+ }
+
+ err = util.FileWrite(dirName+"/public/"+PubKeyFileName, pubKey)
+ if err != nil {
+ return "", err
+ }
+
+ privKeyTest, err := ioutil.ReadFile(dirName + "/private/" + PvtKeyFileName)
+ if err != nil {
+ return "", err
+ }
+
+ Privkey, _, err := crypto.DecodeBIPKeyPair(didCreate.PrivPWD, privKeyTest, nil)
+ if err != nil {
+ return "", err
+ }
+
+ privkeyback := secp256k1.PrivKeyFromBytes(Privkey)
+ privKeySer := privkeyback.ToECDSA()
+ pvtKeySign, err := crypto.BIPSign(privKeySer, []byte("test"))
+ if err != nil {
+ return "", err
+ }
+ pubKeyTest, err := ioutil.ReadFile(dirName + "/public/" + PubKeyFileName)
+ if err != nil {
+ return "", err
+ }
+
+ _, pubKeyByte, err := crypto.DecodeBIPKeyPair("", nil, pubKeyTest)
+ if err != nil {
+ return "", err
+ }
+
+ pubkeyback, _ := secp256k1.ParsePubKey(pubKeyByte)
+ pubKeySer := pubkeyback.ToECDSA()
+ if !crypto.BIPVerify(pubKeySer, []byte("test"), pvtKeySign) {
+ return "", fmt.Errorf("failed to verify private key singature")
+ } else {
+ fmt.Println(" BIP sign tested successfully")
+ }
+
+ if didCreate.QuorumPWD == "" {
+ if didCreate.PrivPWD != "" {
+ didCreate.QuorumPWD = didCreate.PrivPWD
+ } else {
+ didCreate.QuorumPWD = DefaultPWD
+ }
+ }
+
+ }
+
if didCreate.Type == BasicDIDMode || didCreate.Type == StandardDIDMode {
f, err := os.Open(didCreate.ImgFile)
if err != nil {
@@ -157,20 +249,17 @@ func (d *DID) CreateDID(didCreate *DIDCreate) (string, error) {
d.log.Error("failed to create keypair", "err", err)
return "", err
}
+
err = util.FileWrite(dirName+"/private/"+PvtKeyFileName, pvtKey)
if err != nil {
return "", err
}
+
err = util.FileWrite(dirName+"/public/"+PubKeyFileName, pubKey)
if err != nil {
return "", err
}
- } else {
- _, err := util.Filecopy(didCreate.PubKeyFile, dirName+"/public/"+PubKeyFileName)
- if err != nil {
- d.log.Error("failed to copy pub key", "err", err)
- return "", err
- }
+
}
if didCreate.Type == ChildDIDMode {
@@ -181,7 +270,7 @@ func (d *DID) CreateDID(didCreate *DIDCreate) (string, error) {
if err != nil {
return "", err
}
- } else {
+ } else if didCreate.Type != LiteDIDMode {
if didCreate.QuorumPWD == "" {
if didCreate.PrivPWD != "" {
didCreate.QuorumPWD = didCreate.PrivPWD
@@ -205,6 +294,7 @@ func (d *DID) CreateDID(didCreate *DIDCreate) (string, error) {
}
}
+ //passing the diroctory of public key file to add it to ipfs and exctract the hash
did, err := d.getDirHash(dirName + "/public/")
if err != nil {
return "", err
@@ -252,21 +342,24 @@ func (d *DID) MigrateDID(didCreate *DIDCreate) (string, error) {
return "", err
}
- _, err = util.Filecopy(didCreate.DIDImgFileName, dirName+"/public/"+DIDImgFileName)
- if err != nil {
- d.log.Error("failed to copy did image", "err", err)
- return "", err
- }
- _, err = util.Filecopy(didCreate.PubImgFile, dirName+"/public/"+PubShareFileName)
- if err != nil {
- d.log.Error("failed to copy public share", "err", err)
- return "", err
- }
- _, err = util.Filecopy(didCreate.PrivImgFile, dirName+"/private/"+PvtShareFileName)
- if err != nil {
- d.log.Error("failed to copy private share key", "err", err)
- return "", err
+ if didCreate.Type != LiteDIDMode {
+ _, err = util.Filecopy(didCreate.DIDImgFileName, dirName+"/public/"+DIDImgFileName)
+ if err != nil {
+ d.log.Error("failed to copy did image", "err", err)
+ return "", err
+ }
+ _, err = util.Filecopy(didCreate.PubImgFile, dirName+"/public/"+PubShareFileName)
+ if err != nil {
+ d.log.Error("failed to copy public share", "err", err)
+ return "", err
+ }
+ _, err = util.Filecopy(didCreate.PrivImgFile, dirName+"/private/"+PvtShareFileName)
+ if err != nil {
+ d.log.Error("failed to copy private share key", "err", err)
+ return "", err
+ }
}
+
if didCreate.Type == BasicDIDMode {
if didCreate.PrivKeyFile == "" || didCreate.PubKeyFile == "" {
if didCreate.PrivPWD == "" {
@@ -313,28 +406,31 @@ func (d *DID) MigrateDID(didCreate *DIDCreate) (string, error) {
didCreate.QuorumPWD = DefaultPWD
}
}
- if didCreate.QuorumPrivKeyFile == "" || didCreate.QuorumPubKeyFile == "" {
- pvtKey, pubKey, err := crypto.GenerateKeyPair(&crypto.CryptoConfig{Alg: crypto.ECDSAP256, Pwd: didCreate.QuorumPWD})
- if err != nil {
- d.log.Error("failed to create keypair", "err", err)
- return "", err
- }
- err = util.FileWrite(dirName+"/private/"+QuorumPvtKeyFileName, pvtKey)
- if err != nil {
- return "", err
- }
- err = util.FileWrite(dirName+"/public/"+QuorumPubKeyFileName, pubKey)
- if err != nil {
- return "", err
- }
- } else {
- _, err = util.Filecopy(didCreate.QuorumPrivKeyFile, dirName+"/private/"+QuorumPvtKeyFileName)
- if err != nil {
- return "", err
- }
- _, err = util.Filecopy(didCreate.QuorumPubKeyFile, dirName+"/public/"+QuorumPubKeyFileName)
- if err != nil {
- return "", err
+
+ if didCreate.Type != LiteDIDMode {
+ if didCreate.QuorumPrivKeyFile == "" || didCreate.QuorumPubKeyFile == "" {
+ pvtKey, pubKey, err := crypto.GenerateKeyPair(&crypto.CryptoConfig{Alg: crypto.ECDSAP256, Pwd: didCreate.QuorumPWD})
+ if err != nil {
+ d.log.Error("failed to create keypair", "err", err)
+ return "", err
+ }
+ err = util.FileWrite(dirName+"/private/"+QuorumPvtKeyFileName, pvtKey)
+ if err != nil {
+ return "", err
+ }
+ err = util.FileWrite(dirName+"/public/"+QuorumPubKeyFileName, pubKey)
+ if err != nil {
+ return "", err
+ }
+ } else {
+ _, err = util.Filecopy(didCreate.QuorumPrivKeyFile, dirName+"/private/"+QuorumPvtKeyFileName)
+ if err != nil {
+ return "", err
+ }
+ _, err = util.Filecopy(didCreate.QuorumPubKeyFile, dirName+"/public/"+QuorumPubKeyFileName)
+ if err != nil {
+ return "", err
+ }
}
}
@@ -373,21 +469,29 @@ type object struct {
Hash string
}
+// Calculate the hash of a directory using IPFS
func (d *DID) getDirHash(dir string) (string, error) {
+ // Get information about the directory
stat, err := os.Lstat(dir)
if err != nil {
return "", err
}
+ // Create a new SerialFile using the directory information
sf, err := files.NewSerialFile(dir, false, stat)
if err != nil {
return "", err
}
defer sf.Close()
+
+ // Create a new SliceDirectory with the SerialFile
slf := files.NewSliceDirectory([]files.DirEntry{files.FileEntry(filepath.Base(dir), sf)})
defer slf.Close()
+
+ // Create a MultiFileReader with the SliceDirectory
reader := files.NewMultiFileReader(slf, true)
+ // Send a request to IPFS to add the directory
resp, err := d.ipfs.Request("add").
Option("recursive", true).
Option("cid-version", 1).
@@ -400,10 +504,13 @@ func (d *DID) getDirHash(dir string) (string, error) {
defer resp.Close()
+ // Check for errors in the response
if resp.Error != nil {
return "", resp.Error
}
defer resp.Output.Close()
+
+ // Decode the JSON response and extract the hash
dec := json.NewDecoder(resp.Output)
var final string
for {
@@ -418,6 +525,7 @@ func (d *DID) getDirHash(dir string) (string, error) {
final = out.Hash
}
+ // Check if the final hash is empty
if final == "" {
return "", errors.New("no results received")
}
diff --git a/did/dummy.go b/did/dummy.go
index be1b1667..4b098ac0 100644
--- a/did/dummy.go
+++ b/did/dummy.go
@@ -40,7 +40,7 @@ func (d *DIDDummy) Sign(hash string) ([]byte, []byte, error) {
}
// Sign will verifyt he signature
-func (d *DIDDummy) Verify(hash string, pvtShareSig []byte, pvtKeySIg []byte) (bool, error) {
+func (d *DIDDummy) NlssVerify(hash string, pvtShareSig []byte, pvtKeySIg []byte) (bool, error) {
// read senderDID
_, pubKeyByte, err := crypto.DecodeKeyPair("", nil, d.pubKey)
diff --git a/did/lite.go b/did/lite.go
new file mode 100644
index 00000000..beccd88a
--- /dev/null
+++ b/did/lite.go
@@ -0,0 +1,181 @@
+package did
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "time"
+
+ secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4"
+ "github.com/rubixchain/rubixgoplatform/crypto"
+ "github.com/rubixchain/rubixgoplatform/nlss"
+ "github.com/rubixchain/rubixgoplatform/util"
+)
+
+// DIDLite will handle Light DID
+type DIDLite struct {
+ did string
+ dir string
+ ch *DIDChan
+ pwd string
+}
+
+// InitDIDLite will return the Light did handle
+func InitDIDLite(did string, baseDir string, ch *DIDChan) *DIDLite {
+ return &DIDLite{did: did, dir: util.SanitizeDirPath(baseDir) + did + "/", ch: ch}
+}
+
+func InitDIDLiteWithPassword(did string, baseDir string, pwd string) *DIDLite {
+ return &DIDLite{did: did, dir: util.SanitizeDirPath(baseDir) + did + "/", pwd: pwd}
+}
+
+func (d *DIDLite) getPassword() (string, error) {
+ if d.pwd != "" {
+ return d.pwd, nil
+ }
+ if d.ch == nil || d.ch.InChan == nil || d.ch.OutChan == nil {
+ return "", fmt.Errorf("Invalid configuration")
+ }
+ sr := &SignResponse{
+ Status: true,
+ Message: "Password needed",
+ Result: SignReqData{
+ ID: d.ch.ID,
+ Mode: LiteDIDMode,
+ },
+ }
+ d.ch.OutChan <- sr
+ var ch interface{}
+ select {
+ case ch = <-d.ch.InChan:
+ case <-time.After(d.ch.Timeout):
+ return "", fmt.Errorf("Timeout, failed to get password")
+ }
+
+ srd, ok := ch.(SignRespData)
+ if !ok {
+ return "", fmt.Errorf("Invalid data received on the channel")
+ }
+ d.pwd = srd.Password
+ return d.pwd, nil
+}
+
+func (d *DIDLite) GetDID() string {
+ return d.did
+}
+
+// When the did creation and signing is done in Light mode,
+// this function returns the sign version as BIPVersion = 0
+func (d *DIDLite) GetSignType() int {
+ return BIPVersion
+}
+
+// PKI based sign in lite mode
+// In lite mode, the sign function returns only the private signature, unlike the basic mode
+func (d *DIDLite) Sign(hash string) ([]byte, []byte, error) {
+ pvtKeySign, err := d.PvtSign([]byte(hash))
+ bs := []byte{}
+
+ return bs, pvtKeySign, err
+}
+
+// verify nlss based signatures
+func (d *DIDLite) NlssVerify(hash string, pvtShareSig []byte, pvtKeySIg []byte) (bool, error) {
+ //read senderDID
+ didImg, err := util.GetPNGImagePixels(d.dir + DIDImgFileName)
+ if err != nil {
+ return false, err
+ }
+ pubImg, err := util.GetPNGImagePixels(d.dir + PubShareFileName)
+
+ if err != nil {
+ return false, err
+ }
+
+ pSig := util.BytesToBitstream(pvtShareSig)
+
+ ps := util.StringToIntArray(pSig)
+
+ didBin := util.ByteArraytoIntArray(didImg)
+ pubBin := util.ByteArraytoIntArray(pubImg)
+ pubPos := util.RandomPositions("verifier", hash, 32, ps)
+ pubPosInt := util.GetPrivatePositions(pubPos.PosForSign, pubBin)
+ pubStr := util.IntArraytoStr(pubPosInt)
+ orgPos := make([]int, len(pubPos.OriginalPos))
+ for i := range pubPos.OriginalPos {
+ orgPos[i] = pubPos.OriginalPos[i] / 8
+ }
+ didPosInt := util.GetPrivatePositions(orgPos, didBin)
+ didStr := util.IntArraytoStr(didPosInt)
+ cb := nlss.Combine2Shares(nlss.ConvertBitString(pSig), nlss.ConvertBitString(pubStr))
+
+ db := nlss.ConvertBitString(didStr)
+
+ if !bytes.Equal(cb, db) {
+ return false, fmt.Errorf("failed to verify")
+ }
+
+ //create a signature using the private key
+ //1. read and extrqct the private key
+ pubKey, err := ioutil.ReadFile(d.dir + PubKeyFileName)
+ if err != nil {
+ return false, err
+ }
+
+ _, pubKeyByte, err := crypto.DecodeKeyPair("", nil, pubKey)
+ if err != nil {
+ return false, err
+ }
+
+ hashPvtSign := util.HexToStr(util.CalculateHash([]byte(pSig), "SHA3-256"))
+ if !crypto.Verify(pubKeyByte, []byte(hashPvtSign), pvtKeySIg) {
+ return false, fmt.Errorf("failed to verify private key singature")
+ }
+ return true, nil
+
+}
+
+func (d *DIDLite) PvtSign(hash []byte) ([]byte, error) {
+ privKey, err := ioutil.ReadFile(d.dir + PvtKeyFileName)
+ if err != nil {
+ return nil, err
+ }
+
+ pwd, err := d.getPassword()
+ if err != nil {
+ return nil, err
+ }
+
+ Privatekey, _, err := crypto.DecodeBIPKeyPair(pwd, privKey, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ privkeyback := secp256k1.PrivKeyFromBytes(Privatekey)
+ privKeySer := privkeyback.ToECDSA()
+ pvtKeySign, err := crypto.BIPSign(privKeySer, hash)
+ if err != nil {
+ return nil, err
+ }
+ return pvtKeySign, nil
+}
+
+// Verify PKI based signature
+func (d *DIDLite) PvtVerify(hash []byte, sign []byte) (bool, error) {
+ pubKey, err := ioutil.ReadFile(d.dir + PubKeyFileName)
+ if err != nil {
+ return false, err
+ }
+
+ _, pubKeyByte, err := crypto.DecodeBIPKeyPair("", nil, pubKey)
+ if err != nil {
+ return false, err
+ }
+
+ pubkeyback, _ := secp256k1.ParsePubKey(pubKeyByte)
+ pubKeySer := pubkeyback.ToECDSA()
+ if !crypto.BIPVerify(pubKeySer, hash, sign) {
+ return false, fmt.Errorf("failed to verify private key singature")
+ }
+ return true, nil
+}
diff --git a/did/model.go b/did/model.go
index 1ff71eb0..bf18b21f 100644
--- a/did/model.go
+++ b/did/model.go
@@ -5,6 +5,12 @@ const (
StandardDIDMode
WalletDIDMode
ChildDIDMode
+ LiteDIDMode
+)
+
+const (
+ BIPVersion int = iota
+ NlssVersion
)
const (
@@ -17,6 +23,7 @@ const (
PubKeyFileName string = "pubKey.pem"
QuorumPvtKeyFileName string = "quorumPrivKey.pem"
QuorumPubKeyFileName string = "quorumPubKey.pem"
+ MnemonicFileName string = "mnemonic.txt"
)
const (
@@ -40,6 +47,8 @@ type DIDCreate struct {
PrivKeyFile string `json:"priv_key_file"`
QuorumPubKeyFile string `json:"quorum_pub_key_file"`
QuorumPrivKeyFile string `json:"quorum_priv_key_file"`
+ MnemonicFile string `json:"mnemonic_file"`
+ ChildPath int `json:"childPath"`
}
type DIDSignature struct {
diff --git a/did/quorum.go b/did/quorum.go
index 3542f5e7..d1955d8c 100644
--- a/did/quorum.go
+++ b/did/quorum.go
@@ -48,6 +48,10 @@ func (d *DIDQuorum) GetDID() string {
return d.did
}
+func (d *DIDQuorum) GetSignType() int {
+ return NlssVersion
+}
+
// Sign will return the singature of the DID
func (d *DIDQuorum) Sign(hash string) ([]byte, []byte, error) {
if d.privKey == nil {
@@ -67,6 +71,7 @@ func (d *DIDQuorum) Sign(hash string) ([]byte, []byte, error) {
finalPos := randPosObject.PosForSign
pvtPos := util.GetPrivatePositions(finalPos, ps)
pvtPosStr := util.IntArraytoStr(pvtPos)
+
hashPvtSign := util.HexToStr(util.CalculateHash([]byte(pvtPosStr), "SHA3-256"))
pvtKeySign, err := crypto.Sign(d.privKey, []byte(hashPvtSign))
if err != nil {
@@ -79,8 +84,8 @@ func (d *DIDQuorum) Sign(hash string) ([]byte, []byte, error) {
return bs, pvtKeySign, err
}
-// Sign will verifyt he signature
-func (d *DIDQuorum) Verify(hash string, pvtShareSig []byte, pvtKeySIg []byte) (bool, error) {
+// verify the quorum's nlss based signature
+func (d *DIDQuorum) NlssVerify(hash string, pvtShareSig []byte, pvtKeySIg []byte) (bool, error) {
// read senderDID
didImg, err := util.GetPNGImagePixels(d.dir + DIDImgFileName)
if err != nil {
@@ -116,7 +121,7 @@ func (d *DIDQuorum) Verify(hash string, pvtShareSig []byte, pvtKeySIg []byte) (b
}
hashPvtSign := util.HexToStr(util.CalculateHash([]byte(pSig), "SHA3-256"))
if !crypto.Verify(d.pubKey, []byte(hashPvtSign), pvtKeySIg) {
- return false, fmt.Errorf("failed to verify private key singature")
+ return false, fmt.Errorf("failed to verify nlss private key singature")
}
return true, nil
}
diff --git a/did/quorum_lite.go b/did/quorum_lite.go
new file mode 100644
index 00000000..a56f7172
--- /dev/null
+++ b/did/quorum_lite.go
@@ -0,0 +1,160 @@
+package did
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+
+ secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4"
+ "github.com/rubixchain/rubixgoplatform/crypto"
+ "github.com/rubixchain/rubixgoplatform/nlss"
+ "github.com/rubixchain/rubixgoplatform/util"
+)
+
+// DIDQuorum_Lt will handle lite DID
+type DIDQuorum_Lt struct {
+ did string
+ dir string
+ pwd string
+ privKey crypto.PrivateKey
+ pubKey crypto.PublicKey
+}
+
+// InitDIDQuorum_Lt will return the Quorum did handle in lite mode
+func InitDIDQuorum_Lt(did string, baseDir string, pwd string) *DIDQuorum_Lt {
+ d := &DIDQuorum_Lt{did: did, dir: util.SanitizeDirPath(baseDir) + did + "/", pwd: pwd}
+ if d.pwd != "" {
+ privKey, err := ioutil.ReadFile(d.dir + PvtKeyFileName)
+ if err != nil {
+ return nil
+ }
+ d.privKey, _, err = crypto.DecodeBIPKeyPair(d.pwd, privKey, nil)
+ if err != nil {
+ return nil
+ }
+ }
+
+ pubKey, err := ioutil.ReadFile(d.dir + PubKeyFileName)
+ if err != nil {
+ return nil
+ }
+ _, d.pubKey, err = crypto.DecodeBIPKeyPair("", nil, pubKey)
+ if err != nil {
+ return nil
+ }
+ return d
+}
+
+func (d *DIDQuorum_Lt) GetDID() string {
+ return d.did
+}
+
+func (d *DIDQuorum_Lt) GetSignType() int {
+ return BIPVersion
+}
+
+// Sign will return the singature of the DID
+func (d *DIDQuorum_Lt) Sign(hash string) ([]byte, []byte, error) {
+ pvtKeySign, err := d.PvtSign([]byte(hash))
+ // byteImg, err := util.GetPNGImagePixels(d.dir + PvtShareFileName)
+
+ if err != nil {
+ fmt.Println(err)
+ return nil, nil, err
+ }
+
+ bs := []byte{}
+ return bs, pvtKeySign, err
+}
+
+// verify the quorum's nlss based signature
+func (d *DIDQuorum_Lt) NlssVerify(hash string, pvtShareSig []byte, pvtKeySIg []byte) (bool, error) {
+ // read senderDID
+ didImg, err := util.GetPNGImagePixels(d.dir + DIDImgFileName)
+ if err != nil {
+ return false, err
+ }
+ pubImg, err := util.GetPNGImagePixels(d.dir + PubShareFileName)
+
+ if err != nil {
+ return false, err
+ }
+
+ pSig := util.BytesToBitstream(pvtShareSig)
+
+ ps := util.StringToIntArray(pSig)
+
+ didBin := util.ByteArraytoIntArray(didImg)
+ pubBin := util.ByteArraytoIntArray(pubImg)
+ pubPos := util.RandomPositions("verifier", hash, 32, ps)
+ pubPosInt := util.GetPrivatePositions(pubPos.PosForSign, pubBin)
+ pubStr := util.IntArraytoStr(pubPosInt)
+ orgPos := make([]int, len(pubPos.OriginalPos))
+ for i := range pubPos.OriginalPos {
+ orgPos[i] = pubPos.OriginalPos[i] / 8
+ }
+ didPosInt := util.GetPrivatePositions(orgPos, didBin)
+ didStr := util.IntArraytoStr(didPosInt)
+ cb := nlss.Combine2Shares(nlss.ConvertBitString(pSig), nlss.ConvertBitString(pubStr))
+
+ db := nlss.ConvertBitString(didStr)
+
+ if !bytes.Equal(cb, db) {
+ return false, fmt.Errorf("failed to verify")
+ }
+
+ hashPvtSign := util.HexToStr(util.CalculateHash([]byte(pSig), "SHA3-256"))
+
+ pubKey, err := ioutil.ReadFile(d.dir + PubKeyFileName)
+ if err != nil {
+ return false, err
+ }
+
+ _, pubKeyByte, err := crypto.DecodeKeyPair("", nil, pubKey)
+ if err != nil {
+ return false, err
+ }
+
+ if !crypto.Verify(pubKeyByte, []byte(hashPvtSign), pvtKeySIg) {
+ return false, fmt.Errorf("failed to verify private key singature")
+ }
+ return true, nil
+}
+func (d *DIDQuorum_Lt) PvtSign(hash []byte) ([]byte, error) {
+ privKey, err := ioutil.ReadFile(d.dir + PvtKeyFileName)
+ if err != nil {
+ return nil, err
+ }
+
+ Privatekey, _, err := crypto.DecodeBIPKeyPair(d.pwd, privKey, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ privkeyback := secp256k1.PrivKeyFromBytes(Privatekey)
+ privKeySer := privkeyback.ToECDSA()
+ pvtKeySign, err := crypto.BIPSign(privKeySer, hash)
+ if err != nil {
+ return nil, err
+ }
+ return pvtKeySign, nil
+}
+func (d *DIDQuorum_Lt) PvtVerify(hash []byte, sign []byte) (bool, error) {
+ pubKey, err := ioutil.ReadFile(d.dir + PubKeyFileName)
+ if err != nil {
+ return false, err
+ }
+
+ _, pubKeyByte, err := crypto.DecodeBIPKeyPair("", nil, pubKey)
+ if err != nil {
+ return false, err
+ }
+
+ pubkeyback, _ := secp256k1.ParsePubKey(pubKeyByte)
+ pubKeySer := pubkeyback.ToECDSA()
+
+ if !crypto.BIPVerify(pubKeySer, hash, sign) {
+ return false, fmt.Errorf("failed to verify private key singature")
+ }
+ return true, nil
+}
diff --git a/did/standard.go b/did/standard.go
index ecb2466d..d4dbe999 100644
--- a/did/standard.go
+++ b/did/standard.go
@@ -55,6 +55,10 @@ func (d *DIDStandard) GetDID() string {
return d.did
}
+func (d *DIDStandard) GetSignType() int {
+ return NlssVersion
+}
+
// Sign will return the singature of the DID
func (d *DIDStandard) Sign(hash string) ([]byte, []byte, error) {
byteImg, err := util.GetPNGImagePixels(d.dir + PvtShareFileName)
@@ -84,7 +88,7 @@ func (d *DIDStandard) Sign(hash string) ([]byte, []byte, error) {
}
// Sign will verifyt he signature
-func (d *DIDStandard) Verify(hash string, pvtShareSig []byte, pvtKeySIg []byte) (bool, error) {
+func (d *DIDStandard) NlssVerify(hash string, pvtShareSig []byte, pvtKeySIg []byte) (bool, error) {
// read senderDID
didImg, err := util.GetPNGImagePixels(d.dir + DIDImgFileName)
if err != nil {
diff --git a/did/wallet.go b/did/wallet.go
index 1a07f8c0..77fc3a77 100644
--- a/did/wallet.go
+++ b/did/wallet.go
@@ -56,6 +56,10 @@ func (d *DIDWallet) GetDID() string {
return d.did
}
+func (d *DIDWallet) GetSignType() int {
+ return NlssVersion
+}
+
// Sign will return the singature of the DID
func (d *DIDWallet) Sign(hash string) ([]byte, []byte, error) {
bs, pvtKeySign, err := d.getSignature([]byte(hash), false)
@@ -66,7 +70,7 @@ func (d *DIDWallet) Sign(hash string) ([]byte, []byte, error) {
}
// Sign will verifyt he signature
-func (d *DIDWallet) Verify(hash string, pvtShareSig []byte, pvtKeySIg []byte) (bool, error) {
+func (d *DIDWallet) NlssVerify(hash string, pvtShareSig []byte, pvtKeySIg []byte) (bool, error) {
// read senderDID
didImg, err := util.GetPNGImagePixels(d.dir + DIDImgFileName)
if err != nil {
diff --git a/go.mod b/go.mod
index 1c8877bd..7d0ff4ba 100644
--- a/go.mod
+++ b/go.mod
@@ -16,7 +16,10 @@ require (
)
require (
+ filippo.io/keygen v0.0.0-20230306160926-5201437acf8e // indirect
github.com/btcsuite/btcd v0.23.0 // indirect
+ github.com/decred/dcrd/dcrec/secp256k1 v1.0.4
+ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/denisenkom/go-mssqldb v0.12.3 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/fatih/color v1.15.0
@@ -47,6 +50,8 @@ require (
github.com/stretchr/testify v1.8.3 // indirect
github.com/swaggo/files v1.0.0 // indirect
github.com/swaggo/http-swagger v1.3.4
+ github.com/tyler-smith/go-bip32 v1.0.0
+ github.com/tyler-smith/go-bip39 v1.1.0
github.com/whyrusleeping/tar-utils v0.0.0-20201201191210-20a61371de5b // indirect
golang.org/x/net v0.10.0
golang.org/x/tools v0.9.3 // indirect
diff --git a/go.sum b/go.sum
index f090b70a..37458085 100644
--- a/go.sum
+++ b/go.sum
@@ -592,6 +592,10 @@ cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vf
cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA=
cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+filippo.io/bigmod v0.0.1 h1:OaEqDr3gEbofpnHbGqZweSL/bLMhy1pb54puiCDeuOA=
+filippo.io/bigmod v0.0.1/go.mod h1:KyzqAbH7bRH6MOuOF1TPfUjvLoi0mRF2bIyD2ouRNQI=
+filippo.io/keygen v0.0.0-20230306160926-5201437acf8e h1:+xwUCyMiCWKWsI0RowhzB4sngpUdMHgU6lLuWJCX5Dg=
+filippo.io/keygen v0.0.0-20230306160926-5201437acf8e/go.mod h1:ZGSiF/b2hd6MRghF/cid0vXw8pXykRTmIu+JSPw/NCQ=
gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
@@ -606,6 +610,10 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/EnsurityTechnologies/helper v1.0.0 h1:x6kaCFvqH35/P8eD8Vkx/IUUkbvDqyh4KVwqDJWIfWA=
github.com/EnsurityTechnologies/helper v1.0.0/go.mod h1:KESBOBjgzlkimm3dDwr49wjbMXWtaoqrA3SiwhxGDwQ=
+github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e h1:ahyvB3q25YnZWly5Gq1ekg6jcmWaGj/vG/MhF4aisoc=
+github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw=
+github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec h1:1Qb69mGp/UtRPn422BH4/Y4Q3SLUrD9KHuDkm8iodFc=
+github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U=
github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
@@ -662,6 +670,7 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:P13beTBKr5Q18lJe1rIoLUqjM+CB1zYrRg44ZqGuQSA=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@@ -683,8 +692,13 @@ github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/decred/dcrd/chaincfg/chainhash v1.0.2/go.mod h1:BpbrGgrPTr3YJYRN3Bm+D9NuaFd+zGyNeIKgrhCXK60=
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
+github.com/decred/dcrd/dcrec/secp256k1 v1.0.4 h1:0XErmfJBiVbl0NvyclGn4jr+1hIylDf5beFi9W0o7Fc=
+github.com/decred/dcrd/dcrec/secp256k1 v1.0.4/go.mod h1:00z7mJdugt+GBAzPN1QrDRGCXxyKUiexEHu6ukxEw3k=
+github.com/decred/dcrd/dcrec/secp256k1/v2 v2.0.0 h1:3GIJYXQDAKpLEFriGFN8SbSffak10UXHGdIcFaMPykY=
+github.com/decred/dcrd/dcrec/secp256k1/v2 v2.0.0/go.mod h1:3s92l0paYkZoIHuj4X93Teg/HB7eGM9x/zokGw+u4mY=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
@@ -1085,6 +1099,7 @@ github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcD
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.1.5-0.20170601210322-f6abca593680/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -1106,6 +1121,10 @@ github.com/swaggo/swag v1.16.1 h1:fTNRhKstPKxcnoKsytm4sahr8FaYzUcT7i1/3nd/fBg=
github.com/swaggo/swag v1.16.1/go.mod h1:9/LMvHycG3NFHfR6LwvikHv5iFvmPADQ359cKikGxto=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
+github.com/tyler-smith/go-bip32 v1.0.0 h1:sDR9juArbUgX+bO/iblgZnMPeWY1KZMUC2AFUJdv5KE=
+github.com/tyler-smith/go-bip32 v1.0.0/go.mod h1:onot+eHknzV4BVPwrzqY5OoVpyCvnwD7lMawL5aQupE=
+github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
+github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/whyrusleeping/tar-utils v0.0.0-20180509141711-8c6c8ba81d5c/go.mod h1:xxcJeBb7SIUl/Wzkz1eVKJE/CB34YNrqX2TQI6jY9zs=
github.com/whyrusleeping/tar-utils v0.0.0-20201201191210-20a61371de5b h1:wA3QeTsaAXybLL2kb2cKhCAQTHgYTMwuI8lBlJSv5V8=
@@ -1132,6 +1151,7 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
+golang.org/x/crypto v0.0.0-20170613210332-850760c427c5/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -1156,6 +1176,7 @@ golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
+golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1838,6 +1859,7 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
+launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM=
lukechampine.com/blake3 v1.1.6 h1:H3cROdztr7RCfoaTpGZFQsrqvweFLrqS73j7L7cmR5c=
lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
diff --git a/grpcclient/command.go b/grpcclient/command.go
index b52c1a02..92cc2e2b 100644
--- a/grpcclient/command.go
+++ b/grpcclient/command.go
@@ -114,6 +114,7 @@ type Command struct {
grpcAddr string
grpcPort int
grpcSecure bool
+ mnemonicFile string
}
func showVersion() {
@@ -181,6 +182,7 @@ func runCommand() {
flag.StringVar(&cmd.didImgFile, "didImgFile", did.DIDImgFileName, "DID image")
flag.StringVar(&cmd.privImgFile, "privImgFile", did.PvtShareFileName, "DID public share image")
flag.StringVar(&cmd.pubImgFile, "pubImgFile", did.PubShareFileName, "DID public share image")
+ flag.StringVar(&cmd.mnemonicFile, "mnemonicKeyFile", did.MnemonicFileName, "Mnemonic key file")
flag.StringVar(&cmd.privKeyFile, "privKeyFile", did.PvtKeyFileName, "Private key file")
flag.StringVar(&cmd.pubKeyFile, "pubKeyFile", did.PubKeyFileName, "Public key file")
flag.StringVar(&cmd.quorumList, "quorumList", "quorumlist.json", "Quorum list")
diff --git a/grpcclient/token.go b/grpcclient/token.go
index 37c29126..77370d25 100644
--- a/grpcclient/token.go
+++ b/grpcclient/token.go
@@ -34,6 +34,8 @@ func (cmd *Command) GenerateRBT() {
Mode: br.SignRequest.Mode,
}
switch int(br.SignRequest.Mode) {
+ case did.LiteDIDMode:
+ resp.Password = cmd.privPWD
case did.BasicDIDMode:
resp.Password = cmd.privPWD
case did.ChildDIDMode:
diff --git a/grpcserver/did.go b/grpcserver/did.go
index 62e3bca2..21606ed0 100644
--- a/grpcserver/did.go
+++ b/grpcserver/did.go
@@ -84,22 +84,26 @@ func (rn *RubixNative) CreateDID(ctx context.Context, req *protos.CreateDIDReq)
return nil, status.Errorf(codes.Internal, "failed to create folder")
}
defer os.RemoveAll(folderName)
- if req.DidImage != "" {
- err = createFile(folderName+"/"+did.DIDImgFileName, req.DidImage, true)
- if err != nil {
- rn.log.Error(err.Error())
- return nil, status.Errorf(codes.Internal, err.Error())
+
+ if dc.Type != did.LiteDIDMode {
+ if req.DidImage != "" {
+ err = createFile(folderName+"/"+did.DIDImgFileName, req.DidImage, true)
+ if err != nil {
+ rn.log.Error(err.Error())
+ return nil, status.Errorf(codes.Internal, err.Error())
+ }
+ dc.ImgFile = folderName + "/" + did.DIDImgFileName
}
- dc.ImgFile = folderName + "/" + did.DIDImgFileName
- }
- if req.PublicShare != "" {
- err = createFile(folderName+"/"+did.PubShareFileName, req.PublicShare, true)
- if err != nil {
- rn.log.Error(err.Error())
- return nil, status.Errorf(codes.Internal, err.Error())
+ if req.PublicShare != "" {
+ err = createFile(folderName+"/"+did.PubShareFileName, req.PublicShare, true)
+ if err != nil {
+ rn.log.Error(err.Error())
+ return nil, status.Errorf(codes.Internal, err.Error())
+ }
+ dc.PubImgFile = folderName + "/" + did.PubShareFileName
}
- dc.PubImgFile = folderName + "/" + did.PubShareFileName
}
+
if req.PublicKey != "" {
err = createFile(folderName+"/"+did.PubKeyFileName, req.PublicKey, false)
if err != nil {
diff --git a/server/config.go b/server/config.go
index e6b7f959..f4aa3c63 100644
--- a/server/config.go
+++ b/server/config.go
@@ -112,3 +112,43 @@ func (s *Server) APISetupDB(req *ensweb.Request) *ensweb.Result {
}
return s.BasicResponse(req, true, "DB setup done successfully", nil)
}
+
+// APIGetAllExplorer will get all explorer URLs from the db
+func (s *Server) APIGetAllExplorer(req *ensweb.Request) *ensweb.Result {
+ links, err := s.c.GetAllExplorer()
+ if err != nil {
+ return s.BasicResponse(req, false, "Failed to get explorer urls"+err.Error(), nil)
+ }
+ m := model.ExplorerLinks{
+ Links: links,
+ }
+ return s.BasicResponse(req, true, "Got all the explorer URLs successfully", m)
+}
+
+// APIAddExplorer will add bootstrap peers to the configuration
+func (s *Server) APIAddExplorer(req *ensweb.Request) *ensweb.Result {
+ var m model.ExplorerLinks
+ err := s.ParseJSON(req, &m)
+ if err != nil {
+ return s.BasicResponse(req, false, "invlid input request", nil)
+ }
+ err = s.c.AddExplorer(m.Links)
+ if err != nil {
+ return s.BasicResponse(req, false, "failed to add explorer, "+err.Error(), nil)
+ }
+ return s.BasicResponse(req, true, "explorer added successfully", nil)
+}
+
+// APIRemoveExplorer will remove bootstrap peers from the configuration
+func (s *Server) APIRemoveExplorer(req *ensweb.Request) *ensweb.Result {
+ var m model.ExplorerLinks
+ err := s.ParseJSON(req, &m)
+ if err != nil {
+ return s.BasicResponse(req, false, "invlid input request", nil)
+ }
+ err = s.c.RemoveExplorer(m.Links)
+ if err != nil {
+ return s.BasicResponse(req, false, "failed to remove explorer, "+err.Error(), nil)
+ }
+ return s.BasicResponse(req, true, "explorer removed successfully", nil)
+}
diff --git a/server/did.go b/server/did.go
index 8373b257..777a2835 100644
--- a/server/did.go
+++ b/server/did.go
@@ -62,18 +62,22 @@ func (s *Server) APICreateDID(req *ensweb.Request) *ensweb.Result {
}
for _, fileName := range fileNames {
- if strings.Contains(fileName, did.ImgFileName) {
- didCreate.ImgFile = fileName
- }
- if strings.Contains(fileName, did.DIDImgFileName) {
- didCreate.DIDImgFileName = fileName
- }
- if strings.Contains(fileName, did.PubShareFileName) {
- didCreate.PubImgFile = fileName
- }
+
if strings.Contains(fileName, did.PubKeyFileName) {
didCreate.PubKeyFile = fileName
}
+
+ if didCreate.Type != did.LiteDIDMode {
+ if strings.Contains(fileName, did.ImgFileName) {
+ didCreate.ImgFile = fileName
+ }
+ if strings.Contains(fileName, did.DIDImgFileName) {
+ didCreate.DIDImgFileName = fileName
+ }
+ if strings.Contains(fileName, did.PubShareFileName) {
+ didCreate.PubImgFile = fileName
+ }
+ }
}
if !s.cfg.EnableAuth {
didCreate.Dir = DIDRootDir
@@ -199,26 +203,30 @@ func (s *Server) APISetupDID(req *ensweb.Request) *ensweb.Result {
}
for _, fileName := range fileNames {
- if strings.Contains(fileName, did.DIDImgFileName) {
- didCreate.DIDImgFileName = fileName
- }
- if strings.Contains(fileName, did.PubShareFileName) {
- didCreate.PubImgFile = fileName
- }
- if strings.Contains(fileName, did.PvtShareFileName) {
- didCreate.PrivImgFile = fileName
- }
+
if strings.Contains(fileName, did.PvtKeyFileName) {
didCreate.PrivKeyFile = fileName
}
if strings.Contains(fileName, did.PubKeyFileName) {
didCreate.PubKeyFile = fileName
}
- if strings.Contains(fileName, did.QuorumPvtKeyFileName) {
- didCreate.QuorumPrivKeyFile = fileName
- }
- if strings.Contains(fileName, did.QuorumPubKeyFileName) {
- didCreate.QuorumPubKeyFile = fileName
+
+ if didCreate.Type != did.LiteDIDMode {
+ if strings.Contains(fileName, did.DIDImgFileName) {
+ didCreate.DIDImgFileName = fileName
+ }
+ if strings.Contains(fileName, did.PubShareFileName) {
+ didCreate.PubImgFile = fileName
+ }
+ if strings.Contains(fileName, did.PvtShareFileName) {
+ didCreate.PrivImgFile = fileName
+ }
+ if strings.Contains(fileName, did.QuorumPvtKeyFileName) {
+ didCreate.QuorumPrivKeyFile = fileName
+ }
+ if strings.Contains(fileName, did.QuorumPubKeyFileName) {
+ didCreate.QuorumPubKeyFile = fileName
+ }
}
}
dir, ok := s.validateAccess(req)
diff --git a/server/node.go b/server/node.go
new file mode 100644
index 00000000..37ccf3ed
--- /dev/null
+++ b/server/node.go
@@ -0,0 +1,9 @@
+package server
+
+import (
+ "github.com/rubixchain/rubixgoplatform/wrapper/ensweb"
+)
+
+func (s *Server) APIPeerID(req *ensweb.Request) *ensweb.Result {
+ return s.BasicResponse(req, true, s.c.GetPeerID(), nil)
+}
diff --git a/server/server.go b/server/server.go
index 4c501c21..b16b6ef1 100644
--- a/server/server.go
+++ b/server/server.go
@@ -154,9 +154,13 @@ func (s *Server) RegisterRoutes() {
s.AddRoute(setup.APIRegisterCallBackURL, "POST", s.AuthHandle(s.APIRegisterCallbackURL, true, s.AuthError, false))
s.AddRoute(setup.APIGetTxnByNode, "GET", s.AuthHandle(s.APIGetTxnByNode, true, s.AuthError, false))
s.AddRoute(setup.APIRemoveTokenChainBlock, "POST", s.AuthHandle(s.APIRemoveTokenChainBlock, true, s.AuthError, false))
+ s.AddRoute(setup.APIPeerID, "GET", s.AuthHandle(s.APIPeerID, false, s.AuthError, false))
s.AddRoute(setup.APIReleaseAllLockedTokens, "GET", s.AuthHandle(s.APIReleaseAllLockedTokens, true, s.AuthError, false))
s.AddRoute(setup.APICheckQuorumStatus, "GET", s.AuthHandle(s.APICheckQuorumStatus, false, s.AuthError, false))
+ s.AddRoute(setup.APIGetAllExplorer, "GET", s.AuthHandle(s.APIGetAllExplorer, false, s.AuthError, true))
+ s.AddRoute(setup.APIAddExplorer, "POST", s.AuthHandle(s.APIAddExplorer, false, s.AuthError, true))
+ s.AddRoute(setup.APIRemoveExplorer, "POST", s.AuthHandle(s.APIRemoveExplorer, false, s.AuthError, true))
}
func (s *Server) ExitFunc() error {
diff --git a/setup/setup.go b/setup/setup.go
index 6c9954ee..5aa8b15d 100644
--- a/setup/setup.go
+++ b/setup/setup.go
@@ -61,8 +61,12 @@ const (
APIRegisterCallBackURL string = "/api/register-callback-url"
APIGetTxnByNode string = "/api/get-by-node"
APIRemoveTokenChainBlock string = "/api/remove-token-chain-block"
+ APIPeerID string = "/api/get-peer-id"
APIReleaseAllLockedTokens string = "/api/release-all-locked-tokens"
APICheckQuorumStatus string = "/api/check-quorum-status"
+ APIGetAllExplorer string = "/api/get-all-explorer"
+ APIAddExplorer string = "/api/add-explorer"
+ APIRemoveExplorer string = "/api/remove-explorer"
)
// jwt.RegisteredClaims
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 00000000..bff954d1
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,82 @@
+# Test Scripts
+
+The test script does the complete setup by building the rubix node based on the Operatng System, downloads IPFS binary for a specific version (refer `IPFS_KUBO_VERSION` variable in `run.py`) and sets up quorum and non-quorum nodes before proceeding with running all the test cases.
+
+The test script covers the following RBT Transfer scenarios:
+
+1. Shuttle Transfer (Success Case)
+ 1.1 Generate 2 whole RBT for A
+ 1.2 Transfer 0.5 from A to B
+ 1.3 Transfer 1.499 from A to B
+ 1.4 (Transfer 0.25 from B to A) * 4
+ 1.5 Transfer 1 RBT from A to B
+ 1.6 Generate 2 whole RBT for A
+ 1.7 Transfer 2 RBT from A to B
+ 1.8 Transfer 0.001 from A to B
+
+2. Insufficient Balance Transfer (Failure Case)
+ 2.1 Transferring 100 RBT from A which has zero balance
+ 2.2 Transferring 100 RBT from B which has insufficient balance
+
+3. Transferring 0.00000009 RBT from B which is more than allowed decimal places (Failure Case)
+
+4. Transferring whole, part and mix RBT from NLSS DID to BIP39 DID
+ 4.1 Transfer 1 RBT from NLSS DID to BIP39 DID
+ 4.2 Transfer 1.5 RBT from NLSS DID to BIP39 DID
+ 4.3 Transfer 0.5 RBT from NLSS DID to BIP39 DID
+
+5. Transferring whole, part and mix RBT from BIP39 DID to NLSS DID
+ 5.1 Transfer 0.5 RBT from NLSS DID to BIP39 DID
+ 5.2 Transfer 1.5 RBT from NLSS DID to BIP39 DID
+ 5.3 Transfer 1 RBT from NLSS DID to BIP39 DID
+
+## Prerequisites
+
+- Python 3.10+ ([Install Ref](https://www.python.org/downloads/))
+- tmux for MacOs and Ubuntu based systems ([Install Ref](https://github.com/tmux/tmux/wiki/Installing#binary-packages))
+- `pip` package manger ([Install Ref](https://pip.pypa.io/en/stable/installation/))
+- `requests` package. After installing Python and pip, run `pip install requests` to install this package
+
+## Running the tests
+
+To start the test. Please NOTE that it must be run from the `tests` directory only.
+
+```
+python3 run.py
+```
+
+## Running tests in Docker
+
+To run the tests in a Docker Ubuntu environment, run the following:
+
+1. Build the image
+```
+docker build -t rubix_test_image_ubuntu --no-cache -f tests/docker/ubuntu_test.Dockerfile .
+```
+
+2. Run the container
+```
+docker run --rm --name rubix_test_container_ubuntu rubix_test_image_ubuntu
+```
+
+### Flags
+
+The test script is equipped with CLI Parser. Following are the flags and their description
+
+```
+usage: run.py [-h] [--skip_prerequisite | --no-skip_prerequisite] [--run_nodes_only | --no-run_nodes_only] [--skip_adding_quorums | --no-skip_adding_quorums]
+ [--run_tests_only | --no-run_tests_only]
+
+CLI to run tests for Rubix
+
+options:
+ -h, --help show this help message and exit
+ --skip_prerequisite, --no-skip_prerequisite
+ skip prerequisite steps such as installing IPFS and building Rubix
+ --run_nodes_only, --no-run_nodes_only
+ only run the rubix nodes and skip the setup
+ --skip_adding_quorums, --no-skip_adding_quorums
+ skips adding quorums
+ --run_tests_only, --no-run_tests_only
+ only proceed with running tests
+```
\ No newline at end of file
diff --git a/tests/config/__init__.py b/tests/config/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/config/node_registry.json b/tests/config/node_registry.json
new file mode 100644
index 00000000..08c4e27b
--- /dev/null
+++ b/tests/config/node_registry.json
@@ -0,0 +1,5 @@
+{
+ "quorum": [4, 5, 6, 7, 8],
+ "rbt_transfer": [9, 10],
+ "bip39_nlss": [11, 12]
+}
\ No newline at end of file
diff --git a/tests/config/utils.py b/tests/config/utils.py
new file mode 100644
index 00000000..af10b5cb
--- /dev/null
+++ b/tests/config/utils.py
@@ -0,0 +1,39 @@
+import json
+import os
+
+def get_node_registry():
+ current_dir = os.path.dirname(os.path.abspath(__file__))
+ node_registry_path = os.path.join(current_dir, "node_registry.json")
+ config = load_from_config_file(node_registry_path)
+
+ if config == {}:
+ raise Exception("node registry config is empty")
+ return config
+
+def load_from_config_file(config_file_path):
+ try:
+ with open(config_file_path, 'r') as file:
+ config_data = json.load(file)
+ return config_data
+ except FileNotFoundError as e:
+ return {}
+ except json.JSONDecodeError as e:
+ raise ValueError(f"Error: The file at {config_file_path} is not a valid JSON file.") from e
+ except Exception as e:
+ raise Exception(f"An unexpected error occurred: {e}") from e
+
+def save_to_config_file(config_file_path, config):
+ try:
+ if os.path.exists(config_file_path):
+ os.remove(config_file_path)
+
+ with open(config_file_path, 'w') as f:
+ json.dump(config, f, indent=4)
+ except FileNotFoundError as e:
+ raise FileNotFoundError(f"Error: The file at {config_file_path} could not be found.") from e
+ except PermissionError as e:
+ raise PermissionError(f"Error: Permission denied when trying to write to {config_file_path}.") from e
+ except TypeError as e: # JSON serialization errors raise TypeError, not JSONDecodeError
+ raise TypeError(f"Error: Failed to serialize the config data to JSON.") from e
+ except Exception as e:
+ raise Exception(f"An unexpected error occurred: {e}") from e
diff --git a/tests/docker/ubuntu_test.Dockerfile b/tests/docker/ubuntu_test.Dockerfile
new file mode 100644
index 00000000..8ba1dd2c
--- /dev/null
+++ b/tests/docker/ubuntu_test.Dockerfile
@@ -0,0 +1,18 @@
+FROM amd64/ubuntu
+
+# Install python3 and requests dependency
+RUN apt-get update && apt-get install -y python3 python3-requests wget build-essential tmux
+
+# Install golang 1.21
+RUN wget https://go.dev/dl/go1.21.10.linux-amd64.tar.gz
+RUN tar -C /usr/local -xzf go1.21.10.linux-amd64.tar.gz
+ENV PATH="/usr/local/go/bin:${PATH}"
+
+# Copy the tests directory
+COPY . /usr/node/
+
+# Change directory to test
+WORKDIR /usr/node/tests
+
+# Run tests
+CMD ["python3", "-u", "run.py"]
diff --git a/tests/fixtures/didimage.png.file b/tests/fixtures/didimage.png.file
new file mode 100644
index 00000000..09bf52e0
Binary files /dev/null and b/tests/fixtures/didimage.png.file differ
diff --git a/tests/fixtures/testswarm_linux.key b/tests/fixtures/testswarm_linux.key
new file mode 100644
index 00000000..534e7acb
--- /dev/null
+++ b/tests/fixtures/testswarm_linux.key
@@ -0,0 +1,3 @@
+/key/swarm/psk/1.0.0/
+/base16/
+278b9a199c43fa84178920bd9f5cbcd69e933ddf02a8f69e47a3ea5a1705513f
\ No newline at end of file
diff --git a/tests/fixtures/testswarm_mac.key b/tests/fixtures/testswarm_mac.key
new file mode 100644
index 00000000..7490ebe9
--- /dev/null
+++ b/tests/fixtures/testswarm_mac.key
@@ -0,0 +1,3 @@
+/key/swarm/psk/1.0.0/
+/base16/
+e105d6caa191dc76ff553d36d094fafb2ad1c646000a52ebc89b488e17f61f92
\ No newline at end of file
diff --git a/tests/fixtures/testswarm_windows.key b/tests/fixtures/testswarm_windows.key
new file mode 100644
index 00000000..7490ebe9
--- /dev/null
+++ b/tests/fixtures/testswarm_windows.key
@@ -0,0 +1,3 @@
+/key/swarm/psk/1.0.0/
+/base16/
+e105d6caa191dc76ff553d36d094fafb2ad1c646000a52ebc89b488e17f61f92
\ No newline at end of file
diff --git a/tests/helper/__init__.py b/tests/helper/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/helper/utils.py b/tests/helper/utils.py
new file mode 100644
index 00000000..d0732faa
--- /dev/null
+++ b/tests/helper/utils.py
@@ -0,0 +1,16 @@
+def expect_success(func):
+ def wrapper(*args, **kwargs):
+ try:
+ func(*args, **kwargs)
+ except:
+ raise Exception("The transaction/action was expected to pass, but it failed")
+ return wrapper
+
+def expect_failure(func):
+ def wrapper(*args, **kwargs):
+ try:
+ func(*args, **kwargs)
+ raise Exception("The transaction/action was expected to fail, but it passed")
+ except:
+ return 0
+ return wrapper
\ No newline at end of file
diff --git a/tests/node/__init__.py b/tests/node/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/node/actions.py b/tests/node/actions.py
new file mode 100644
index 00000000..629e253d
--- /dev/null
+++ b/tests/node/actions.py
@@ -0,0 +1,109 @@
+from .commands import cmd_run_rubix_servers, cmd_get_peer_id, cmd_create_did, cmd_register_did, \
+ cmd_generate_rbt, cmd_add_quorum_dids, cmd_setup_quorum_dids, cmd_rbt_transfer, get_build_dir
+from .utils import get_node_name_from_idx, get_did_by_alias
+from config.utils import save_to_config_file, get_node_registry
+
+def add_quorums(node_config: dict):
+ for config in node_config.values():
+ cmd_add_quorum_dids(
+ config["server"],
+ config["grpcPort"]
+ )
+
+def setup_quorums(node_config: dict, node_did_alias_map: dict):
+ for node, config in node_config.items():
+ did = get_did_by_alias(config, node_did_alias_map[node])
+ cmd_setup_quorum_dids(
+ did,
+ config["server"],
+ config["grpcPort"]
+ )
+
+def quorum_config(node_config: dict, node_did_alias_map: dict, skip_adding_quorums: bool = False):
+ # Prepare quorumlist.json
+ quorum_list = []
+ build_dir = get_build_dir()
+ quorum_list_file_path = f"../{build_dir}/quorumlist.json"
+
+ if skip_adding_quorums:
+ setup_quorums(node_config, node_did_alias_map)
+ else:
+ for node, config in node_config.items():
+ did = get_did_by_alias(config, node_did_alias_map[node])
+ quorum_info = {
+ "type": 2,
+ "address": config["peerId"] + "." + did
+ }
+
+ quorum_list.append(quorum_info)
+
+ save_to_config_file(quorum_list_file_path, quorum_list)
+
+ add_quorums(node_config)
+
+ setup_quorums(node_config, node_did_alias_map)
+
+
+def setup_rubix_nodes(node_registry_config_key):
+ if node_registry_config_key == "":
+ raise Exception("a key is needed to fetch node_registry.json config")
+
+ node_registry = get_node_registry()
+ if not node_registry_config_key in node_registry:
+ raise Exception(f"config key {node_registry_config_key} not found in node_registry.json config")
+
+ node_indices = node_registry[node_registry_config_key]
+
+ if not isinstance(node_indices, list):
+ raise Exception(f"the correspoding value for {node_registry_config_key} in node_registry.json must of List type")
+
+ if len(node_indices) == 0:
+ raise Exception(f"no indices found for {node_registry_config_key} in node_registry.json, provide at least one index")
+
+ node_config = {}
+
+ for idx in node_indices:
+ node_name = "node" + str(idx)
+ node_server, grpc_server = cmd_run_rubix_servers(node_name, idx)
+
+ cfg = {
+ "dids": {},
+ "server": node_server,
+ "grpcPort": grpc_server,
+ "peerId": ""
+ }
+
+ fetch_peer_id(cfg)
+ node_config[node_name] = cfg
+
+ return node_config
+
+def fetch_peer_id(config):
+ peer_id = cmd_get_peer_id(config["server"], config["grpcPort"])
+ config["peerId"] = peer_id
+
+def create_and_register_did(config: dict, did_alias: str, did_type: int = 4, register_did: bool = True):
+ did = cmd_create_did(config["server"], config["grpcPort"], did_type)
+ print(f"DID {did} has been created successfully")
+
+ config["dids"] = {
+ did_alias: did
+ }
+
+ if register_did:
+ cmd_register_did(did, config["server"], config["grpcPort"])
+ print(f"DID {did} has been registered successfully")
+
+ return did
+
+def fund_did_with_rbt(node_config: dict, did: str, rbt_amount: int = 70):
+ cmd_generate_rbt(did, rbt_amount, node_config["server"], node_config["grpcPort"])
+ print("DID ", did, f" is funded with {rbt_amount} RBT")
+
+def rbt_transfer(
+ sender_address: str,
+ receiver_address: str,
+ transfer_rbt: float,
+ sender_server_port: int,
+ sender_grpc_port: int):
+ cmd_rbt_transfer(sender_address, receiver_address, transfer_rbt, sender_server_port, sender_grpc_port)
diff --git a/tests/node/commands.py b/tests/node/commands.py
new file mode 100644
index 00000000..999f316c
--- /dev/null
+++ b/tests/node/commands.py
@@ -0,0 +1,217 @@
+import subprocess
+import os
+import re
+import platform
+import time
+import requests
+from .utils import get_base_ports
+
+def is_windows_os():
+ os_name = platform.system()
+ return os_name == "Windows"
+
+def get_build_dir():
+ os_name = platform.system()
+ build_folder = ""
+ if os_name == "Linux":
+ build_folder = "linux"
+ elif os_name == "Windows":
+ build_folder = "windows"
+ elif os_name == "Darwin":
+ build_folder = "mac"
+
+ return build_folder
+
+def run_command(cmd_string, is_output_from_stderr=False):
+ assert isinstance(cmd_string, str), "command must be of string type"
+ cmd_result = subprocess.run(cmd_string, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
+ code = cmd_result.returncode
+
+ if int(code) != 0:
+ err_output = cmd_result.stderr.decode('utf-8')[:-1]
+ print(err_output)
+ return err_output, int(code)
+
+ output = ""
+ if not is_output_from_stderr:
+ output = cmd_result.stdout.decode('utf-8')[:-1]
+ print(output)
+ if output.find('[ERROR]') > 0 or output.find('parse error') > 0:
+ return output, 1
+ else:
+ return output, code
+ else:
+ output = cmd_result.stderr.decode('utf-8')[:-1]
+ if output.find('[ERROR]') > 0 or output.find('parse error') > 0:
+ print(output)
+ return output, 1
+ else:
+ return output, code
+
+def cmd_run_rubix_servers(node_name, server_port_idx):
+ os.chdir("../" + get_build_dir())
+
+ base_node_server, base_grpc_port = get_base_ports()
+ grpc_port = base_grpc_port + server_port_idx
+ node_server = base_node_server + server_port_idx
+
+ cmd_string = ""
+ if is_windows_os():
+ cmd_string = f"powershell -Command Start-Process -FilePath '.\\rubixgoplatform.exe' -ArgumentList 'run -p {node_name} -n {server_port_idx} -s -testNet -grpcPort {grpc_port}' -WindowStyle Hidden"
+ else:
+ cmd_string = f"tmux new -s {node_name} -d ./rubixgoplatform run -p {node_name} -n {server_port_idx} -s -testNet -grpcPort {grpc_port}"
+
+ _, code = run_command(cmd_string)
+ if code != 0:
+ raise Exception("Error occurred while run the command: " + cmd_string)
+
+ print("Waiting for 40 seconds before checking if its running....")
+ time.sleep(40)
+ try:
+ check_if_nodes_is_running(server_port_idx)
+ except Exception as e:
+ raise e
+
+ os.chdir("../tests")
+ return node_server, grpc_port
+
+def check_if_nodes_is_running(server_idx):
+ base_server, _ = get_base_ports()
+ port = base_server + int(server_idx)
+ print(f"Check if server with ENS web server port {port} is running...")
+ url = f"http://localhost:{port}/api/getalldid"
+ try:
+ print(f"Sending GET request to URL: {url}")
+ response = requests.get(url)
+ if response.status_code == 200:
+ print(f"Server with port {port} is running successfully")
+ else:
+ raise Exception(f"Failed with Status Code: {response.status_code} | Server with port {port} is NOT running successfully")
+ except:
+ raise Exception(f"ConnectionError | Server with port {port} is NOT running successfully")
+
+def cmd_create_did(server_port, grpc_port, did_type = 4):
+ os.chdir("../" + get_build_dir())
+
+ cmd_string = f"./rubixgoplatform createdid -port {server_port} -grpcPort {grpc_port} -didType {did_type}"
+ if is_windows_os():
+ cmd_string = f".\\rubixgoplatform createdid -port {server_port} -grpcPort {grpc_port} -didType {did_type}"
+ output, code = run_command(cmd_string, True)
+ print(output)
+
+ if code != 0:
+ raise Exception("Error occurred while run the command: " + cmd_string)
+
+ did_id = ""
+ if "successfully" in output:
+ pattern = r'bafybmi\w+'
+ matches = re.findall(pattern, output)
+ if matches:
+ did_id = matches[0]
+ else:
+ raise Exception("unable to extract DID ID")
+
+ os.chdir("../tests")
+ return did_id
+
+def cmd_register_did(did_id, server_port, grpc_port):
+ os.chdir("../" + get_build_dir())
+ cmd_string = f"./rubixgoplatform registerdid -did {did_id} -port {server_port} -grpcPort {grpc_port}"
+ if is_windows_os():
+ cmd_string = f".\\rubixgoplatform registerdid -did {did_id} -port {server_port} -grpcPort {grpc_port}"
+ output, code = run_command(cmd_string, True)
+ print(output)
+
+ if code != 0:
+ raise Exception("Error occurred while run the command: " + cmd_string)
+
+ os.chdir("../tests")
+ return output
+
+def cmd_generate_rbt(did_id, numTokens, server_port, grpc_port):
+ os.chdir("../" + get_build_dir())
+ cmd_string = f"./rubixgoplatform generatetestrbt -did {did_id} -numTokens {numTokens} -port {server_port} -grpcPort {grpc_port}"
+ if is_windows_os():
+ cmd_string = f".\\rubixgoplatform generatetestrbt -did {did_id} -numTokens {numTokens} -port {server_port} -grpcPort {grpc_port}"
+ output, code = run_command(cmd_string, True)
+
+ if code != 0:
+ raise Exception("Error occurred while run the command: " + cmd_string)
+
+ os.chdir("../tests")
+ return output
+
+def cmd_add_quorum_dids(server_port, grpc_port):
+ os.chdir("../" + get_build_dir())
+ cmd_string = f"./rubixgoplatform addquorum -port {server_port} -grpcPort {grpc_port}"
+ if is_windows_os():
+ cmd_string = f".\\rubixgoplatform addquorum -port {server_port} -grpcPort {grpc_port}"
+ output, code = run_command(cmd_string, True)
+ print(output)
+ if code != 0:
+ raise Exception("Error occurred while run the command: " + cmd_string)
+
+ os.chdir("../tests")
+ return output
+
+def cmd_shutdown_node(server_port, grpc_port):
+ os.chdir("../" + get_build_dir())
+ cmd_string = f"./rubixgoplatform shutdown -port {server_port} -grpcPort {grpc_port}"
+ if is_windows_os():
+ cmd_string = f".\\rubixgoplatform shutdown -port {server_port} -grpcPort {grpc_port}"
+ output, _ = run_command(cmd_string, True)
+ print(output)
+
+ os.chdir("../tests")
+ return output
+
+def cmd_setup_quorum_dids(did, server_port, grpc_port):
+ os.chdir("../" + get_build_dir())
+ cmd_string = f"./rubixgoplatform setupquorum -did {did} -port {server_port} -grpcPort {grpc_port}"
+ if is_windows_os():
+ cmd_string = f".\\rubixgoplatform setupquorum -did {did} -port {server_port} -grpcPort {grpc_port}"
+ output, code = run_command(cmd_string, True)
+ print(output)
+ if code != 0:
+ raise Exception("Error occurred while run the command: " + cmd_string)
+
+ os.chdir("../tests")
+ return output
+
+def cmd_get_peer_id(server_port, grpc_port):
+ os.chdir("../" + get_build_dir())
+ cmd_string = f"./rubixgoplatform get-peer-id -port {server_port} -grpcPort {grpc_port}"
+ if is_windows_os():
+ cmd_string = f".\\rubixgoplatform get-peer-id -port {server_port} -grpcPort {grpc_port}"
+ output, code = run_command(cmd_string)
+
+ if code != 0:
+ raise Exception("Error occurred while run the command: " + cmd_string)
+ os.chdir("../tests")
+ return output
+
+def check_account_info(did, server_port, grpc_port):
+ os.chdir("../" + get_build_dir())
+ cmd_string = f"./rubixgoplatform getaccountinfo -did {did} -port {server_port} -grpcPort {grpc_port}"
+ if is_windows_os():
+ cmd_string = f".\\rubixgoplatform getaccountinfo -did {did} -port {server_port} -grpcPort {grpc_port}"
+ output, code = run_command(cmd_string)
+
+ if code != 0:
+ raise Exception("Error occurred while run the command: " + cmd_string)
+ os.chdir("../tests")
+ return output
+
+# Note: address != did, address = peerId.didId
+def cmd_rbt_transfer(sender_address, receiver_address, rbt_amount, server_port, grpc_port):
+ os.chdir("../" + get_build_dir())
+ cmd_string = f"./rubixgoplatform transferrbt -senderAddr {sender_address} -receiverAddr {receiver_address} -rbtAmount {rbt_amount} -port {server_port} -grpcPort {grpc_port}"
+ if is_windows_os():
+ cmd_string = f".\\rubixgoplatform transferrbt -senderAddr {sender_address} -receiverAddr {receiver_address} -rbtAmount {rbt_amount} -port {server_port} -grpcPort {grpc_port}"
+ output, code = run_command(cmd_string, True)
+ print(output)
+ if code != 0:
+ raise Exception("Error occurred while run the command: " + cmd_string)
+
+ os.chdir("../tests")
+ return output
\ No newline at end of file
diff --git a/tests/node/quorum.py b/tests/node/quorum.py
new file mode 100644
index 00000000..cb65ccbd
--- /dev/null
+++ b/tests/node/quorum.py
@@ -0,0 +1,40 @@
+import pprint
+import time
+
+from .actions import setup_rubix_nodes, create_and_register_did, \
+ fund_did_with_rbt, quorum_config
+from config.utils import save_to_config_file, load_from_config_file
+
+QUORUM_CONFIG_PATH = "./quorum_config.json"
+
+def run_quorum_nodes(only_run_nodes, skip_adding_quorums):
+ print("Running Rubix Quorum nodes......")
+ node_registry_key = "quorum"
+ node_config = setup_rubix_nodes(node_registry_key)
+ print("Rubix Quorum nodes are now running")
+
+ if not only_run_nodes:
+ did_alias = "did_quorum"
+ node_did_alias_map = {}
+
+ print("Creating, Registering and Funding Quorum DIDs\n")
+ for node, config in node_config.items():
+ did = create_and_register_did(config, did_alias)
+
+ fund_did_with_rbt(config, did)
+
+ # Selecting DIDs for quorum setup
+ node_did_alias_map[node] = did_alias
+ save_to_config_file(QUORUM_CONFIG_PATH, node_config)
+ print("\nquorum_config.json is created")
+
+ print("Setting up quorums and saving the info in quorumlist.json")
+ quorum_config(node_config, node_did_alias_map, skip_adding_quorums=skip_adding_quorums)
+
+ pprint.pp(node_config)
+ print("Quorums have been configured")
+ else:
+ quorum_config(node_config, node_did_alias_map, skip_adding_quorums=True)
+
+def get_quorum_config():
+ return load_from_config_file(QUORUM_CONFIG_PATH)
diff --git a/tests/node/utils.py b/tests/node/utils.py
new file mode 100644
index 00000000..668c1d10
--- /dev/null
+++ b/tests/node/utils.py
@@ -0,0 +1,17 @@
+import json
+import sys
+import os
+sys.path.insert(1, os.getcwd())
+from .vars import QUORUM_NODES
+
+def get_node_name_from_idx(idx, prefix_string: str = "node"):
+ return prefix_string + str(idx)
+
+def get_base_ports():
+ base_ens_server = 20000
+ base_grpc_port = 10500
+
+ return base_ens_server, base_grpc_port
+
+def get_did_by_alias(node_config, alias):
+ return node_config["dids"][alias]
\ No newline at end of file
diff --git a/tests/node/vars.py b/tests/node/vars.py
new file mode 100644
index 00000000..1cdf27f6
--- /dev/null
+++ b/tests/node/vars.py
@@ -0,0 +1,2 @@
+QUORUM_NODES = 5
+
diff --git a/tests/pack_node_logs.py b/tests/pack_node_logs.py
new file mode 100644
index 00000000..afe56433
--- /dev/null
+++ b/tests/pack_node_logs.py
@@ -0,0 +1,33 @@
+# This script packs all the quorum and non-quorum node logs in `tests/node_logs`
+
+import os
+import shutil
+
+def collect_logs(base_dir, output_dir):
+ # Ensure the output directory exists
+ os.makedirs(output_dir, exist_ok=True)
+
+ # Iterate over files and directories in the build directory
+ for env in ['linux', 'windows', 'mac']:
+ env_path = os.path.join(base_dir, env)
+ if os.path.isdir(env_path):
+ for item in os.listdir(env_path):
+ item_path = os.path.join(env_path, item)
+ # Check if the directory name starts with `node`.
+ if os.path.isdir(item_path) and item.startswith('node'):
+ log_file_path = os.path.join(item_path, 'log.txt')
+ if os.path.exists(log_file_path):
+ # Rename the log file in `log__.txt`
+ new_log_name = f'log_{env}_{item}.txt'
+ new_log_path = os.path.join(output_dir, new_log_name)
+ # Copy the log file to the output directory
+ shutil.copyfile(log_file_path, new_log_path)
+ print(f'Copied {new_log_name} to {output_dir}')
+ else:
+ print(f'log.txt not found in {item_path}')
+
+if __name__ == "__main__":
+ base_directory = '..' # Parent directory of 'tests' where build directory is located
+ output_directory = './node_logs' # Directory to store collected logs
+ collect_logs(base_directory, output_directory)
+ print(f'All logs collected into {output_directory}')
\ No newline at end of file
diff --git a/tests/run.py b/tests/run.py
new file mode 100644
index 00000000..67f3705f
--- /dev/null
+++ b/tests/run.py
@@ -0,0 +1,171 @@
+import platform
+import os
+import shutil
+import requests
+import argparse
+from node.commands import run_command
+from node.quorum import run_quorum_nodes
+
+from scenarios import (
+ rbt_transfer,
+ bip39_nlss_test
+)
+
+IPFS_KUBO_VERSION = "v0.21.0"
+
+def get_os_info():
+ os_name = platform.system()
+ build_folder = ""
+
+ if os_name == "Linux":
+ build_folder = "linux"
+ elif os_name == "Windows":
+ build_folder = "windows"
+ elif os_name == "Darwin":
+ build_folder = "mac"
+ else:
+ print("Unsupported operating system to build Rubix")
+ return None, None
+
+ return os_name, build_folder
+
+def download_ipfs_binary(os_name, version, build_dir):
+ download_url = ""
+
+ if os_name == "Linux":
+ download_url = f"https://dist.ipfs.tech/kubo/{version}/kubo_{version}_linux-amd64.tar.gz"
+ elif os_name == "Windows":
+ download_url = f"https://dist.ipfs.tech/kubo/{version}/kubo_{version}_windows-amd64.zip"
+ elif os_name == "Darwin": # MacOS
+ download_url = f"https://dist.ipfs.tech/kubo/{version}/kubo_{version}_darwin-amd64.tar.gz"
+ else:
+ raise ValueError("Unsupported operating system")
+
+ # Download the IPFS binary archive
+ download_path = f"kubo_{version}_{os_name.lower()}-amd64.tar.gz" if os_name != "Windows" else f"kubo_{version}_{os_name.lower()}-amd64.zip"
+ print("Downloading IPFS binary...")
+ response = requests.get(download_url)
+ with open(download_path, "wb") as f:
+ f.write(response.content)
+ print("Download completed.")
+
+ # Extract the archive
+ print("Extracting IPFS binary...")
+ if os_name == "Windows":
+ # For Windows, we need to use the 'zipfile' module to extract
+ import zipfile
+ with zipfile.ZipFile(download_path, "r") as zip_ref:
+ zip_ref.extractall("kubo")
+ else:
+ # For Linux and MacOS, we use tar
+ import tarfile
+ with tarfile.open(download_path, "r:gz" if os_name != "Darwin" else "r") as tar_ref:
+ tar_ref.extractall("kubo")
+ print("Extraction completed.")
+
+ # Check the contents of the kubo directory
+ print("Contents of kubo directory:")
+ for item in os.listdir("kubo"):
+ print(item)
+
+ # Move IPFS binary to the appropriate folder
+ print("Moving IPFS binary...")
+
+ ipfs_bin_name = "ipfs"
+ if os_name == "Windows":
+ ipfs_bin_name = "ipfs.exe"
+
+ src_file = os.path.join("kubo", "kubo", ipfs_bin_name)
+ dest_dir = os.path.join(build_dir, ipfs_bin_name)
+ if os.path.exists(src_file):
+ shutil.move(src_file, dest_dir)
+ print("IPFS binary moved to", dest_dir)
+
+ # Check if the file is present at the destination
+ dest_file = os.path.join(dest_dir)
+ if not os.path.exists(dest_file):
+ raise FileNotFoundError("IPFS binary not found at the destination after move operation.")
+ else:
+ raise FileNotFoundError("Installed IPFS binary file does not exist.")
+
+ # Clean up
+ os.remove(download_path)
+ shutil.rmtree("kubo")
+ print("\nIPFS has been installed succesfully.")
+
+def copy_fixtures_to_build_dir(build_directory):
+ fixtures_directory = os.path.join("tests", "fixtures")
+
+ # Copy didimage.png.file
+ image_file_src = os.path.join(fixtures_directory, "didimage.png.file")
+ image_file_dest = os.path.join(build_directory, "image.png")
+ shutil.copyfile(image_file_src, image_file_dest)
+
+ if not os.path.exists(image_file_dest):
+ raise FileNotFoundError(f"Copy operation for didimage.png.file failed. Destination file not found: {image_file_dest}")
+
+ # Copy testswarm.key
+ swarmkey_src = os.path.join(fixtures_directory, f"testswarm_{build_directory}.key")
+ swarmkey_dest = os.path.join(build_directory, f"testswarm.key")
+ shutil.copyfile(swarmkey_src, swarmkey_dest)
+
+ if not os.path.exists(swarmkey_dest):
+ raise FileNotFoundError(f"Copy operation for testswarm_{build_directory}.key failed. Destination file not found: {swarmkey_dest}")
+
+ print("\nimage.png and swarm key have been added to build directory successfully")
+
+def cli():
+ parser = argparse.ArgumentParser(description="CLI to run tests for Rubix")
+ parser.add_argument("--skip_prerequisite", action=argparse.BooleanOptionalAction, help="skip prerequisite steps such as installing IPFS and building Rubix")
+ parser.add_argument("--run_nodes_only", action=argparse.BooleanOptionalAction, help="only run the rubix nodes and skip the setup")
+ parser.add_argument("--skip_adding_quorums", action=argparse.BooleanOptionalAction, help="skips adding quorums")
+ parser.add_argument("--run_tests_only", action=argparse.BooleanOptionalAction, help="only proceed with running tests")
+ return parser.parse_args()
+
+if __name__=='__main__':
+ args = cli()
+ skip_prerequisite = args.skip_prerequisite
+ run_nodes_only = args.run_nodes_only
+ skip_adding_quorums = args.skip_adding_quorums
+ run_tests_only = args.run_tests_only
+
+ non_quorum_node_config = {}
+
+ if not run_tests_only:
+ os_name, build_folder = get_os_info()
+ if os_name is None:
+ exit(1)
+
+ if not skip_prerequisite:
+ os.chdir("../")
+ print(f"Building Rubix binary for {os_name}\n")
+ build_command = ""
+ if os_name == "Linux":
+ build_command = "make compile-linux"
+ elif os_name == "Windows":
+ build_command = "make compile-windows"
+ elif os_name == "Darwin":
+ build_command = "make compile-mac"
+
+ output, code = run_command(build_command)
+ if code != 0:
+ print("build failed with error:", output)
+ exit(1)
+ else:
+ print("\nBuild successful\n")
+
+ download_ipfs_binary(os_name, IPFS_KUBO_VERSION, build_folder)
+ copy_fixtures_to_build_dir(build_folder)
+ os.chdir("./tests")
+
+ run_quorum_nodes(run_nodes_only, skip_adding_quorums=skip_adding_quorums)
+
+ # It will carry list of Python files containing the function `run()`
+ # that consists of logic to run all the necessary tests
+ modules = [
+ rbt_transfer,
+ bip39_nlss_test
+ ]
+
+ for module in modules:
+ module.run()
diff --git a/tests/scenarios/__init__.py b/tests/scenarios/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/scenarios/bip39_nlss_test.py b/tests/scenarios/bip39_nlss_test.py
new file mode 100644
index 00000000..74aa300c
--- /dev/null
+++ b/tests/scenarios/bip39_nlss_test.py
@@ -0,0 +1,89 @@
+from node.actions import rbt_transfer, fund_did_with_rbt, setup_rubix_nodes, \
+ create_and_register_did, add_quorums
+from node.utils import get_did_by_alias
+from config.utils import save_to_config_file, load_from_config_file
+from helper.utils import expect_failure, expect_success
+
+__node_config_path = "./bip39_nlss_config.json"
+
+def setup():
+ print("Setting up test.....")
+ print("Configuring and running node11 and node12...")
+
+ node_config = setup_rubix_nodes("bip39_nlss")
+
+ config_bip39 = node_config["node11"]
+ config_nlss = node_config["node12"]
+
+ create_and_register_did(config_bip39, "bip39_1", did_type=4)
+
+
+ create_and_register_did(config_nlss, "nlss_1", did_type=0)
+
+ save_to_config_file(__node_config_path, node_config)
+
+ print("Adding quorums")
+ add_quorums(node_config)
+
+ print("Setup Done\n")
+ return node_config
+
+def run(skip_setup: bool = False):
+ print("\n----------- 2. Running Tests related to RBT Transfer between BIP39 and NLSS dids -----------\n")
+ node_config = {}
+
+ if not skip_setup:
+ node_config = setup()
+ else:
+ node_config = load_from_config_file(__node_config_path)
+
+ nlss_to_bip39(node_config)
+ bip39_to_nlss(node_config)
+ print("\n-------------- Tests Completed -------------------\n")
+
+def nlss_to_bip39(node_config):
+ node_bip39, node_nlss = node_config["node11"], node_config["node12"]
+ server_port_nlss, grpc_port_nlss = node_nlss["server"], node_nlss["grpcPort"]
+ did_bip39, did_nlss = get_did_by_alias(node_bip39, "bip39_1"), get_did_by_alias(node_nlss, "nlss_1")
+ address_bip39, address_nlss = node_bip39["peerId"]+"."+did_bip39, node_nlss["peerId"]+"."+did_nlss
+
+ print("------ Test Case (PASS): Transferring whole, part and mix RBT from NLSS DID to BIP39 DID ------\n")
+
+ print("\n1. Generating 3 RBT for NLSS DID")
+ expect_success(fund_did_with_rbt)(node_nlss, did_nlss, 3)
+ print("Funded NLSS DID with 3 RBT")
+
+ print("\n2. Transferring 1 RBT from NLSS DID to BIP39 DID....")
+ expect_success(rbt_transfer)(address_nlss, address_bip39, 1, server_port_nlss, grpc_port_nlss)
+ print("Transferred 1 RBT from NLSS DID to BIP39 DID")
+
+ print("\n3. Transferring 1.5 RBT from NLSS DID to BIP39 DID....")
+ expect_success(rbt_transfer)(address_nlss, address_bip39, 1.5, server_port_nlss, grpc_port_nlss)
+ print("Transferred 1.5 RBT from NLSS DID to BIP39 DID")
+
+ print("\n4. Transferring 0.5 RBT from NLSS DID to BIP39 DID....")
+ expect_success(rbt_transfer)(address_nlss, address_bip39, 0.5, server_port_nlss, grpc_port_nlss)
+ print("Transferred 0.5 RBT from NLSS DID to BIP39 DID")
+
+ print("\n------ Test Case (PASS): Transferring whole, part and mix RBT from NLSS DID to BIP39 DID completed ------\n")
+
+def bip39_to_nlss(node_config):
+ node_bip39, node_nlss = node_config["node11"], node_config["node12"]
+ server_port_bip39, grpc_port_bip39 = node_bip39["server"], node_bip39["grpcPort"]
+ did_bip39, did_nlss = get_did_by_alias(node_bip39, "bip39_1"), get_did_by_alias(node_nlss, "nlss_1")
+ address_bip39, address_nlss = node_bip39["peerId"]+"."+did_bip39, node_nlss["peerId"]+"."+did_nlss
+
+ print("------ Test Case (PASS): Transferring whole, part and mix RBT from BIP39 DID to NLSS DID ------\n")
+
+ print("\n4. Transferring 0.5 RBT from BIP39 DID to NLSS DID....")
+ expect_success(rbt_transfer)(address_bip39, address_nlss, 0.5, server_port_bip39, grpc_port_bip39)
+ print("Transferred 0.5 RBT from BIP39 DID to NLSS DID")
+
+ print("\n3. Transferring 1.5 RBT from BIP39 DID to NLSS DID....")
+ expect_success(rbt_transfer)(address_bip39, address_nlss, 1.5, server_port_bip39, grpc_port_bip39)
+ print("Transferred 1.5 RBT from BIP39 DID to NLSS DID")
+
+ print("\n2. Transferring 1 RBT from BIP39 DID to NLSS DID....")
+ expect_success(rbt_transfer)(address_bip39, address_nlss, 1, server_port_bip39, grpc_port_bip39)
+ print("Transferred 1 RBT from BIP39 DID to NLSS DID")
+
diff --git a/tests/scenarios/rbt_transfer.py b/tests/scenarios/rbt_transfer.py
new file mode 100644
index 00000000..7f5c2da0
--- /dev/null
+++ b/tests/scenarios/rbt_transfer.py
@@ -0,0 +1,135 @@
+from node.actions import rbt_transfer, fund_did_with_rbt, setup_rubix_nodes, \
+ create_and_register_did, add_quorums
+from node.utils import get_did_by_alias
+from config.utils import save_to_config_file, load_from_config_file
+from helper.utils import expect_failure, expect_success
+
+__node_config_path = "./rbt_transfer_config.json"
+
+def setup():
+ print("Setting up test.....")
+ print("Configuring and running node9 and node10...")
+
+ node_config = setup_rubix_nodes("rbt_transfer")
+
+ config_A = node_config["node9"]
+ config_B = node_config["node10"]
+
+ create_and_register_did(config_A, "did_a")
+ create_and_register_did(config_B, "did_b")
+
+ save_to_config_file(__node_config_path, node_config)
+
+ print("Adding quorums")
+ add_quorums(node_config)
+
+ print("Setup Done\n")
+ return node_config
+
+def run(skip_setup: bool = False):
+ print("\n----------- 1. Running Tests related to RBT Transfer -----------\n")
+ node_config = {}
+
+ # In some cases, we may wish to run tests for an existing test configuration
+ # where the nodes are running already. If skip_setup is True, the setup steps
+ # are skipped and we proceed to directly run the test cases and load the config
+ # from the config file
+ if not skip_setup:
+ node_config = setup()
+ else:
+ node_config = load_from_config_file(__node_config_path)
+
+ shuttle_transfer(node_config)
+ insufficient_balance_transfer(node_config)
+ max_decimal_place_transfer(node_config)
+
+ print("\n-------------- Tests Completed -------------------\n")
+
+def max_decimal_place_transfer(config):
+ node_A_info, node_B_info = config["node9"], config["node10"]
+ server_port_B, grpc_port_B = node_B_info["server"], node_B_info["grpcPort"]
+ did_A, did_B = get_did_by_alias(node_A_info, "did_a"), get_did_by_alias(node_B_info, "did_b")
+ address_A, address_B = node_A_info["peerId"]+"."+did_A, node_B_info["peerId"]+"."+did_B
+
+ print("------ Test Case (FAIL) : Transferring 0.00000009 RBT from B which is more than allowed decimal places ------")
+
+ print("\nTransferring 0.00000009 RBT from B to A....")
+ expect_failure(rbt_transfer)(address_B, address_A, 0.00000009, server_port_B, grpc_port_B)
+
+ print("\n------ Test Case (FAIL) : Transferring 0.00000009 RBT from B which is more than allowed decimal places completed ------\n")
+
+def insufficient_balance_transfer(config):
+ node_A_info, node_B_info = config["node9"], config["node10"]
+ server_port_A, grpc_port_A = node_A_info["server"], node_A_info["grpcPort"]
+ server_port_B, grpc_port_B = node_B_info["server"], node_B_info["grpcPort"]
+ did_A, did_B = get_did_by_alias(node_A_info, "did_a"), get_did_by_alias(node_B_info, "did_b")
+ address_A, address_B = node_A_info["peerId"]+"."+did_A, node_B_info["peerId"]+"."+did_B
+
+ print("\n------ Test Case (FAIL) : Transferring 100 RBT from A which has zero balance ------")
+
+ print("\nTransferring 100 RBT from A to B....")
+ expect_failure(rbt_transfer)(address_A, address_B, 100, server_port_A, grpc_port_A)
+
+ print("\n------ Test Case (FAIL) : Transferring 100 RBT from A which has zero balance completed ------\n")
+
+ print("\n------ Test Case (FAIL) : Transferring 100 RBT from B which has insufficient balance ------")
+
+ print("\nTransferring 100 RBT from B to A....")
+ expect_failure(rbt_transfer)(address_B, address_A, 0.25, server_port_B, grpc_port_B)
+
+ print("\n------ Test Case (FAIL) : Transferring 100 RBT from B which has insufficient balance completed ------\n")
+
+def shuttle_transfer(config):
+ node_A_info, node_B_info = config["node9"], config["node10"]
+ server_port_A, grpc_port_A = node_A_info["server"], node_A_info["grpcPort"]
+ server_port_B, grpc_port_B = node_B_info["server"], node_B_info["grpcPort"]
+ did_A, did_B = get_did_by_alias(node_A_info, "did_a"), get_did_by_alias(node_B_info, "did_b")
+ address_A, address_B = node_A_info["peerId"]+"."+did_A, node_B_info["peerId"]+"."+did_B
+
+ print("------ Test Case (PASS): Shuttle transfer started ------\n")
+
+ print("\n1. Generating 2 whole RBT for A")
+ expect_success(fund_did_with_rbt)(node_A_info, did_A, 2)
+ print("Funded node A with 2 RBT")
+
+ print("\n2. Transferring 0.5 RBT from A to B....")
+ expect_success(rbt_transfer)(address_A, address_B, 0.5, server_port_A, grpc_port_A)
+ print("Transferred 0.5 RBT from A to B")
+
+ print("\n3. Transferring 1.499 RBT from A to B....")
+ expect_success(rbt_transfer)(address_A, address_B, 1.499, server_port_A, grpc_port_A)
+ print("Transferred 1.499 RBT from A to B")
+
+ print("\n4. Transferring 0.25 RBT from B to A....")
+ expect_success(rbt_transfer)(address_B, address_A, 0.25, server_port_B, grpc_port_B)
+ print("Transferred 0.25 RBT from B to A")
+
+ print("\n5. Transferring 0.25 RBT from B to A....")
+ expect_success(rbt_transfer)(address_B, address_A, 0.25, server_port_B, grpc_port_B)
+ print("Transferred 0.25 RBT from B to A")
+
+ print("\n6. Transferring 0.25 RBT from B to A....")
+ expect_success(rbt_transfer)(address_B, address_A, 0.25, server_port_B, grpc_port_B)
+ print("Transferred 0.25 RBT from B to A")
+
+ print("\n7. Transferring 0.25 RBT from B to A....")
+ expect_success(rbt_transfer)(address_B, address_A, 0.25, server_port_B, grpc_port_B)
+ print("Transferred 0.25 RBT from B to A")
+
+ print("\n8. Transferring 1 RBT from A to B....")
+ expect_success(rbt_transfer)(address_A, address_B, 1, server_port_A, grpc_port_A)
+ print("Transferred 1 RBT from A to B")
+
+ print("\n9. Generating 2 whole RBT for A")
+ expect_success(fund_did_with_rbt)(node_A_info, did_A, 2)
+ print("Funded node A with 2 RBT")
+
+ print("\n10. Transferring 2 RBT from A to B....")
+ expect_success(rbt_transfer)(address_A, address_B, 2, server_port_A, grpc_port_A)
+ print("Transferred 2 RBT from A to B")
+
+ print("\n11. Transferring 0.001 RBT from A to B....")
+ expect_success(rbt_transfer)(address_A, address_B, 0.001, server_port_A, grpc_port_A)
+ print("Transferred 0.001 RBT from A to B")
+
+ print("\n------ Test Case (PASS): Shuttle transfer completed ------\n")
diff --git a/tests/shutdown.py b/tests/shutdown.py
new file mode 100644
index 00000000..8d96c103
--- /dev/null
+++ b/tests/shutdown.py
@@ -0,0 +1,14 @@
+from node.commands import cmd_shutdown_node
+from node.utils import get_base_ports
+from config.utils import get_node_registry
+
+if __name__=='__main__':
+ base_node_server, base_grpc_server = get_base_ports()
+ node_registry_config = get_node_registry()
+
+ for indices in node_registry_config.values():
+ for i in indices:
+ server_port = base_node_server + i
+ grpc_port = base_grpc_server + i
+ print(f"Shutting down server running at {server_port}")
+ cmd_shutdown_node(server_port, grpc_port)
diff --git a/wrapper/ensweb/client.go b/wrapper/ensweb/client.go
index bb996c91..31571457 100644
--- a/wrapper/ensweb/client.go
+++ b/wrapper/ensweb/client.go
@@ -128,6 +128,30 @@ func (c *Client) JSONRequest(method string, requestPath string, model interface{
return req, err
}
+func (c *Client) JSONRequestForExplorer(method string, requestPath string, model interface{}, explorerURL string) (*http.Request, error) {
+ var body *bytes.Buffer
+ if model != nil {
+ j, err := json.Marshal(model)
+ if err != nil {
+ return nil, err
+ }
+ body = bytes.NewBuffer(j)
+ } else {
+ body = bytes.NewBuffer(make([]byte, 0))
+ }
+
+ url := explorerURL + requestPath
+
+ req, err := http.NewRequest(method, url, body)
+ if err != nil {
+ return nil, err
+ }
+
+ req.Header.Set("Content-Type", "application/json")
+
+ return req, err
+}
+
func (c *Client) MultiFormRequest(method string, requestPath string, field map[string]string, files map[string]string) (*http.Request, error) {
var b bytes.Buffer
w := multipart.NewWriter(&b)