Browse Source

Status update release 1

master
Captain Dero 6 years ago
parent
commit
a390b5e235
2346 changed files with 716006 additions and 2 deletions
  1. +52
    -0
      Captain_Dero_pub.txt
  2. +89
    -0
      LICENSE
  3. +2
    -2
      README.md
  4. +71
    -0
      address/address.go
  5. +120
    -0
      address/address_test.go
  6. +77
    -0
      address/base58.go
  7. +24
    -0
      alt_chain_data/038e2b878d3c204b8726a4159d8e2c113900a7180015ea30a48eda138ed043c7_17004.txt
  8. +25
    -0
      alt_chain_data/03ca43b2dbfefe2b1854c516a4d30a7feeb5386dfe1169c3caf33ec3ff898582_16998.txt
  9. +25
    -0
      alt_chain_data/0eb61f1b78d810d314769e5e6157bc22894127b5d8e5bc417e99091005a7b858_16999.txt
  10. +24
    -0
      alt_chain_data/1ae49ffd0caa6ffb6dc5617a06ae75e7d140303d69e06f4b6b0dcc4bc98383bc_16996.txt
  11. +24
    -0
      alt_chain_data/1d9687780f854ea310bf5d7ea5e756be167fa043d81a58c3a3e602e08e7ad920_17006.txt
  12. +24
    -0
      alt_chain_data/6360ac8eba51cf1215461f304635e01935345f7fb7fa0c1ce3263ac131b5cb35_17007.txt
  13. +25
    -0
      alt_chain_data/77d6b92961146232ba36ced359f6dbbf3170299b530d2c7d0da5c398f7713d23_17008.txt
  14. +24
    -0
      alt_chain_data/78581a7ea3a71082ead85cfde06ebb2a4d7bbfcfef9a0fe69e373e78b807fa7f_16997.txt
  15. +25
    -0
      alt_chain_data/8e7a86de29068834e3dd3a498c4717159cceec7b6f0753b65cbf889d564a6982_17003.txt
  16. +24
    -0
      alt_chain_data/9249ac65bef9acb1b4375a50d9857627ef9f78961fe8fdd6068621572a174bd6_17005.txt
  17. +25
    -0
      alt_chain_data/a39b5f88e39231ddebf80f8023724f9b7ee77dbb81e91f19b96933a8cf118313_17000.txt
  18. +24
    -0
      alt_chain_data/f63b50e5962bb7fec38a224f09935177a103c20bcd272a0df24c36559bdc2106_17001.txt
  19. +24
    -0
      alt_chain_data/f87dd7136998a2e56a963ca1387a9738a98c7aa72a6f8a570f9c71397af383ed_17002.txt
  20. +89
    -0
      blockchain/alt_blocks.go
  21. +313
    -0
      blockchain/block.go
  22. +42
    -0
      blockchain/block_test.go
  23. +593
    -0
      blockchain/blockchain.go
  24. +17
    -0
      blockchain/caller.go
  25. +49
    -0
      blockchain/checkpoints_static.go
  26. +50
    -0
      blockchain/genesis.go
  27. +24
    -0
      blockchain/genesis_test.go
  28. +97
    -0
      blockchain/mempool.go
  29. +56
    -0
      blockchain/outputs_index.go
  30. +37
    -0
      blockchain/readme.txt
  31. +169
    -0
      blockchain/reorg_test.go
  32. +9
    -0
      blockchain/signature.go
  33. +455
    -0
      blockchain/store.go
  34. +661
    -0
      blockchain/transaction.go
  35. +83
    -0
      blockchain/transaction_ringct_test.go
  36. +186
    -0
      blockchain/transaction_test.go
  37. +73
    -0
      build_all.sh
  38. +8
    -0
      cmd/derod/dummy_test.go
  39. +438
    -0
      cmd/derod/main.go
  40. +119
    -0
      config/config.go
  41. +8
    -0
      config/dummy_test.go
  42. +35
    -0
      crypto/hash.go
  43. +26
    -0
      crypto/keccak.go
  44. +63
    -0
      crypto/keccak_test.go
  45. +13
    -0
      crypto/key.go
  46. +47
    -0
      crypto/ringct/basic.go
  47. +1490
    -0
      crypto/ringct/const.go
  48. +3053
    -0
      crypto/ringct/edwards25519.go
  49. +1614
    -0
      crypto/ringct/edwards25519_test.go
  50. +175
    -0
      crypto/ringct/key.go
  51. +39
    -0
      crypto/ringct/mlsag.go
  52. +159
    -0
      crypto/ringct/range.go
  53. +23
    -0
      crypto/ringct/range_test.go
  54. +357
    -0
      crypto/ringct/ringct.go
  55. +36
    -0
      crypto/ringct/varint.go
  56. +59
    -0
      crypto/ringct/varint_test.go
  57. +526
    -0
      cryptonight/cryptonight.go
  58. +44
    -0
      cryptonight/cryptonight_test.go
  59. +416
    -0
      cryptonight/hash.go
  60. +546
    -0
      cryptonight/jhash.go
  61. +334
    -0
      cryptonight/keccak.go
  62. +117
    -0
      difficulty/difficulty.go
  63. +93
    -0
      difficulty/difficulty_test.go
  64. +89
    -0
      emission/emission.go
  65. +42
    -0
      emission/emission_test.go
  66. +8
    -0
      globals/dummy_test.go
  67. +98
    -0
      globals/globals.go
  68. +21
    -0
      licenses/aead.skein.license.txt
  69. +20
    -0
      licenses/boltdb.license.txt
  70. +20
    -0
      licenses/docopt.license.txt
  71. +23
    -0
      licenses/efbe.keccak.license.txt
  72. +27
    -0
      licenses/golang.license.txt
  73. +21
    -0
      licenses/logrus.license.txt
  74. +8
    -0
      licenses/msgp.license.txt
  75. +22
    -0
      licenses/readline.license.txt
  76. +2
    -0
      licenses/readme.txt
  77. +201
    -0
      licenses/rlog.license.txt
  78. +21
    -0
      licenses/stack.license.txt
  79. +20
    -0
      licenses/uuid.license
  80. +15
    -0
      licenses/x11-license.txt
  81. +121
    -0
      p2p/bc_notify_new_block.go
  82. +91
    -0
      p2p/bc_notify_new_transactions.go
  83. +178
    -0
      p2p/bc_notify_request_chain_handler.go
  84. +337
    -0
      p2p/bc_notify_request_getobjects.go
  85. +227
    -0
      p2p/bc_notify_response_chain_handler.go
  86. +128
    -0
      p2p/bc_notify_response_get_objects.go
  87. +72
    -0
      p2p/boost.go
  88. +74
    -0
      p2p/boost_test.go
  89. +194
    -0
      p2p/command_handler_p2p_handshake.go
  90. +68
    -0
      p2p/command_handler_p2p_supportflags.go
  91. +139
    -0
      p2p/command_handler_p2p_timed_sync.go
  92. +130
    -0
      p2p/connection_pool.go
  93. +85
    -0
      p2p/controller.go
  94. +27
    -0
      p2p/cryptonote_core_blockchain_command_handler.go
  95. +7
    -0
      p2p/external.go
  96. +383
    -0
      p2p/levin.go
  97. +56
    -0
      p2p/levin_test.go
  98. +27
    -0
      p2p/notes.txt
  99. +559
    -0
      p2p/p2p_command_handshake.go
  100. +176
    -0
      p2p/p2p_send_and_receive_timedsync.go

+ 52
- 0
Captain_Dero_pub.txt

@ -0,0 +1,52 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQSuBFpgP9IRDAC5HFDj9beW/6THlCHMPmjSCUeT0lKtT22uHbTA5CpZFTRvrjF8
l1QFpECuax2LiQUWCg2rl5LZtjE2BL53uNhPagGiUOnMC7w50i3YD/KWoanM9or4
8uNmkYRp7pgnjQKX+NK9TWJmLE94UMUgCUach+WXRG4ito/mc2U2A37Lonokpjb2
hnc3d2wSESg+N0Am91TNSiEo80/JVRcKlttyEHJo6FE1sW5Ll84hW8QeROwYa/kU
N8/jAAVTUc2KzMKknlVlGYRcfNframwCu2xUMlyX5Ghjrr3PmLgQX3qc3k/eTwAr
fHifdvZnsBTquLuOxFHk0xlvdSyoGeX3F0LKAXw1+Y6uyX9v7F4Ap7vEGsuCWfNW
hNIayxIM8iOeb6AOFQycL/GkI0Mv+SCd/8KqdAHT8FWjsJUnOWcYYKvFdN5jcORw
C6OVxf296Sj1Zrti6XVQv63/iaJ9at142AcVwbnvaR2h5IqyXdmzmszmoYVvf7jG
JVsmkwTrRvIgyMcBAOLrwQ7I4JGlL54nKr1mIvGRLZ2lH/2sfM2QHcTgcCQ5DACi
P0wOKlt6UgRQ27Aeh0LtOuFuZReXE8dIpD8f6l+zLS5Kii1SB1yffeSsQbTD6bvt
Ic6h88iUKypNHiFcFNncyad6f4zFYPB1ULXyFoZcpPo3jKjwNW/h//AymgfbqFUa
4dWgdVhdkSKB1BzSMamxKSv9O87Q/Zc2vTcA/0j9RjPsrRIfOCziob+kIcpuylA9
a71R9dJ7r2ivwvdOK2De/VHkEanM8qyPgmxdD03jLsx159fX7B9ItSdxg5i0K9sV
6mgfyGiHETminsW28f36O/WMH0SUnwjdG2eGJsZE2IOS/BqTXHRXQeFVR4b44Ubg
U9h8moORPxc1+/0IFN2Bq4AiLQZ9meCtTmCe3QHOWbKRZ3JydMpoohdU3l96ESXl
hNpD6C+froqQgemID51xe3iPRY947oXjeTD87AHDBcLD/vwE6Ys2Vi9mD5bXwoym
hrXCIh+v823HsJSQiN8QUDFfIMIgbATNemJTXs84EnWwBGLozvmuUvpVWXZSstcL
/ROivKTKRkTYqVZ+sX/yXzQM5Rp2LPF13JDeeATwrgTR9j8LSiycOOFcp3n+ndvy
tNg+GQAKYC5NZWL/OrrqRuFmjWkZu0234qZIFd0/oUQ5tqDGwy84L9f6PGPvshTR
yT6B4FpOqvPt10OQFfpD/h9ocFguNBw0AELjXUHk89bnBTU5cKGLkb1iOnGwtAgJ
mV6MJRjS/TKL6Ne2ddiv46fXlY05zJfg0ZHehe49BIZXQK8/9h5YJGmtcUZP19+6
xPTF5zXWs0k3yzoTGP2iCW/Ksf6b0t0fIIASGFAhQJUmGW1lKAcZTTt425G3NYOc
jmhJaFzcLpTnoqB8RKOTUzWXESXmA86cq4DtyQ2yzeLKBkroRGdpwvpZLH3MeDJ4
EIWSmcKPxm8oafMk6Ni9I4qQLFeSTHcF2qFoBMLKai1lqLd+NAzQmbXHDw6gOac8
+DBfIcaj0f5AK/0G39dOV+pg29pISt2PWDDhZ/XsjetrqcrnhsqNNRyplmmy0xR0
srQwQ2FwdGFpbiBEZXJvIChodHRwczovL2Rlcm8uaW8pIDxzdXBwb3J0QGRlcm8u
aW8+iJAEExEIADgWIQQPOeQljGU5R3AqgjQIsgNgoDqd6AUCWmA/0gIbAwULCQgH
AgYVCAkKCwIEFgIDAQIeAQIXgAAKCRAIsgNgoDqd6FYnAQChtgDnzVwe28s6WDTK
4bBa60dSZf1T08PCKl3+c3xx1QEA2R9K2CLQ6IsO9NXD5kA/pTQs5AxYc9bLo/eD
CZSe/4u5Aw0EWmA/0hAMALjwoBe35jZ7blE9n5mg6e57H0Bri43dkGsQEQ1fNaDq
7XByD0JAiZ20vrrfDsbXZQc+1SBGGOa38pGi6RKEf/q4krGe7EYx4hihHQuc+hco
PqOs6rN3+hfHerUolKpYlkGOSxO1ZjpvMOPBF1hz0Bj9NoPMWwVb5fdWis2BzKAu
GHFAX5Ls86KKZs19DRejWsdFtytEiqM7bAjUW75o3O24faxtByTa2SVmmkavCFS4
BpjDhIU2d5RqhJRkb9fqBU8MDFrmCQqSraQs/CqmOTYzM7E8wlk1SwylXN6yBFX3
RAwq1koFMw8yRMVzswEy917kTHS4IyM2yfYjbnENmWJuHiYJmgn8Lqw1QA3syIfP
E4qpzGBTBq3YXXOSymsNKZmKH0rK/G0l3p33rIagl5UXfr1LVd5XJRu6BzjKuk+q
uL3zb6d0ZSaT+aQ/Sju3shhWjGdCRVoT1shvBbQeyEU5ZLe5by6sp0FH9As3hRkN
0PDALEkhgQwl5hU8aIkwewADBQv/Xt31aVh+k/l+CwThAt9rMCDf2PQl0FKDH0pd
7Tcg1LgbqM20sF62PeLpRq+9iMe/pD/rNDEq94ANnCoqC5yyZvxganjG2Sxryzwc
jseZeq3t/He8vhiDxs3WwFbJSylzPG3u9xgyGkKDfGA74Iu+ASPOPOEOT4oLjI5E
s/tB7muD8l/lpkWij2BOopiZzieQntn8xW8eCFTocSAjZW52SoI1x/gw3NasILoB
nrTy0yOYlM01ucZOTB/0JKpzidkJg336amZdF4bLkfUPyCTE6kzG0PrLrQSeycr4
jkDfWfuFmRhKD2lDtoWDHqiPfe9IJkcTMnp5XfXAG3V2pAc+Mer1WIYajuHieO8m
oFNCzBc0obe9f+zEIBjoINco4FumxP78UZMzwe+hHrj8nFtju7WbKqGWumYH0L34
47tUoWXkCZs9Ni9DUIBVYWzEobgS7pl/H1HLR36klfAHLut0T9PZgipKRjSx1Ljz
M78wxVhupdDvHDEdKnq9E9lD6018iHgEGBEIACAWIQQPOeQljGU5R3AqgjQIsgNg
oDqd6AUCWmA/0gIbDAAKCRAIsgNgoDqd6LTZAQDESAvVHbtyKTwMmrx88p6Ljmtp
pKxKP0O5AFM7b7INbQEAtE3lAIBUA31x3fjC5L6UyGk/a2ssOWTsJx98YxMcPhs=
=H4Qj
-----END PGP PUBLIC KEY BLOCK-----

+ 89
- 0
LICENSE

@ -0,0 +1,89 @@
RESEARCH LICENSE
Version 1.1.2
I. DEFINITIONS.
"Licensee " means You and any other party that has entered into and has in effect a version of this License.
“Licensor” means DERO PROJECT(GPG: 0F39 E425 8C65 3947 702A 8234 08B2 0360 A03A 9DE8) and its successors and assignees.
"Modifications" means any (a) change or addition to the Technology or (b) new source or object code implementing any portion of the Technology.
"Research Use" means research, evaluation, or development for the purpose of advancing knowledge, teaching, learning, or customizing the Technology for personal use. Research Use expressly excludes use or distribution for direct or indirect commercial (including strategic) gain or advantage.
"Technology" means the source code, object code and specifications of the technology made available by Licensor pursuant to this License.
"Technology Site" means the website designated by Licensor for accessing the Technology.
"You" means the individual executing this License or the legal entity or entities represented by the individual executing this License.
II. PURPOSE.
Licensor is licensing the Technology under this Research License (the "License") to promote research, education, innovation, and development using the Technology.
COMMERCIAL USE AND DISTRIBUTION OF TECHNOLOGY AND MODIFICATIONS IS PERMITTED ONLY UNDER AN APPROPRIATE COMMERCIAL USE LICENSE AVAILABLE FROM LICENSOR AT <url>.
III. RESEARCH USE RIGHTS.
A. Subject to the conditions contained herein, Licensor grants to You a non-exclusive, non-transferable, worldwide, and royalty-free license to do the following for Your Research Use only:
1. reproduce, create Modifications of, and use the Technology alone, or with Modifications;
2. share source code of the Technology alone, or with Modifications, with other Licensees;
3. distribute object code of the Technology, alone, or with Modifications, to any third parties for Research Use only, under a license of Your choice that is consistent with this License; and
4. publish papers and books discussing the Technology which may include relevant excerpts that do not in the aggregate constitute a significant portion of the Technology.
B. Residual Rights. You may use any information in intangible form that you remember after accessing the Technology, except when such use violates Licensor's copyrights or patent rights.
C. No Implied Licenses. Other than the rights granted herein, Licensor retains all rights, title, and interest in Technology , and You retain all rights, title, and interest in Your Modifications and associated specifications, subject to the terms of this License.
D. Open Source Licenses. Portions of the Technology may be provided with notices and open source licenses from open source communities and third parties that govern the use of those portions, and any licenses granted hereunder do not alter any rights and obligations you may have under such open source licenses, however, the disclaimer of warranty and limitation of liability provisions in this License will apply to all Technology in this distribution.
IV. INTELLECTUAL PROPERTY REQUIREMENTS
As a condition to Your License, You agree to comply with the following restrictions and responsibilities:
A. License and Copyright Notices. You must include a copy of this License in a Readme file for any Technology or Modifications you distribute. You must also include the following statement, "Use and distribution of this technology is subject to the Java Research License included herein", (a) once prominently in the source code tree and/or specifications for Your source code distributions, and (b) once in the same file as Your copyright or proprietary notices for Your binary code distributions. You must cause any files containing Your Modification to carry prominent notice stating that You changed the files. You must not remove or alter any copyright or other proprietary notices in the Technology.
B. Licensee Exchanges. Any Technology and Modifications You receive from any Licensee are governed by this License.
V. GENERAL TERMS.
A. Disclaimer Of Warranties.
TECHNOLOGY IS PROVIDED "AS IS", WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT ANY SUCH TECHNOLOGY IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING OF THIRD PARTY RIGHTS. YOU AGREE THAT YOU BEAR THE ENTIRE RISK IN CONNECTION WITH YOUR USE AND DISTRIBUTION OF ANY AND ALL TECHNOLOGY UNDER THIS LICENSE.
B. Infringement; Limitation Of Liability.
1. If any portion of, or functionality implemented by, the Technology becomes the subject of a claim or threatened claim of infringement ("Affected Materials"), Licensor may, in its unrestricted discretion, suspend Your rights to use and distribute the Affected Materials under this License. Such suspension of rights will be effective immediately upon Licensor's posting of notice of suspension on the Technology Site.
2. IN NO EVENT WILL LICENSOR BE LIABLE FOR ANY DIRECT, INDIRECT, PUNITIVE, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES IN CONNECTION WITH OR ARISING OUT OF THIS LICENSE (INCLUDING, WITHOUT LIMITATION, LOSS OF PROFITS, USE, DATA, OR ECONOMIC ADVANTAGE OF ANY SORT), HOWEVER IT ARISES AND ON ANY THEORY OF LIABILITY (including negligence), WHETHER OR NOT LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. LIABILITY UNDER THIS SECTION V.B.2 SHALL BE SO LIMITED AND EXCLUDED, NOTWITHSTANDING FAILURE OF THE ESSENTIAL PURPOSE OF ANY REMEDY.
C. Termination.
1. You may terminate this License at any time by notifying Licensor in writing.
2. All Your rights will terminate under this License if You fail to comply with any of its material terms or conditions and do not cure such failure within thirty (30) days after becoming aware of such noncompliance.
3. Upon termination, You must discontinue all uses and distribution of the Technology , and all provisions of this Section V shall survive termination.
D. Miscellaneous.
1. Trademark. You agree to comply with Licensor's Trademark & Logo Usage Requirements, if any and as modified from time to time, available at the Technology Site. Except as expressly provided in this License, You are granted no rights in or to any Licensor's trademarks now or hereafter used or licensed by Licensor.
2. Integration. This License represents the complete agreement of the parties concerning the subject matter hereof.
3. Severability. If any provision of this License is held unenforceable, such provision shall be reformed to the extent necessary to make it enforceable unless to do so would defeat the intent of the parties, in which case, this License shall terminate.
4. Governing Law. This License is governed by the laws of the United States and the State of California, as applied to contracts entered into and performed in California between California residents. In no event shall this License be construed against the drafter.
5. Export Control. You agree to comply with the U.S. export controlsand trade laws of other countries that apply to Technology and Modifications.
READ ALL THE TERMS OF THIS LICENSE CAREFULLY BEFORE ACCEPTING.
BY CLICKING ON THE YES BUTTON BELOW OR USING THE TECHNOLOGY, YOU ARE ACCEPTING AND AGREEING TO ABIDE BY THE TERMS AND CONDITIONS OF THIS LICENSE. YOU MUST BE AT LEAST 18 YEARS OF AGE AND OTHERWISE COMPETENT TO ENTER INTO CONTRACTS.
IF YOU DO NOT MEET THESE CRITERIA, OR YOU DO NOT AGREE TO ANY OF THE TERMS OF THIS LICENSE, DO NOT USE THIS SOFTWARE IN ANY FORM.

+ 2
- 2
README.md

@ -5,12 +5,12 @@
DERO blockchain is a complete new blockchain supporting privacy on cryptonote protocol and smart contracts with lightening fast transactions.
DERO blockchain will be implemented in Golang.
We are pleased to announce first release update of DERO blockchain.
We are pleased to announce status update release 1 of DERO Blockchain.
**NOTE: DO NOT MIGRATE to this daemon. This is strictly for evaluation.**
LINK:
In the first status update release, following parts of cryptonote has been rewritten in golang.
In the status update release 1, Following parts of cryptonote has been rewritten in golang.
At present, Golang DERO daemon can sync and verify blockchain with the existing DERO network.

+ 71
- 0
address/address.go

@ -0,0 +1,71 @@
package address
import "fmt"
import "bytes"
import "encoding/binary"
import "github.com/deroproject/derosuite/crypto"
type Address struct {
Network uint64
SpendingKey []byte
ViewingKey []byte
//TODO add support for integrated address
}
const ChecksumLength = 4
type Checksum [ChecksumLength]byte
func GetChecksum(data ...[]byte) (result Checksum) {
keccak256 := crypto.Keccak256(data...)
copy(result[:], keccak256[:4])
return
}
func (a *Address) Base58() (result string) {
prefix := make([]byte, 9, 9)
n := binary.PutUvarint(prefix, a.Network)
prefix = prefix[:n]
checksum := GetChecksum(prefix, a.SpendingKey, a.ViewingKey)
result = EncodeDeroBase58(prefix, a.SpendingKey, a.ViewingKey, checksum[:])
return
}
func NewAddress(address string) (result *Address, err error) {
raw := DecodeDeroBase58(address)
// donot compare length to support much more user base and be compatible with cryptonote
if len(raw) < 69 { // 1 byte prefix + 32 byte key + 32 byte key + 4 byte checksum
err = fmt.Errorf("Address is the wrong length")
return
}
checksum := GetChecksum(raw[:len(raw)-4])
if bytes.Compare(checksum[:], raw[len(raw)-4:]) != 0 {
err = fmt.Errorf("Checksum does not validate")
return
}
// parse network first
address_prefix, done := binary.Uvarint(raw)
if done <= 0 {
err = fmt.Errorf("Network could not be parsed in address\n")
return
}
raw = raw[done:]
result = &Address{
Network: address_prefix,
SpendingKey: raw[0:32],
ViewingKey: raw[32:64],
}
return
}

+ 120
- 0
address/address_test.go

@ -0,0 +1,120 @@
package address
import "fmt"
import "bytes"
import "testing"
import "encoding/hex"
import "github.com/deroproject/derosuite/config"
func TestAddressError(t *testing.T) {
_, err := NewAddress("")
/*want := "Address is the wrong length"
if err != want {
t.Errorf("want: %s, got: %s", want, err)
}
*/
_, err = NewAddress("46w3n5EGhBeYmKvQRsd8UK9GhvcbYWQDobJape3NLMMFEjFZnJ3CnRmeKspubQGiP8iMTwFEX2QiBsjUkjKT4SSPd3fK1")
want := fmt.Errorf("Checksum does not validate")
if err.Error() != want.Error() {
t.Errorf("want: %s, got: %s", want, err)
}
}
func TestAddress(t *testing.T) {
const Monero_MainNetwork = 18
const Monero_TestNetwork = 53
tests := []struct {
name string
Network uint64
SpendingKeyHex string
ViewingKeyHex string
Address string
}{
{
name: "generic",
Network: Monero_MainNetwork,
SpendingKeyHex: "8c1a9d5ff5aaf1c3cdeb2a1be62f07a34ae6b15fe47a254c8bc240f348271679",
ViewingKeyHex: "0a29b163e392eb9416a52907fd7d3b84530f8d02ff70b1f63e72fdcb54cf7fe1",
Address: "46w3n5EGhBeZkYmKvQRsd8UK9GhvcbYWQDobJape3NLMMFEjFZnJ3CnRmeKspubQGiP8iMTwFEX2QiBsjUkjKT4SSPd3fKp",
},
{
name: "generic 2",
Network: Monero_MainNetwork,
SpendingKeyHex: "5007b84275af9a173c2080683afce90b2157ab640c18ddd5ce3e060a18a9ce99",
ViewingKeyHex: "27024b45150037b677418fcf11ba9675494ffdf994f329b9f7a8f8402b7934a0",
Address: "44f1Y84r9Lu4tQdLWRxV122rygfhUeVBrcmBaqcYCwUHScmf1ht8DFLXX9YN4T7nPPLcpqYLUdrFiY77nQYeH9RuK9gg4p6",
},
{
name: "require 1 padding in middle",
Network: Monero_MainNetwork,
SpendingKeyHex: "6add197bd82866e8bfbf1dc2fdf49873ec5f679059652da549cd806f2b166756",
ViewingKeyHex: "f5cf2897088fda0f7ac1c42491ed7d558a46ee41d0c81d038fd53ff4360afda0",
Address: "45fzHekTd5FfvxWBPYX2TqLPbtWjaofxYUeWCi6BRQXYFYd85sY2qw73bAuKhqY7deFJr6pN3STY81bZ9x2Zf4nGKASksqe",
},
{
name: "require 1 padding in last chunk",
Network: Monero_MainNetwork,
SpendingKeyHex: "50defe92d88b19aaf6bf66f061dd4380b79866a4122b25a03bceb571767dbe7b",
ViewingKeyHex: "f8f6f28283921bf5a17f0bcf4306233fc25ce9b6276154ad0de22aebc5c67702",
Address: "44grjkXtDHJVbZgtU1UKnrNXidcHfZ3HWToU5WjR3KgHMjgwrYLjXC6i5vm3HCp4vnBfYaNEyNiuZVwqtHD2SenS1JBRyco",
},
{
name: "testnet",
Network: Monero_TestNetwork,
SpendingKeyHex: "8de9cce254e60cd940abf6c77ef344c3a21fad74320e45734fbfcd5870e5c875",
ViewingKeyHex: "27024b45150037b677418fcf11ba9675494ffdf994f329b9f7a8f8402b7934a0",
Address: "9xYZvCDf6aFdLd7Qawg5XHZitWLKoeFvcLHfe5GxsGCFLbXSWeQNKciXX9YN4T7nPPLcpqYLUdrFiY77nQYeH9RuK9bogZJ",
},
{
name: "DERO testnet",
Network: config.Testnet.Public_Address_Prefix,
SpendingKeyHex: "ffb4baf32792d38d36c5f1792201d1cff142a10bad6aa088090156a35858739d",
ViewingKeyHex: "0ea428a9608fc9dc06acceea608ac97cc9119647b943941a381306548ee43455",
Address: "dETosYceeTxRZQBk5hQzN51JepzZn5H24JqR96q7mY7ZFo6JhJKPNSKR3vs9ES1ibyQDQgeRheDP6CJbb7AKJY2H9eacz2RtPy",
},
}
var base58 string
var spendingKey, viewingKey []byte
for _, test := range tests {
spendingKey, _ = hex.DecodeString(test.SpendingKeyHex)
viewingKey, _ = hex.DecodeString(test.ViewingKeyHex)
_ = spendingKey
_ = viewingKey
address, err := NewAddress(test.Address)
if err != nil {
t.Errorf("%s: Failed while parsing address %s", test.name, err)
continue
}
_ = address
if address.Network != test.Network {
t.Errorf("%s: want: %d, got: %d", test.name, test.Network, address.Network)
continue
}
if bytes.Compare(address.SpendingKey, spendingKey) != 0 {
t.Errorf("%s: want: %x, got: %x", test.name, spendingKey, address.SpendingKey)
continue
}
if bytes.Compare(address.ViewingKey, viewingKey) != 0 {
t.Errorf("%s: want: %x, got: %x", test.name, viewingKey, address.ViewingKey)
continue
}
base58 = address.Base58()
if base58 != test.Address {
t.Errorf("%s: want: %s, got: %s", test.name, test.Address, base58)
continue
}
}
}

+ 77
- 0
address/base58.go

@ -0,0 +1,77 @@
package address
import "strings"
import "math/big"
// all characters in the base58
const BASE58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
var base58Lookup = map[string]int{
"1": 0, "2": 1, "3": 2, "4": 3, "5": 4, "6": 5, "7": 6, "8": 7,
"9": 8, "A": 9, "B": 10, "C": 11, "D": 12, "E": 13, "F": 14, "G": 15,
"H": 16, "J": 17, "K": 18, "L": 19, "M": 20, "N": 21, "P": 22, "Q": 23,
"R": 24, "S": 25, "T": 26, "U": 27, "V": 28, "W": 29, "X": 30, "Y": 31,
"Z": 32, "a": 33, "b": 34, "c": 35, "d": 36, "e": 37, "f": 38, "g": 39,
"h": 40, "i": 41, "j": 42, "k": 43, "m": 44, "n": 45, "o": 46, "p": 47,
"q": 48, "r": 49, "s": 50, "t": 51, "u": 52, "v": 53, "w": 54, "x": 55,
"y": 56, "z": 57,
}
var bigBase = big.NewInt(58)
func encodeChunk(raw []byte, padding int) (result string) {
remainder := new(big.Int)
remainder.SetBytes(raw)
bigZero := new(big.Int)
for remainder.Cmp(bigZero) > 0 {
current := new(big.Int)
remainder.DivMod(remainder, bigBase, current)
result = string(BASE58[current.Int64()]) + result
}
if len(result) < padding {
result = strings.Repeat("1", (padding-len(result))) + result
}
return
}
func decodeChunk(encoded string) (result []byte) {
bigResult := big.NewInt(0)
currentMultiplier := big.NewInt(1)
tmp := new(big.Int)
for i := len(encoded) - 1; i >= 0; i-- {
tmp.SetInt64(int64(base58Lookup[string(encoded[i])]))
tmp.Mul(currentMultiplier, tmp)
bigResult.Add(bigResult, tmp)
currentMultiplier.Mul(currentMultiplier, bigBase)
}
result = bigResult.Bytes()
return
}
func EncodeDeroBase58(data ...[]byte) (result string) {
var combined []byte
for _, item := range data {
combined = append(combined, item...)
}
length := len(combined)
rounds := length / 8
for i := 0; i < rounds; i++ {
result += encodeChunk(combined[i*8:(i+1)*8], 11)
}
if length%8 > 0 {
result += encodeChunk(combined[rounds*8:], 7)
}
return
}
func DecodeDeroBase58(data string) (result []byte) {
length := len(data)
rounds := length / 11
for i := 0; i < rounds; i++ {
result = append(result, decodeChunk(data[i*11:(i+1)*11])...)
}
if length%11 > 0 {
result = append(result, decodeChunk(data[rounds*11:])...)
}
return
}

+ 24
- 0
alt_chain_data/038e2b878d3c204b8726a4159d8e2c113900a7180015ea30a48eda138ed043c7_17004.txt

@ -0,0 +1,24 @@
{
"id": "0",
"jsonrpc": "2.0",
"result": {
"blob": "0606a8dfd3d2058e7a86de29068834e3dd3a498c4717159cceec7b6f0753b65cbf889d564a69823c7c008a02a8850101ffec84010197c39fb0ecf3060235527c1f4ad20b7003a4a714801d5e0fc5c44538256b2c80e4c98a75be7424ed2b01296ce3ef0900a4d99c574f918e11b1e72e536b8f3a69702bf307800b8852037a02080000000002f64c030000",
"block_header": {
"block_size": 95,
"depth": 862,
"difficulty": 92945024,
"hash": "038e2b878d3c204b8726a4159d8e2c113900a7180015ea30a48eda138ed043c7",
"height": 17004,
"major_version": 6,
"minor_version": 6,
"nonce": 2315287612,
"num_txes": 0,
"orphan_status": true,
"prev_hash": "8e7a86de29068834e3dd3a498c4717159cceec7b6f0753b65cbf889d564a6982",
"reward": 30368741187991,
"timestamp": 1515515816
},
"json": "{\n \"major_version\": 6, \n \"minor_version\": 6, \n \"timestamp\": 1515515816, \n \"prev_id\": \"8e7a86de29068834e3dd3a498c4717159cceec7b6f0753b65cbf889d564a6982\", \n \"nonce\": 2315287612, \n \"miner_tx\": {\n \"version\": 2, \n \"unlock_time\": 17064, \n \"vin\": [ {\n \"gen\": {\n \"height\": 17004\n }\n }\n ], \n \"vout\": [ {\n \"amount\": 30368741187991, \n \"target\": {\n \"key\": \"35527c1f4ad20b7003a4a714801d5e0fc5c44538256b2c80e4c98a75be7424ed\"\n }\n }\n ], \n \"extra\": [ 1, 41, 108, 227, 239, 9, 0, 164, 217, 156, 87, 79, 145, 142, 17, 177, 231, 46, 83, 107, 143, 58, 105, 112, 43, 243, 7, 128, 11, 136, 82, 3, 122, 2, 8, 0, 0, 0, 0, 2, 246, 76, 3\n ], \n \"rct_signatures\": {\n \"type\": 0\n }\n }, \n \"tx_hashes\": [ ]\n}",
"status": "OK"
}
}

+ 25
- 0
alt_chain_data/03ca43b2dbfefe2b1854c516a4d30a7feeb5386dfe1169c3caf33ec3ff898582_16998.txt

@ -0,0 +1,25 @@
{
"id": "0",
"jsonrpc": "2.0",
"result": {
"blob": "0606c8b2d3d20578581a7ea3a71082ead85cfde06ebb2a4d7bbfcfef9a0fe69e373e78b807fa7f9662013d02a2850101ffe6840101def3eaad9ba00702bb5bb6f749afdd7d64d8b3b4fa2b8a44c77bf637d0c13d3a994b1d269d8780f82b013eeb27eeff0bd10d40f5892504ec95d81399d150cefa504a0fbacfe6fdc8a563020800000000a0472ac70017abda0ac60520cf99e8b28612349ae639fcbdcc4b1ee256bd064660e96029ba63b0450e0e92ee7af62ca755e2f0522e68a4c11497c1179e09dad0a807729455abc27d43eefa50e1714eb0714e45742c7c6c4dd718b494d3290177dd18172a5acc03d87bf62b5c7d6980a78a238061894f2baf3e3929dc006abcd3622f80adc7dcd900d779234f61b29f6f98ba0956a08082607d6c95ead01c252a5086b413a9fd3a0dd65e7e2005395c69a0a676af4048a69e4191b5948d1cae9cb548ef8bc79dd8e247ed59b2b67e7ff1d7129de06038c388bc55581585b178c440eb9d8e60ab843859096066f80ca797d71db0b481d27f64791dc4cfc621ab462d455c94f7424ab37e5a8ca54eca36d5c0dc096cf85b3022f5c5461f704eea1b48b625189e13575dc5528ba9321dea609fa0574b36a4225c7c850f6b78ce68b2a6d6f001bf9874400f342178ef174768af48d2f3bab1cf647684315321fa09ee984b18667712a37f8767923e0f43524a27b4744a2a189ab42b0eee3505aad99afa25952a9debf3fb8f643488edc07d19bd4ce931e82405a22f3f65aa2500146eeafe7aa2785e9e25f5dc5283b684e29582242db611927cdb6a33636b916a6923f56afe2233bc6cd9e3fb4c999eda97345ce93f6af87d917a110c24b885e40b1bab0213a3dc13ea61c256f8cae39eda04a7d4f234a4b8a664834135bd01169b5ae91683a0b11e245b7e3ac9088f863f260c9697b420a80b9e1ec0713b230a2ae4b2edb9f8fad5616c670ff266a41b2006fdfbe835682a6f97486c2df19c3a1052202b74e0d65a59c2fada5e48344a33d0b1d97d1b7fd261a3f8226644c8ca8127fd3a4347ae5bd5b09a9bbbfcb1026da1a2ef4521247d53c0d71abcec48f9d6112f6dd43627aa882a53d4f7e97b27e52c57b66eefa55d2e855459fb46b2f273f564f390fa3f005281716c770f50afbbc1946e1516e6bddd2ce0ea826a4a6f433cadfeb962e4b97964737a3bada86e6ec67b36b94873402dbf72fd53f7f7f5c8804ddb82069f41",
"block_header": {
"block_size": 95,
"depth": 866,
"difficulty": 89115444,
"hash": "03ca43b2dbfefe2b1854c516a4d30a7feeb5386dfe1169c3caf33ec3ff898582",
"height": 16998,
"major_version": 6,
"minor_version": 6,
"nonce": 1023500950,
"num_txes": 23,
"orphan_status": true,
"prev_hash": "78581a7ea3a71082ead85cfde06ebb2a4d7bbfcfef9a0fe69e373e78b807fa7f",
"reward": 31893181086174,
"timestamp": 1515510088
},
"json": "{\n \"major_version\": 6, \n \"minor_version\": 6, \n \"timestamp\": 1515510088, \n \"prev_id\": \"78581a7ea3a71082ead85cfde06ebb2a4d7bbfcfef9a0fe69e373e78b807fa7f\", \n \"nonce\": 1023500950, \n \"miner_tx\": {\n \"version\": 2, \n \"unlock_time\": 17058, \n \"vin\": [ {\n \"gen\": {\n \"height\": 16998\n }\n }\n ], \n \"vout\": [ {\n \"amount\": 31893181086174, \n \"target\": {\n \"key\": \"bb5bb6f749afdd7d64d8b3b4fa2b8a44c77bf637d0c13d3a994b1d269d8780f8\"\n }\n }\n ], \n \"extra\": [ 1, 62, 235, 39, 238, 255, 11, 209, 13, 64, 245, 137, 37, 4, 236, 149, 216, 19, 153, 209, 80, 206, 250, 80, 74, 15, 186, 207, 230, 253, 200, 165, 99, 2, 8, 0, 0, 0, 0, 160, 71, 42, 199\n ], \n \"rct_signatures\": {\n \"type\": 0\n }\n }, \n \"tx_hashes\": [ \"abda0ac60520cf99e8b28612349ae639fcbdcc4b1ee256bd064660e96029ba63\", \"b0450e0e92ee7af62ca755e2f0522e68a4c11497c1179e09dad0a807729455ab\", \"c27d43eefa50e1714eb0714e45742c7c6c4dd718b494d3290177dd18172a5acc\", \"03d87bf62b5c7d6980a78a238061894f2baf3e3929dc006abcd3622f80adc7dc\", \"d900d779234f61b29f6f98ba0956a08082607d6c95ead01c252a5086b413a9fd\", \"3a0dd65e7e2005395c69a0a676af4048a69e4191b5948d1cae9cb548ef8bc79d\", \"d8e247ed59b2b67e7ff1d7129de06038c388bc55581585b178c440eb9d8e60ab\", \"843859096066f80ca797d71db0b481d27f64791dc4cfc621ab462d455c94f742\", \"4ab37e5a8ca54eca36d5c0dc096cf85b3022f5c5461f704eea1b48b625189e13\", \"575dc5528ba9321dea609fa0574b36a4225c7c850f6b78ce68b2a6d6f001bf98\", \"74400f342178ef174768af48d2f3bab1cf647684315321fa09ee984b18667712\", \"a37f8767923e0f43524a27b4744a2a189ab42b0eee3505aad99afa25952a9deb\", \"f3fb8f643488edc07d19bd4ce931e82405a22f3f65aa2500146eeafe7aa2785e\", \"9e25f5dc5283b684e29582242db611927cdb6a33636b916a6923f56afe2233bc\", \"6cd9e3fb4c999eda97345ce93f6af87d917a110c24b885e40b1bab0213a3dc13\", \"ea61c256f8cae39eda04a7d4f234a4b8a664834135bd01169b5ae91683a0b11e\", \"245b7e3ac9088f863f260c9697b420a80b9e1ec0713b230a2ae4b2edb9f8fad5\", \"616c670ff266a41b2006fdfbe835682a6f97486c2df19c3a1052202b74e0d65a\", \"59c2fada5e48344a33d0b1d97d1b7fd261a3f8226644c8ca8127fd3a4347ae5b\", \"d5b09a9bbbfcb1026da1a2ef4521247d53c0d71abcec48f9d6112f6dd43627aa\", \"882a53d4f7e97b27e52c57b66eefa55d2e855459fb46b2f273f564f390fa3f00\", \"5281716c770f50afbbc1946e1516e6bddd2ce0ea826a4a6f433cadfeb962e4b9\", \"7964737a3bada86e6ec67b36b94873402dbf72fd53f7f7f5c8804ddb82069f41\"\n ]\n}",
"status": "OK",
"tx_hashes": ["abda0ac60520cf99e8b28612349ae639fcbdcc4b1ee256bd064660e96029ba63","b0450e0e92ee7af62ca755e2f0522e68a4c11497c1179e09dad0a807729455ab","c27d43eefa50e1714eb0714e45742c7c6c4dd718b494d3290177dd18172a5acc","03d87bf62b5c7d6980a78a238061894f2baf3e3929dc006abcd3622f80adc7dc","d900d779234f61b29f6f98ba0956a08082607d6c95ead01c252a5086b413a9fd","3a0dd65e7e2005395c69a0a676af4048a69e4191b5948d1cae9cb548ef8bc79d","d8e247ed59b2b67e7ff1d7129de06038c388bc55581585b178c440eb9d8e60ab","843859096066f80ca797d71db0b481d27f64791dc4cfc621ab462d455c94f742","4ab37e5a8ca54eca36d5c0dc096cf85b3022f5c5461f704eea1b48b625189e13","575dc5528ba9321dea609fa0574b36a4225c7c850f6b78ce68b2a6d6f001bf98","74400f342178ef174768af48d2f3bab1cf647684315321fa09ee984b18667712","a37f8767923e0f43524a27b4744a2a189ab42b0eee3505aad99afa25952a9deb","f3fb8f643488edc07d19bd4ce931e82405a22f3f65aa2500146eeafe7aa2785e","9e25f5dc5283b684e29582242db611927cdb6a33636b916a6923f56afe2233bc","6cd9e3fb4c999eda97345ce93f6af87d917a110c24b885e40b1bab0213a3dc13","ea61c256f8cae39eda04a7d4f234a4b8a664834135bd01169b5ae91683a0b11e","245b7e3ac9088f863f260c9697b420a80b9e1ec0713b230a2ae4b2edb9f8fad5","616c670ff266a41b2006fdfbe835682a6f97486c2df19c3a1052202b74e0d65a","59c2fada5e48344a33d0b1d97d1b7fd261a3f8226644c8ca8127fd3a4347ae5b","d5b09a9bbbfcb1026da1a2ef4521247d53c0d71abcec48f9d6112f6dd43627aa","882a53d4f7e97b27e52c57b66eefa55d2e855459fb46b2f273f564f390fa3f00","5281716c770f50afbbc1946e1516e6bddd2ce0ea826a4a6f433cadfeb962e4b9","7964737a3bada86e6ec67b36b94873402dbf72fd53f7f7f5c8804ddb82069f41"]
}
}

+ 25
- 0
alt_chain_data/0eb61f1b78d810d314769e5e6157bc22894127b5d8e5bc417e99091005a7b858_16999.txt

@ -0,0 +1,25 @@
{
"id": "0",
"jsonrpc": "2.0",
"result": {
"blob": "0606d0c9d3d20503ca43b2dbfefe2b1854c516a4d30a7feeb5386dfe1169c3caf33ec3ff898582ed0300d702a3850101ffe78401019fd4ef8592a007025a5d066ac8f5772be60aad59ac33813937efb7edd090006d83f7b625d56deeb72b012c28df62dfe6eda5c5d3113f8d1f3de463f99937e57369ce44b26580444dbf1b0208000000000cf68274001774149ceb2f51f2b8a71536e1e1ddd57a141cdb19d9dea33bf10b09fd336617b0b0f82f931a8147cdc44dbaa9d95425c9980dcd5ab9065892fbeed204902e339f31ca3214fdecac929eb7a4d09fcd799bbe12c5adda9740fa20e4a58178bf32fa014252ccdfb7f5e97838ec2e142eb7ba77115ad0ce34f875e8d0e7d15a2aaa4ddd07a95dd7b66f7b3354c455b712014acf488051700d91869abd0c386ebb40c8c958b296459577ac81d472c1b7c026cfbf355bf86c603fc98615b8f3ae17d17cb4f8edbbdf0bdaf1f269651ef74c1e653d84ff5c597031a008e58f68a04db944da76a0ee549dbadbf47bffa9d729a7747226496b7e213f0729639eebeb6f48108a82a1ca79591e9efd8c6c020be08b02e76d2ebf61aa3f21b84f90bf2a618b477bb01c4b62bd317b4cf92b846d81f8eee205689e4f084e189f3124e697877f67d857f478803f81c1f8b7b893f3f8af830833600d75c43e61733c648cc4a524b50e32232cf5e0e6d9b5c4e539e3d33578aa9c82b54e549e92b1e04d16f3f9dbd156163b6fca1ce4c1572482f2d7dc16634043a7b828488f0b8e5178ddedab49bba1b6646110f2437e83782f50dcffedc67ea4f59971681a3e0f552de091bebbd2c0be66fd022b64d3c61e450513bb4edd1123ef43b1da771bcd94141b29ca933db89499001acb38098e666a8c8d426692ab16547009db6f55a1badc84ea714fbe9c6d63cbfdba522c4d9bac212fdabce699691c646e50fe77e6b6110174d4ff592de728b309cd842af835b80b5966df29fa10d5341c7c3637922b7dfa7ec00937b17522a0136067fe7823bd1808ff8850556584f39b61cc774871a4ff3adedab05cad0e401308b0e9ecd231a4d50f85bc50babfd3701256c7dfaa5b86f37cbe7dc71ce59eb3fbfc0afc6a80b2a4450673f9a44772eee79df273d1bdd43faf13f813dc222c7c88d371504d721be75332b20e3278afefcdb1b4c5a36aee6ae4a5fd7aec7e4f79daab35affd5f8fa2edbd40b83e761b8d59a74cf87e77944244539b",
"block_header": {
"block_size": 95,
"depth": 866,
"difficulty": 92722474,
"hash": "0eb61f1b78d810d314769e5e6157bc22894127b5d8e5bc417e99091005a7b858",
"height": 16999,
"major_version": 6,
"minor_version": 6,
"nonce": 3607102445,
"num_txes": 23,
"orphan_status": true,
"prev_hash": "03ca43b2dbfefe2b1854c516a4d30a7feeb5386dfe1169c3caf33ec3ff898582",
"reward": 31890681358879,
"timestamp": 1515513040
},
"json": "{\n \"major_version\": 6, \n \"minor_version\": 6, \n \"timestamp\": 1515513040, \n \"prev_id\": \"03ca43b2dbfefe2b1854c516a4d30a7feeb5386dfe1169c3caf33ec3ff898582\", \n \"nonce\": 3607102445, \n \"miner_tx\": {\n \"version\": 2, \n \"unlock_time\": 17059, \n \"vin\": [ {\n \"gen\": {\n \"height\": 16999\n }\n }\n ], \n \"vout\": [ {\n \"amount\": 31890681358879, \n \"target\": {\n \"key\": \"5a5d066ac8f5772be60aad59ac33813937efb7edd090006d83f7b625d56deeb7\"\n }\n }\n ], \n \"extra\": [ 1, 44, 40, 223, 98, 223, 230, 237, 165, 197, 211, 17, 63, 141, 31, 61, 228, 99, 249, 153, 55, 229, 115, 105, 206, 68, 178, 101, 128, 68, 77, 191, 27, 2, 8, 0, 0, 0, 0, 12, 246, 130, 116\n ], \n \"rct_signatures\": {\n \"type\": 0\n }\n }, \n \"tx_hashes\": [ \"74149ceb2f51f2b8a71536e1e1ddd57a141cdb19d9dea33bf10b09fd336617b0\", \"b0f82f931a8147cdc44dbaa9d95425c9980dcd5ab9065892fbeed204902e339f\", \"31ca3214fdecac929eb7a4d09fcd799bbe12c5adda9740fa20e4a58178bf32fa\", \"014252ccdfb7f5e97838ec2e142eb7ba77115ad0ce34f875e8d0e7d15a2aaa4d\", \"dd07a95dd7b66f7b3354c455b712014acf488051700d91869abd0c386ebb40c8\", \"c958b296459577ac81d472c1b7c026cfbf355bf86c603fc98615b8f3ae17d17c\", \"b4f8edbbdf0bdaf1f269651ef74c1e653d84ff5c597031a008e58f68a04db944\", \"da76a0ee549dbadbf47bffa9d729a7747226496b7e213f0729639eebeb6f4810\", \"8a82a1ca79591e9efd8c6c020be08b02e76d2ebf61aa3f21b84f90bf2a618b47\", \"7bb01c4b62bd317b4cf92b846d81f8eee205689e4f084e189f3124e697877f67\", \"d857f478803f81c1f8b7b893f3f8af830833600d75c43e61733c648cc4a524b5\", \"0e32232cf5e0e6d9b5c4e539e3d33578aa9c82b54e549e92b1e04d16f3f9dbd1\", \"56163b6fca1ce4c1572482f2d7dc16634043a7b828488f0b8e5178ddedab49bb\", \"a1b6646110f2437e83782f50dcffedc67ea4f59971681a3e0f552de091bebbd2\", \"c0be66fd022b64d3c61e450513bb4edd1123ef43b1da771bcd94141b29ca933d\", \"b89499001acb38098e666a8c8d426692ab16547009db6f55a1badc84ea714fbe\", \"9c6d63cbfdba522c4d9bac212fdabce699691c646e50fe77e6b6110174d4ff59\", \"2de728b309cd842af835b80b5966df29fa10d5341c7c3637922b7dfa7ec00937\", \"b17522a0136067fe7823bd1808ff8850556584f39b61cc774871a4ff3adedab0\", \"5cad0e401308b0e9ecd231a4d50f85bc50babfd3701256c7dfaa5b86f37cbe7d\", \"c71ce59eb3fbfc0afc6a80b2a4450673f9a44772eee79df273d1bdd43faf13f8\", \"13dc222c7c88d371504d721be75332b20e3278afefcdb1b4c5a36aee6ae4a5fd\", \"7aec7e4f79daab35affd5f8fa2edbd40b83e761b8d59a74cf87e77944244539b\"\n ]\n}",
"status": "OK",
"tx_hashes": ["74149ceb2f51f2b8a71536e1e1ddd57a141cdb19d9dea33bf10b09fd336617b0","b0f82f931a8147cdc44dbaa9d95425c9980dcd5ab9065892fbeed204902e339f","31ca3214fdecac929eb7a4d09fcd799bbe12c5adda9740fa20e4a58178bf32fa","014252ccdfb7f5e97838ec2e142eb7ba77115ad0ce34f875e8d0e7d15a2aaa4d","dd07a95dd7b66f7b3354c455b712014acf488051700d91869abd0c386ebb40c8","c958b296459577ac81d472c1b7c026cfbf355bf86c603fc98615b8f3ae17d17c","b4f8edbbdf0bdaf1f269651ef74c1e653d84ff5c597031a008e58f68a04db944","da76a0ee549dbadbf47bffa9d729a7747226496b7e213f0729639eebeb6f4810","8a82a1ca79591e9efd8c6c020be08b02e76d2ebf61aa3f21b84f90bf2a618b47","7bb01c4b62bd317b4cf92b846d81f8eee205689e4f084e189f3124e697877f67","d857f478803f81c1f8b7b893f3f8af830833600d75c43e61733c648cc4a524b5","0e32232cf5e0e6d9b5c4e539e3d33578aa9c82b54e549e92b1e04d16f3f9dbd1","56163b6fca1ce4c1572482f2d7dc16634043a7b828488f0b8e5178ddedab49bb","a1b6646110f2437e83782f50dcffedc67ea4f59971681a3e0f552de091bebbd2","c0be66fd022b64d3c61e450513bb4edd1123ef43b1da771bcd94141b29ca933d","b89499001acb38098e666a8c8d426692ab16547009db6f55a1badc84ea714fbe","9c6d63cbfdba522c4d9bac212fdabce699691c646e50fe77e6b6110174d4ff59","2de728b309cd842af835b80b5966df29fa10d5341c7c3637922b7dfa7ec00937","b17522a0136067fe7823bd1808ff8850556584f39b61cc774871a4ff3adedab0","5cad0e401308b0e9ecd231a4d50f85bc50babfd3701256c7dfaa5b86f37cbe7d","c71ce59eb3fbfc0afc6a80b2a4450673f9a44772eee79df273d1bdd43faf13f8","13dc222c7c88d371504d721be75332b20e3278afefcdb1b4c5a36aee6ae4a5fd","7aec7e4f79daab35affd5f8fa2edbd40b83e761b8d59a74cf87e77944244539b"]
}
}

+ 24
- 0
alt_chain_data/1ae49ffd0caa6ffb6dc5617a06ae75e7d140303d69e06f4b6b0dcc4bc98383bc_16996.txt

@ -0,0 +1,24 @@
{
"id": "0",
"jsonrpc": "2.0",
"result": {
"blob": "06069dacd3d20516417000452bb3c27c028024a1a21b2d8264a13967cc2b070563b7cb2ecc8236240215db02a0850101ffe484010192ca908deef30602b7840aca58a897165d0c4c90a6e1519d635808f5f32d0dfd8cec545751f9719a2b0185ab9544cf529130c7557fa1a7d3ebc0a25f832ca909bca1e67ca1fb38b472fe02080000000007472ac70000",
"block_header": {
"block_size": 95,
"depth": 868,
"difficulty": 84590039,
"hash": "1ae49ffd0caa6ffb6dc5617a06ae75e7d140303d69e06f4b6b0dcc4bc98383bc",
"height": 16996,
"major_version": 6,
"minor_version": 6,
"nonce": 3675587108,
"num_txes": 0,
"orphan_status": true,
"prev_hash": "16417000452bb3c27c028024a1a21b2d8264a13967cc2b070563b7cb2ecc8236",
"reward": 30369204413714,
"timestamp": 1515509277
},
"json": "{\n \"major_version\": 6, \n \"minor_version\": 6, \n \"timestamp\": 1515509277, \n \"prev_id\": \"16417000452bb3c27c028024a1a21b2d8264a13967cc2b070563b7cb2ecc8236\", \n \"nonce\": 3675587108, \n \"miner_tx\": {\n \"version\": 2, \n \"unlock_time\": 17056, \n \"vin\": [ {\n \"gen\": {\n \"height\": 16996\n }\n }\n ], \n \"vout\": [ {\n \"amount\": 30369204413714, \n \"target\": {\n \"key\": \"b7840aca58a897165d0c4c90a6e1519d635808f5f32d0dfd8cec545751f9719a\"\n }\n }\n ], \n \"extra\": [ 1, 133, 171, 149, 68, 207, 82, 145, 48, 199, 85, 127, 161, 167, 211, 235, 192, 162, 95, 131, 44, 169, 9, 188, 161, 230, 124, 161, 251, 56, 180, 114, 254, 2, 8, 0, 0, 0, 0, 7, 71, 42, 199\n ], \n \"rct_signatures\": {\n \"type\": 0\n }\n }, \n \"tx_hashes\": [ ]\n}",
"status": "OK"
}
}

+ 24
- 0
alt_chain_data/1d9687780f854ea310bf5d7ea5e756be167fa043d81a58c3a3e602e08e7ad920_17006.txt

@ -0,0 +1,24 @@
{
"id": "0",
"jsonrpc": "2.0",
"result": {
"blob": "0606d9e7d3d2059249ac65bef9acb1b4375a50d9857627ef9f78961fe8fdd6068621572a174bd61c1e006802aa850101ffee84010183e180f9ebf30602edc679784518be061a918062d7b11544948d091e5f06d389d849156100d47b5e2b01a9128d72db29bc746278350850ba4d12a904dc565a03265c599c30b5b34e921a02080000000004472ac70000",
"block_header": {
"block_size": 95,
"depth": 860,
"difficulty": 106837230,
"hash": "1d9687780f854ea310bf5d7ea5e756be167fa043d81a58c3a3e602e08e7ad920",
"height": 17006,
"major_version": 6,
"minor_version": 6,
"nonce": 1744838172,
"num_txes": 0,
"orphan_status": true,
"prev_hash": "9249ac65bef9acb1b4375a50d9857627ef9f78961fe8fdd6068621572a174bd6",
"reward": 30368625340547,
"timestamp": 1515516889
},
"json": "{\n \"major_version\": 6, \n \"minor_version\": 6, \n \"timestamp\": 1515516889, \n \"prev_id\": \"9249ac65bef9acb1b4375a50d9857627ef9f78961fe8fdd6068621572a174bd6\", \n \"nonce\": 1744838172, \n \"miner_tx\": {\n \"version\": 2, \n \"unlock_time\": 17066, \n \"vin\": [ {\n \"gen\": {\n \"height\": 17006\n }\n }\n ], \n \"vout\": [ {\n \"amount\": 30368625340547, \n \"target\": {\n \"key\": \"edc679784518be061a918062d7b11544948d091e5f06d389d849156100d47b5e\"\n }\n }\n ], \n \"extra\": [ 1, 169, 18, 141, 114, 219, 41, 188, 116, 98, 120, 53, 8, 80, 186, 77, 18, 169, 4, 220, 86, 90, 3, 38, 92, 89, 156, 48, 181, 179, 78, 146, 26, 2, 8, 0, 0, 0, 0, 4, 71, 42, 199\n ], \n \"rct_signatures\": {\n \"type\": 0\n }\n }, \n \"tx_hashes\": [ ]\n}",
"status": "OK"
}
}

+ 24
- 0
alt_chain_data/6360ac8eba51cf1215461f304635e01935345f7fb7fa0c1ce3263ac131b5cb35_17007.txt

@ -0,0 +1,24 @@
{
"id": "0",
"jsonrpc": "2.0",
"result": {
"blob": "0606a9e9d3d2051d9687780f854ea310bf5d7ea5e756be167fa043d81a58c3a3e602e08e7ad9209929090602ab850101ffef8401019fb1b1ddebf30602c7b8a1da31ed781b3153245337e5c34aab53055ebc64a0de8a7c336af1a233ce2b016cbf39ec1ad5079ec981f8ce5b0e59b2e014199c0476496cf404a1323adb6d4d02080000000001bcd9f60000",
"block_header": {
"block_size": 95,
"depth": 859,
"difficulty": 87833212,
"hash": "6360ac8eba51cf1215461f304635e01935345f7fb7fa0c1ce3263ac131b5cb35",
"height": 17007,
"major_version": 6,
"minor_version": 6,
"nonce": 101263769,
"num_txes": 0,
"orphan_status": true,
"prev_hash": "1d9687780f854ea310bf5d7ea5e756be167fa043d81a58c3a3e602e08e7ad920",
"reward": 30368567416991,
"timestamp": 1515517097
},
"json": "{\n \"major_version\": 6, \n \"minor_version\": 6, \n \"timestamp\": 1515517097, \n \"prev_id\": \"1d9687780f854ea310bf5d7ea5e756be167fa043d81a58c3a3e602e08e7ad920\", \n \"nonce\": 101263769, \n \"miner_tx\": {\n \"version\": 2, \n \"unlock_time\": 17067, \n \"vin\": [ {\n \"gen\": {\n \"height\": 17007\n }\n }\n ], \n \"vout\": [ {\n \"amount\": 30368567416991, \n \"target\": {\n \"key\": \"c7b8a1da31ed781b3153245337e5c34aab53055ebc64a0de8a7c336af1a233ce\"\n }\n }\n ], \n \"extra\": [ 1, 108, 191, 57, 236, 26, 213, 7, 158, 201, 129, 248, 206, 91, 14, 89, 178, 224, 20, 25, 156, 4, 118, 73, 108, 244, 4, 161, 50, 58, 219, 109, 77, 2, 8, 0, 0, 0, 0, 1, 188, 217, 246\n ], \n \"rct_signatures\": {\n \"type\": 0\n }\n }, \n \"tx_hashes\": [ ]\n}",
"status": "OK"
}
}

+ 25
- 0
alt_chain_data/77d6b92961146232ba36ced359f6dbbf3170299b530d2c7d0da5c398f7713d23_17008.txt

@ -0,0 +1,25 @@
{
"id": "0",
"jsonrpc": "2.0",
"result": {
"blob": "0606f3efd3d2056360ac8eba51cf1215461f304635e01935345f7fb7fa0c1ce3263ac131b5cb35ff94049c02ac850101fff0840101aa9fddd9e3f90602a96b487a44e654d507ce0874bf5464b6bc7e6ca41af6616b2e3382a95c6ca0c82b01ba0c32ea6c5ca20178026281d05527d6bb0c55b744df551a7dca1bb836f69cb802080000000012f64c030003c58f0769da85da444823d8d6839b16253df8f09ffdddc13dfdb9a2b6fbc8628e83cbaf489cf66fb1c6b0c1b8999fba800cf9fce5750dbc167e359c9b0dee3fdc1492c83a04df176ebc184492194ebe64191d471f377132dcafb09724b8176b4a",
"block_header": {
"block_size": 95,
"depth": 858,
"difficulty": 85023515,
"hash": "77d6b92961146232ba36ced359f6dbbf3170299b530d2c7d0da5c398f7713d23",
"height": 17008,
"major_version": 6,
"minor_version": 6,
"nonce": 2617545983,
"num_txes": 3,
"orphan_status": true,
"prev_hash": "6360ac8eba51cf1215461f304635e01935345f7fb7fa0c1ce3263ac131b5cb35",
"reward": 30572570693546,
"timestamp": 1515517939
},
"json": "{\n \"major_version\": 6, \n \"minor_version\": 6, \n \"timestamp\": 1515517939, \n \"prev_id\": \"6360ac8eba51cf1215461f304635e01935345f7fb7fa0c1ce3263ac131b5cb35\", \n \"nonce\": 2617545983, \n \"miner_tx\": {\n \"version\": 2, \n \"unlock_time\": 17068, \n \"vin\": [ {\n \"gen\": {\n \"height\": 17008\n }\n }\n ], \n \"vout\": [ {\n \"amount\": 30572570693546, \n \"target\": {\n \"key\": \"a96b487a44e654d507ce0874bf5464b6bc7e6ca41af6616b2e3382a95c6ca0c8\"\n }\n }\n ], \n \"extra\": [ 1, 186, 12, 50, 234, 108, 92, 162, 1, 120, 2, 98, 129, 208, 85, 39, 214, 187, 12, 85, 183, 68, 223, 85, 26, 125, 202, 27, 184, 54, 246, 156, 184, 2, 8, 0, 0, 0, 0, 18, 246, 76, 3\n ], \n \"rct_signatures\": {\n \"type\": 0\n }\n }, \n \"tx_hashes\": [ \"c58f0769da85da444823d8d6839b16253df8f09ffdddc13dfdb9a2b6fbc8628e\", \"83cbaf489cf66fb1c6b0c1b8999fba800cf9fce5750dbc167e359c9b0dee3fdc\", \"1492c83a04df176ebc184492194ebe64191d471f377132dcafb09724b8176b4a\"\n ]\n}",
"status": "OK",
"tx_hashes": ["c58f0769da85da444823d8d6839b16253df8f09ffdddc13dfdb9a2b6fbc8628e","83cbaf489cf66fb1c6b0c1b8999fba800cf9fce5750dbc167e359c9b0dee3fdc","1492c83a04df176ebc184492194ebe64191d471f377132dcafb09724b8176b4a"]
}
}

+ 24
- 0
alt_chain_data/78581a7ea3a71082ead85cfde06ebb2a4d7bbfcfef9a0fe69e373e78b807fa7f_16997.txt

@ -0,0 +1,24 @@
{
"id": "0",
"jsonrpc": "2.0",
"result": {
"blob": "0606e1acd3d2051ae49ffd0caa6ffb6dc5617a06ae75e7d140303d69e06f4b6b0dcc4bc98383bc0fb1aa6002a1850101ffe5840101dd91c1f1edf30602ec337478b685134eff525f3cfbf18dd46de2824fa71a2b822d855376ec04b7f82b01f6f9bcac1f8bc3358c0279588a46433e1fe6a11e97eaaebfaee98dff85a621a102080000000001f682740000",
"block_header": {
"block_size": 95,
"depth": 867,
"difficulty": 85817995,
"hash": "78581a7ea3a71082ead85cfde06ebb2a4d7bbfcfef9a0fe69e373e78b807fa7f",
"height": 16997,
"major_version": 6,
"minor_version": 6,
"nonce": 1621799183,
"num_txes": 0,
"orphan_status": true,
"prev_hash": "1ae49ffd0caa6ffb6dc5617a06ae75e7d140303d69e06f4b6b0dcc4bc98383bc",
"reward": 30369146489053,
"timestamp": 1515509345
},
"json": "{\n \"major_version\": 6, \n \"minor_version\": 6, \n \"timestamp\": 1515509345, \n \"prev_id\": \"1ae49ffd0caa6ffb6dc5617a06ae75e7d140303d69e06f4b6b0dcc4bc98383bc\", \n \"nonce\": 1621799183, \n \"miner_tx\": {\n \"version\": 2, \n \"unlock_time\": 17057, \n \"vin\": [ {\n \"gen\": {\n \"height\": 16997\n }\n }\n ], \n \"vout\": [ {\n \"amount\": 30369146489053, \n \"target\": {\n \"key\": \"ec337478b685134eff525f3cfbf18dd46de2824fa71a2b822d855376ec04b7f8\"\n }\n }\n ], \n \"extra\": [ 1, 246, 249, 188, 172, 31, 139, 195, 53, 140, 2, 121, 88, 138, 70, 67, 62, 31, 230, 161, 30, 151, 234, 174, 191, 174, 233, 141, 255, 133, 166, 33, 161, 2, 8, 0, 0, 0, 0, 1, 246, 130, 116\n ], \n \"rct_signatures\": {\n \"type\": 0\n }\n }, \n \"tx_hashes\": [ ]\n}",
"status": "OK"
}
}

+ 25
- 0
alt_chain_data/8e7a86de29068834e3dd3a498c4717159cceec7b6f0753b65cbf889d564a6982_17003.txt

@ -0,0 +1,25 @@
{
"id": "0",
"jsonrpc": "2.0",
"result": {
"blob": "0606a0ddd3d205f87dd7136998a2e56a963ca1387a9738a98c7aa72a6f8a570f9c71397af383ed76080a1502a7850101ffeb840101c6c7edffe9f506021c41a23fd5a320a69fa4cb7d2ed436ccbab272e00a87a8e5e62773d2ba068af02b01f760a7b6c9ab5c7a93980930048314a0d030453bff3f17d480e2a47db4423d6202080000000002f682740001a3da5b87603312186d940ef790c3cd3d46ae5533a4118b40949d902d3a190583",
"block_header": {
"block_size": 95,
"depth": 863,
"difficulty": 89795673,
"hash": "8e7a86de29068834e3dd3a498c4717159cceec7b6f0753b65cbf889d564a6982",
"height": 17003,
"major_version": 6,
"minor_version": 6,
"nonce": 352979062,
"num_txes": 1,
"orphan_status": true,
"prev_hash": "f87dd7136998a2e56a963ca1387a9738a98c7aa72a6f8a570f9c71397af383ed",
"reward": 30436822311878,
"timestamp": 1515515552
},
"json": "{\n \"major_version\": 6, \n \"minor_version\": 6, \n \"timestamp\": 1515515552, \n \"prev_id\": \"f87dd7136998a2e56a963ca1387a9738a98c7aa72a6f8a570f9c71397af383ed\", \n \"nonce\": 352979062, \n \"miner_tx\": {\n \"version\": 2, \n \"unlock_time\": 17063, \n \"vin\": [ {\n \"gen\": {\n \"height\": 17003\n }\n }\n ], \n \"vout\": [ {\n \"amount\": 30436822311878, \n \"target\": {\n \"key\": \"1c41a23fd5a320a69fa4cb7d2ed436ccbab272e00a87a8e5e62773d2ba068af0\"\n }\n }\n ], \n \"extra\": [ 1, 247, 96, 167, 182, 201, 171, 92, 122, 147, 152, 9, 48, 4, 131, 20, 160, 208, 48, 69, 59, 255, 63, 23, 212, 128, 226, 164, 125, 180, 66, 61, 98, 2, 8, 0, 0, 0, 0, 2, 246, 130, 116\n ], \n \"rct_signatures\": {\n \"type\": 0\n }\n }, \n \"tx_hashes\": [ \"a3da5b87603312186d940ef790c3cd3d46ae5533a4118b40949d902d3a190583\"\n ]\n}",
"status": "OK",
"tx_hashes": ["a3da5b87603312186d940ef790c3cd3d46ae5533a4118b40949d902d3a190583"]
}
}

+ 24
- 0
alt_chain_data/9249ac65bef9acb1b4375a50d9857627ef9f78961fe8fdd6068621572a174bd6_17005.txt

@ -0,0 +1,24 @@
{
"id": "0",
"jsonrpc": "2.0",
"result": {
"blob": "0606c5e1d3d205038e2b878d3c204b8726a4159d8e2c113900a7180015ea30a48eda138ed043c7714e05ec02a9850101ffed840101d691d094ecf306020b9449d7678bc9eeaee5d73f2d56b87e07c7d44172c8b994e009bf2a4c4af6752b01fb004518d60291251a1a576677f9e2bcf5233079c7a0b2b42190b7cb8fb6ff0502080000000002472ac70000",
"block_header": {
"block_size": 95,
"depth": 861,
"difficulty": 99356865,
"hash": "9249ac65bef9acb1b4375a50d9857627ef9f78961fe8fdd6068621572a174bd6",
"height": 17005,
"major_version": 6,
"minor_version": 6,
"nonce": 3959770737,
"num_txes": 0,
"orphan_status": true,
"prev_hash": "038e2b878d3c204b8726a4159d8e2c113900a7180015ea30a48eda138ed043c7",
"reward": 30368683264214,
"timestamp": 1515516101
},
"json": "{\n \"major_version\": 6, \n \"minor_version\": 6, \n \"timestamp\": 1515516101, \n \"prev_id\": \"038e2b878d3c204b8726a4159d8e2c113900a7180015ea30a48eda138ed043c7\", \n \"nonce\": 3959770737, \n \"miner_tx\": {\n \"version\": 2, \n \"unlock_time\": 17065, \n \"vin\": [ {\n \"gen\": {\n \"height\": 17005\n }\n }\n ], \n \"vout\": [ {\n \"amount\": 30368683264214, \n \"target\": {\n \"key\": \"0b9449d7678bc9eeaee5d73f2d56b87e07c7d44172c8b994e009bf2a4c4af675\"\n }\n }\n ], \n \"extra\": [ 1, 251, 0, 69, 24, 214, 2, 145, 37, 26, 26, 87, 102, 119, 249, 226, 188, 245, 35, 48, 121, 199, 160, 178, 180, 33, 144, 183, 203, 143, 182, 255, 5, 2, 8, 0, 0, 0, 0, 2, 71, 42, 199\n ], \n \"rct_signatures\": {\n \"type\": 0\n }\n }, \n \"tx_hashes\": [ ]\n}",
"status": "OK"
}
}

+ 25
- 0
alt_chain_data/a39b5f88e39231ddebf80f8023724f9b7ee77dbb81e91f19b96933a8cf118313_17000.txt

@ -0,0 +1,25 @@
{
"id": "0",
"jsonrpc": "2.0",
"result": {
"blob": "0606a1cfd3d2050eb61f1b78d810d314769e5e6157bc22894127b5d8e5bc417e99091005a7b8588f81029602a4850101ffe8840101ebccbfbbe5f906023c944822f98c9f59fdef57a196bf1987d6f04f8e0721034f974ceb3e4d88bb2d2b019c25765d6603202013f4a89d8ab60e3149ef064aee4a8feab0f936115a716a5502080000000004f64c0300036c69e35c2acfc877af5d1eaa74c70606dbc0b61d84f7e9d497fffaa1af1b6d169027c337be0ddbe65cbe3e21586915d4f39627a96397a40e445be458ba739200ef54f28c61a031912cd49d1a8c8ef1ccba64e23854dbb3328a2551de2290cfc8",
"block_header": {
"block_size": 95,
"depth": 865,
"difficulty": 88182342,
"hash": "a39b5f88e39231ddebf80f8023724f9b7ee77dbb81e91f19b96933a8cf118313",
"height": 17000,
"major_version": 6,
"minor_version": 6,
"nonce": 2516746639,
"num_txes": 3,
"orphan_status": true,
"prev_hash": "0eb61f1b78d810d314769e5e6157bc22894127b5d8e5bc417e99091005a7b858",
"reward": 30573044164203,
"timestamp": 1515513761
},
"json": "{\n \"major_version\": 6, \n \"minor_version\": 6, \n \"timestamp\": 1515513761, \n \"prev_id\": \"0eb61f1b78d810d314769e5e6157bc22894127b5d8e5bc417e99091005a7b858\", \n \"nonce\": 2516746639, \n \"miner_tx\": {\n \"version\": 2, \n \"unlock_time\": 17060, \n \"vin\": [ {\n \"gen\": {\n \"height\": 17000\n }\n }\n ], \n \"vout\": [ {\n \"amount\": 30573044164203, \n \"target\": {\n \"key\": \"3c944822f98c9f59fdef57a196bf1987d6f04f8e0721034f974ceb3e4d88bb2d\"\n }\n }\n ], \n \"extra\": [ 1, 156, 37, 118, 93, 102, 3, 32, 32, 19, 244, 168, 157, 138, 182, 14, 49, 73, 239, 6, 74, 238, 74, 143, 234, 176, 249, 54, 17, 90, 113, 106, 85, 2, 8, 0, 0, 0, 0, 4, 246, 76, 3\n ], \n \"rct_signatures\": {\n \"type\": 0\n }\n }, \n \"tx_hashes\": [ \"6c69e35c2acfc877af5d1eaa74c70606dbc0b61d84f7e9d497fffaa1af1b6d16\", \"9027c337be0ddbe65cbe3e21586915d4f39627a96397a40e445be458ba739200\", \"ef54f28c61a031912cd49d1a8c8ef1ccba64e23854dbb3328a2551de2290cfc8\"\n ]\n}",
"status": "OK",
"tx_hashes": ["6c69e35c2acfc877af5d1eaa74c70606dbc0b61d84f7e9d497fffaa1af1b6d16","9027c337be0ddbe65cbe3e21586915d4f39627a96397a40e445be458ba739200","ef54f28c61a031912cd49d1a8c8ef1ccba64e23854dbb3328a2551de2290cfc8"]
}
}

+ 24
- 0
alt_chain_data/f63b50e5962bb7fec38a224f09935177a103c20bcd272a0df24c36559bdc2106_17001.txt

@ -0,0 +1,24 @@
{
"id": "0",
"jsonrpc": "2.0",
"result": {
"blob": "0606f5d7d3d205a39b5f88e39231ddebf80f8023724f9b7ee77dbb81e91f19b96933a8cf118313ac15007902a5850101ffe9840101f1dc8d83edf30602b41caa95b2a8f18f4a776d13b501d9c75690cc8eb08214a2f56ade005667b1c32b01b8ebd18f15c4d2028b057b25561c5802455cb0e8628036448d87f8aa917a866302080000000003472ac70000",
"block_header": {
"block_size": 311875,
"depth": 865,
"difficulty": 89283276,
"hash": "f63b50e5962bb7fec38a224f09935177a103c20bcd272a0df24c36559bdc2106",
"height": 17001,
"major_version": 6,
"minor_version": 6,
"nonce": 2030048684,
"num_txes": 0,
"orphan_status": true,
"prev_hash": "a39b5f88e39231ddebf80f8023724f9b7ee77dbb81e91f19b96933a8cf118313",
"reward": 30368914959985,
"timestamp": 1515514869
},
"json": "{\n \"major_version\": 6, \n \"minor_version\": 6, \n \"timestamp\": 1515514869, \n \"prev_id\": \"a39b5f88e39231ddebf80f8023724f9b7ee77dbb81e91f19b96933a8cf118313\", \n \"nonce\": 2030048684, \n \"miner_tx\": {\n \"version\": 2, \n \"unlock_time\": 17061, \n \"vin\": [ {\n \"gen\": {\n \"height\": 17001\n }\n }\n ], \n \"vout\": [ {\n \"amount\": 30368914959985, \n \"target\": {\n \"key\": \"b41caa95b2a8f18f4a776d13b501d9c75690cc8eb08214a2f56ade005667b1c3\"\n }\n }\n ], \n \"extra\": [ 1, 184, 235, 209, 143, 21, 196, 210, 2, 139, 5, 123, 37, 86, 28, 88, 2, 69, 92, 176, 232, 98, 128, 54, 68, 141, 135, 248, 170, 145, 122, 134, 99, 2, 8, 0, 0, 0, 0, 3, 71, 42, 199\n ], \n \"rct_signatures\": {\n \"type\": 0\n }\n }, \n \"tx_hashes\": [ ]\n}",
"status": "OK"
}
}

+ 24
- 0
alt_chain_data/f87dd7136998a2e56a963ca1387a9738a98c7aa72a6f8a570f9c71397af383ed_17002.txt

@ -0,0 +1,24 @@
{
"id": "0",
"jsonrpc": "2.0",
"result": {
"blob": "060684dbd3d205f63b50e5962bb7fec38a224f09935177a103c20bcd272a0df24c36559bdc21068e8e029502a6850101ffea840101e4a8bee7ecf30602613f808c5bbb29eb77f807aaf7a4f155a40b60c858a1eac744f110f182ff236c2b0179be02b5802845c93eb47dec53c476b3ab6c9d416a098ce0c94a0a108f87b6c102080000000003f64c030000",
"block_header": {
"block_size": 189336,
"depth": 864,
"difficulty": 86994653,
"hash": "f87dd7136998a2e56a963ca1387a9738a98c7aa72a6f8a570f9c71397af383ed",
"height": 17002,
"major_version": 6,
"minor_version": 6,
"nonce": 2499972750,
"num_txes": 0,
"orphan_status": true,
"prev_hash": "f63b50e5962bb7fec38a224f09935177a103c20bcd272a0df24c36559bdc2106",
"reward": 30368857035876,
"timestamp": 1515515268
},
"json": "{\n \"major_version\": 6, \n \"minor_version\": 6, \n \"timestamp\": 1515515268, \n \"prev_id\": \"f63b50e5962bb7fec38a224f09935177a103c20bcd272a0df24c36559bdc2106\", \n \"nonce\": 2499972750, \n \"miner_tx\": {\n \"version\": 2, \n \"unlock_time\": 17062, \n \"vin\": [ {\n \"gen\": {\n \"height\": 17002\n }\n }\n ], \n \"vout\": [ {\n \"amount\": 30368857035876, \n \"target\": {\n \"key\": \"613f808c5bbb29eb77f807aaf7a4f155a40b60c858a1eac744f110f182ff236c\"\n }\n }\n ], \n \"extra\": [ 1, 121, 190, 2, 181, 128, 40, 69, 201, 62, 180, 125, 236, 83, 196, 118, 179, 171, 108, 157, 65, 106, 9, 140, 224, 201, 74, 10, 16, 143, 135, 182, 193, 2, 8, 0, 0, 0, 0, 3, 246, 76, 3\n ], \n \"rct_signatures\": {\n \"type\": 0\n }\n }, \n \"tx_hashes\": [ ]\n}",
"status": "OK"
}
}

+ 89
- 0
blockchain/alt_blocks.go

@ -0,0 +1,89 @@
package blockchain
import "fmt"
import "encoding/hex"
// this file only contains an altchain, 13 long
// which can be used to test out consensus on live data in house
var block_16996 []byte
var block_16997 []byte
var block_16998 []byte
var block_16999 []byte
var block_17000 []byte
var block_17001 []byte
var block_17002 []byte
var block_17003 []byte
var block_17004 []byte
var block_17005 []byte
var block_17006 []byte
var block_17007 []byte
var block_17008 []byte
func init() {
block_16996, _ = hex.DecodeString("06069dacd3d20516417000452bb3c27c028024a1a21b2d8264a13967cc2b070563b7cb2ecc8236240215db02a0850101ffe484010192ca908deef30602b7840aca58a897165d0c4c90a6e1519d635808f5f32d0dfd8cec545751f9719a2b0185ab9544cf529130c7557fa1a7d3ebc0a25f832ca909bca1e67ca1fb38b472fe02080000000007472ac70000")
block_16997, _ = hex.DecodeString("0606e1acd3d2051ae49ffd0caa6ffb6dc5617a06ae75e7d140303d69e06f4b6b0dcc4bc98383bc0fb1aa6002a1850101ffe5840101dd91c1f1edf30602ec337478b685134eff525f3cfbf18dd46de2824fa71a2b822d855376ec04b7f82b01f6f9bcac1f8bc3358c0279588a46433e1fe6a11e97eaaebfaee98dff85a621a102080000000001f682740000")
block_16998, _ = hex.DecodeString("0606c8b2d3d20578581a7ea3a71082ead85cfde06ebb2a4d7bbfcfef9a0fe69e373e78b807fa7f9662013d02a2850101ffe6840101def3eaad9ba00702bb5bb6f749afdd7d64d8b3b4fa2b8a44c77bf637d0c13d3a994b1d269d8780f82b013eeb27eeff0bd10d40f5892504ec95d81399d150cefa504a0fbacfe6fdc8a563020800000000a0472ac70017abda0ac60520cf99e8b28612349ae639fcbdcc4b1ee256bd064660e96029ba63b0450e0e92ee7af62ca755e2f0522e68a4c11497c1179e09dad0a807729455abc27d43eefa50e1714eb0714e45742c7c6c4dd718b494d3290177dd18172a5acc03d87bf62b5c7d6980a78a238061894f2baf3e3929dc006abcd3622f80adc7dcd900d779234f61b29f6f98ba0956a08082607d6c95ead01c252a5086b413a9fd3a0dd65e7e2005395c69a0a676af4048a69e4191b5948d1cae9cb548ef8bc79dd8e247ed59b2b67e7ff1d7129de06038c388bc55581585b178c440eb9d8e60ab843859096066f80ca797d71db0b481d27f64791dc4cfc621ab462d455c94f7424ab37e5a8ca54eca36d5c0dc096cf85b3022f5c5461f704eea1b48b625189e13575dc5528ba9321dea609fa0574b36a4225c7c850f6b78ce68b2a6d6f001bf9874400f342178ef174768af48d2f3bab1cf647684315321fa09ee984b18667712a37f8767923e0f43524a27b4744a2a189ab42b0eee3505aad99afa25952a9debf3fb8f643488edc07d19bd4ce931e82405a22f3f65aa2500146eeafe7aa2785e9e25f5dc5283b684e29582242db611927cdb6a33636b916a6923f56afe2233bc6cd9e3fb4c999eda97345ce93f6af87d917a110c24b885e40b1bab0213a3dc13ea61c256f8cae39eda04a7d4f234a4b8a664834135bd01169b5ae91683a0b11e245b7e3ac9088f863f260c9697b420a80b9e1ec0713b230a2ae4b2edb9f8fad5616c670ff266a41b2006fdfbe835682a6f97486c2df19c3a1052202b74e0d65a59c2fada5e48344a33d0b1d97d1b7fd261a3f8226644c8ca8127fd3a4347ae5bd5b09a9bbbfcb1026da1a2ef4521247d53c0d71abcec48f9d6112f6dd43627aa882a53d4f7e97b27e52c57b66eefa55d2e855459fb46b2f273f564f390fa3f005281716c770f50afbbc1946e1516e6bddd2ce0ea826a4a6f433cadfeb962e4b97964737a3bada86e6ec67b36b94873402dbf72fd53f7f7f5c8804ddb82069f41")
block_16999, _ = hex.DecodeString("0606d0c9d3d20503ca43b2dbfefe2b1854c516a4d30a7feeb5386dfe1169c3caf33ec3ff898582ed0300d702a3850101ffe78401019fd4ef8592a007025a5d066ac8f5772be60aad59ac33813937efb7edd090006d83f7b625d56deeb72b012c28df62dfe6eda5c5d3113f8d1f3de463f99937e57369ce44b26580444dbf1b0208000000000cf68274001774149ceb2f51f2b8a71536e1e1ddd57a141cdb19d9dea33bf10b09fd336617b0b0f82f931a8147cdc44dbaa9d95425c9980dcd5ab9065892fbeed204902e339f31ca3214fdecac929eb7a4d09fcd799bbe12c5adda9740fa20e4a58178bf32fa014252ccdfb7f5e97838ec2e142eb7ba77115ad0ce34f875e8d0e7d15a2aaa4ddd07a95dd7b66f7b3354c455b712014acf488051700d91869abd0c386ebb40c8c958b296459577ac81d472c1b7c026cfbf355bf86c603fc98615b8f3ae17d17cb4f8edbbdf0bdaf1f269651ef74c1e653d84ff5c597031a008e58f68a04db944da76a0ee549dbadbf47bffa9d729a7747226496b7e213f0729639eebeb6f48108a82a1ca79591e9efd8c6c020be08b02e76d2ebf61aa3f21b84f90bf2a618b477bb01c4b62bd317b4cf92b846d81f8eee205689e4f084e189f3124e697877f67d857f478803f81c1f8b7b893f3f8af830833600d75c43e61733c648cc4a524b50e32232cf5e0e6d9b5c4e539e3d33578aa9c82b54e549e92b1e04d16f3f9dbd156163b6fca1ce4c1572482f2d7dc16634043a7b828488f0b8e5178ddedab49bba1b6646110f2437e83782f50dcffedc67ea4f59971681a3e0f552de091bebbd2c0be66fd022b64d3c61e450513bb4edd1123ef43b1da771bcd94141b29ca933db89499001acb38098e666a8c8d426692ab16547009db6f55a1badc84ea714fbe9c6d63cbfdba522c4d9bac212fdabce699691c646e50fe77e6b6110174d4ff592de728b309cd842af835b80b5966df29fa10d5341c7c3637922b7dfa7ec00937b17522a0136067fe7823bd1808ff8850556584f39b61cc774871a4ff3adedab05cad0e401308b0e9ecd231a4d50f85bc50babfd3701256c7dfaa5b86f37cbe7dc71ce59eb3fbfc0afc6a80b2a4450673f9a44772eee79df273d1bdd43faf13f813dc222c7c88d371504d721be75332b20e3278afefcdb1b4c5a36aee6ae4a5fd7aec7e4f79daab35affd5f8fa2edbd40b83e761b8d59a74cf87e77944244539b")
block_17000, _ = hex.DecodeString("0606a1cfd3d2050eb61f1b78d810d314769e5e6157bc22894127b5d8e5bc417e99091005a7b8588f81029602a4850101ffe8840101ebccbfbbe5f906023c944822f98c9f59fdef57a196bf1987d6f04f8e0721034f974ceb3e4d88bb2d2b019c25765d6603202013f4a89d8ab60e3149ef064aee4a8feab0f936115a716a5502080000000004f64c0300036c69e35c2acfc877af5d1eaa74c70606dbc0b61d84f7e9d497fffaa1af1b6d169027c337be0ddbe65cbe3e21586915d4f39627a96397a40e445be458ba739200ef54f28c61a031912cd49d1a8c8ef1ccba64e23854dbb3328a2551de2290cfc8")
block_17001, _ = hex.DecodeString("0606f5d7d3d205a39b5f88e39231ddebf80f8023724f9b7ee77dbb81e91f19b96933a8cf118313ac15007902a5850101ffe9840101f1dc8d83edf30602b41caa95b2a8f18f4a776d13b501d9c75690cc8eb08214a2f56ade005667b1c32b01b8ebd18f15c4d2028b057b25561c5802455cb0e8628036448d87f8aa917a866302080000000003472ac70000")
block_17002, _ = hex.DecodeString("060684dbd3d205f63b50e5962bb7fec38a224f09935177a103c20bcd272a0df24c36559bdc21068e8e029502a6850101ffea840101e4a8bee7ecf30602613f808c5bbb29eb77f807aaf7a4f155a40b60c858a1eac744f110f182ff236c2b0179be02b5802845c93eb47dec53c476b3ab6c9d416a098ce0c94a0a108f87b6c102080000000003f64c030000")
block_17003, _ = hex.DecodeString("0606a0ddd3d205f87dd7136998a2e56a963ca1387a9738a98c7aa72a6f8a570f9c71397af383ed76080a1502a7850101ffeb840101c6c7edffe9f506021c41a23fd5a320a69fa4cb7d2ed436ccbab272e00a87a8e5e62773d2ba068af02b01f760a7b6c9ab5c7a93980930048314a0d030453bff3f17d480e2a47db4423d6202080000000002f682740001a3da5b87603312186d940ef790c3cd3d46ae5533a4118b40949d902d3a190583")
block_17004, _ = hex.DecodeString("0606a8dfd3d2058e7a86de29068834e3dd3a498c4717159cceec7b6f0753b65cbf889d564a69823c7c008a02a8850101ffec84010197c39fb0ecf3060235527c1f4ad20b7003a4a714801d5e0fc5c44538256b2c80e4c98a75be7424ed2b01296ce3ef0900a4d99c574f918e11b1e72e536b8f3a69702bf307800b8852037a02080000000002f64c030000")
block_17005, _ = hex.DecodeString("0606c5e1d3d205038e2b878d3c204b8726a4159d8e2c113900a7180015ea30a48eda138ed043c7714e05ec02a9850101ffed840101d691d094ecf306020b9449d7678bc9eeaee5d73f2d56b87e07c7d44172c8b994e009bf2a4c4af6752b01fb004518d60291251a1a576677f9e2bcf5233079c7a0b2b42190b7cb8fb6ff0502080000000002472ac70000")
block_17006, _ = hex.DecodeString("0606d9e7d3d2059249ac65bef9acb1b4375a50d9857627ef9f78961fe8fdd6068621572a174bd61c1e006802aa850101ffee84010183e180f9ebf30602edc679784518be061a918062d7b11544948d091e5f06d389d849156100d47b5e2b01a9128d72db29bc746278350850ba4d12a904dc565a03265c599c30b5b34e921a02080000000004472ac70000")
block_17007, _ = hex.DecodeString("0606a9e9d3d2051d9687780f854ea310bf5d7ea5e756be167fa043d81a58c3a3e602e08e7ad9209929090602ab850101ffef8401019fb1b1ddebf30602c7b8a1da31ed781b3153245337e5c34aab53055ebc64a0de8a7c336af1a233ce2b016cbf39ec1ad5079ec981f8ce5b0e59b2e014199c0476496cf404a1323adb6d4d02080000000001bcd9f60000")
block_17008, _ = hex.DecodeString("0606f3efd3d2056360ac8eba51cf1215461f304635e01935345f7fb7fa0c1ce3263ac131b5cb35ff94049c02ac850101fff0840101aa9fddd9e3f90602a96b487a44e654d507ce0874bf5464b6bc7e6ca41af6616b2e3382a95c6ca0c82b01ba0c32ea6c5ca20178026281d05527d6bb0c55b744df551a7dca1bb836f69cb802080000000012f64c030003c58f0769da85da444823d8d6839b16253df8f09ffdddc13dfdb9a2b6fbc8628e83cbaf489cf66fb1c6b0c1b8999fba800cf9fce5750dbc167e359c9b0dee3fdc1492c83a04df176ebc184492194ebe64191d471f377132dcafb09724b8176b4a")
}
func (chain *Blockchain) Inject_Alt_Chain() {
if chain.Get_Height() < 16997 {
fmt.Printf("cannot inject alt-chain for testing purpose\n")
}
chain.inject_block(block_16996)
/**/ /* chain.inject_block(block_16997)
chain.inject_block(block_16998)
/**/ /* chain.inject_block(block_16999)
chain.inject_block(block_17000)
chain.inject_block(block_17001)
chain.inject_block(block_17002)
chain.inject_block(block_17003)
chain.inject_block(block_17004)
chain.inject_block(block_17005)
chain.inject_block(block_17006)
chain.inject_block(block_17007)
/* chain.inject_block(block_17008)
*/
}
func (chain *Blockchain) inject_block(x []byte) {
var bl Block
if len(x) < 20 {
fmt.Printf("block cannot be injected for sure\n")
return
}
err := bl.Deserialize(x)
if err != nil {
fmt.Printf("cannor deserialize hardcoded block\n")
return
}
chain.Chain_Add(&bl)
}

+ 313
- 0
blockchain/block.go

@ -0,0 +1,313 @@
package blockchain
import "fmt"
import "bytes"
import "encoding/hex"
import "encoding/binary"
import "github.com/romana/rlog"
import "github.com/deroproject/derosuite/crypto"
import "github.com/deroproject/derosuite/config"
import "github.com/deroproject/derosuite/cryptonight"
// these are defined in file
//https://github.com/monero-project/monero/src/cryptonote_basic/cryptonote_basic.h
type Block_Header struct {
Major_Version uint32 `json:"major_version"`
Minor_Version uint32 `json:"minor_version"`
Timestamp uint64 `json:"timestamp"`
Prev_Hash crypto.Hash `json:"prev_id"`
Nonce uint32 `json:"nonce"`
}
type Block struct {
Block_Header
Miner_tx Transaction `json:"miner_tx"`
Merkle_Root crypto.Hash `json:"-"`
Tx_hashes []crypto.Hash `json:"tx_hashes"`
treehash crypto.Hash
}
// see spec here https://cryptonote.org/cns/cns003.txt
// this function gets the block identifier hash
func (bl *Block) GetHash() (hash crypto.Hash) {
buf := make([]byte, binary.MaxVarintLen64)
long_header := bl.GetBlockWork()
length := uint64(len(long_header))
n := binary.PutUvarint(buf, length) //
buf = buf[:n]
block_id_blob := append(buf, long_header...)
// keccak hash of this above blob, gives the block id
hash2 := crypto.Keccak256(block_id_blob)
return crypto.Hash(hash2)
}
// converts a block, into a getwork style work, ready for either submitting the block
// or doing Pow Calculations
func (bl *Block) GetBlockWork() []byte {
buf := make([]byte, binary.MaxVarintLen64)
header := bl.SerializeHeader()
tx_treehash := bl.GetTreeHash() // treehash of all transactions
// length of all transactions
n := binary.PutUvarint(buf, uint64(len(bl.Tx_hashes)+1)) // +1 for miner TX
buf = buf[:n]
long_header := append(header, tx_treehash[:]...)
long_header = append(long_header, buf...)
return long_header
}
// Get PoW hash , this is very slow function
func (bl *Block) GetPoWHash() (hash crypto.Hash) {
long_header := bl.GetBlockWork()
rlog.Tracef(9, "longheader %x\n", long_header)
tmphash := cryptonight.SlowHash(long_header)
copy(hash[:], tmphash[:32])
return
}
// Reward is, total amount in the miner tx - fees
func (bl *Block) GetReward() uint64 {
total_amount := bl.Miner_tx.Vout[0].Amount
total_fees := uint64(0)
// load all the TX and get the fees, since we are in a post rct world
// extract the fees from the rct sig
return total_amount - total_fees
}
// serialize block header
func (bl *Block) SerializeHeader() []byte {
var serialised bytes.Buffer
buf := make([]byte, binary.MaxVarintLen64)
n := binary.PutUvarint(buf, uint64(bl.Major_Version))
serialised.Write(buf[:n])
n = binary.PutUvarint(buf, uint64(bl.Minor_Version))
serialised.Write(buf[:n])
n = binary.PutUvarint(buf, bl.Timestamp)
serialised.Write(buf[:n])
serialised.Write(bl.Prev_Hash[:32]) // write previous ID
binary.LittleEndian.PutUint32(buf[0:8], bl.Nonce) // check whether it needs to be big endian
serialised.Write(buf[:4])
return serialised.Bytes()
}
// serialize entire block ( block_header + miner_tx + tx_list )
func (bl *Block) Serialize() []byte {
var serialized bytes.Buffer
buf := make([]byte, binary.MaxVarintLen64)
header := bl.SerializeHeader()
serialized.Write(header)
// miner tx should always be coinbase
minex_tx := bl.Miner_tx.Serialize()
serialized.Write(minex_tx)
//fmt.Printf("serializing tx hashes %d\n", len(bl.Tx_hashes))
n := binary.PutUvarint(buf, uint64(len(bl.Tx_hashes)))
serialized.Write(buf[:n])
for _, hash := range bl.Tx_hashes {
serialized.Write(hash[:])
}
return serialized.Bytes()
}
// get block transactions tree hash
func (bl *Block) GetTreeHash() (hash crypto.Hash) {
var hash_list []crypto.Hash
hash_list = append(hash_list, bl.Miner_tx.GetHash())
// add all the remaining hashes
for i := range bl.Tx_hashes {
hash_list = append(hash_list, bl.Tx_hashes[i])
}
return TreeHash(hash_list)
}
// input is the list of transactions hashes
func TreeHash(hashes []crypto.Hash) (hash crypto.Hash) {
switch len(hashes) {
case 0:
panic("Treehash cannot have 0 transactions, atleast miner tx will be present")
case 1:
copy(hash[:], hashes[0][:32])
case 2:
var buf []byte
for i := 0; i < len(hashes); i++ {
buf = append(buf, hashes[i][:32]...)
}
tmp_hash := crypto.Keccak256(buf)
copy(hash[:], tmp_hash[:32])
default:
count := uint64(len(hashes))
cnt := tree_hash_cnt(count)
//fmt.Printf("cnt %d count %d\n",cnt, count)
ints := make([]byte, 32*cnt, 32*cnt)
hashes_buf := make([]byte, 32*count, 32*count)
for i := uint64(0); i < count; i++ {
copy(hashes_buf[i*32:], hashes[i][:32]) // copy hashes 1 by 1
}
for i := uint64(0); i < ((2 * cnt) - count); i++ {
copy(ints[i*32:], hashes[i][:32]) // copy hashes 1 by 1
}
i := ((2 * cnt) - count)
j := ((2 * cnt) - count)
for ; j < cnt; i, j = i+2, j+1 {
hash := crypto.Keccak256(hashes_buf[i*32 : (i*32)+64]) // find hash of 64 bytes
copy(ints[j*32:], hash[:32])
}
if i != count {
panic("please fix tree hash")
}
for cnt > 2 {
cnt = cnt >> 1
i = 0
j = 0
for ; j < cnt; i, j = i+2, j+1 {
hash := crypto.Keccak256(ints[i*32 : (i*32)+64]) // find hash of 64 bytes
copy(ints[j*32:], hash[:32])
}
}
hash = crypto.Hash(crypto.Keccak256(ints[0:64])) // find hash of 64 bytes
}
return
}
// see crypto/tree-hash.c
// this function has a naughty history
func tree_hash_cnt(count uint64) uint64 {
pow := uint64(2)
for pow < count {
pow = pow << 1
}
return pow >> 1
}
func (bl *Block) Deserialize(buf []byte) (err error) {
done := 0
var tmp uint64
defer func() {
if r := recover(); r != nil {
logger.Warnf("Panic while deserialising block, block hex_dump below to make a testcase/debug\n")
logger.Warnf("%s", hex.EncodeToString(buf))
err = fmt.Errorf("Invalid Block")
return
}
}()
tmp, done = binary.Uvarint(buf)
if done <= 0 {
return fmt.Errorf("Invalid Version in Block\n")
}
buf = buf[done:]
bl.Major_Version = uint32(tmp)
if uint64(bl.Major_Version) != tmp {
return fmt.Errorf("Invalid Block major version")
}
tmp, done = binary.Uvarint(buf)
if done <= 0 {
return fmt.Errorf("Invalid minor Version in Block\n")
}
buf = buf[done:]
bl.Minor_Version = uint32(tmp)
if uint64(bl.Minor_Version) != tmp {
return fmt.Errorf("Invalid Block minor version")
}
bl.Timestamp, done = binary.Uvarint(buf)
if done <= 0 {
return fmt.Errorf("Invalid Timestamp in Block\n")
}
buf = buf[done:]
copy(bl.Prev_Hash[:], buf[:32]) // hash is always 32 byte
buf = buf[32:]
bl.Nonce = binary.LittleEndian.Uint32(buf)
buf = buf[4:] // nonce is always 4 bytes
// read and parse transaction
err = bl.Miner_tx.DeserializeHeader(buf)
if err != nil {
return fmt.Errorf("Cannot parse miner TX %x", buf)
}
// if tx was parse, make sure it's coin base
if len(bl.Miner_tx.Vin) != 1 || bl.Miner_tx.Vin[0].(Txin_gen).Height > config.MAX_CHAIN_HEIGHT {
// serialize transaction again to get the tx size, so as parsing could continue
return fmt.Errorf("Invalid Miner TX")
}
miner_tx_serialized_size := bl.Miner_tx.Serialize()
buf = buf[len(miner_tx_serialized_size):]
//fmt.Printf("miner tx %x\n", miner_tx_serialized_size)
// read number of transactions
tx_count, done := binary.Uvarint(buf)
if done <= 0 {
return fmt.Errorf("Invalid Tx count in Block\n")
}
buf = buf[done:]
// remember first tx is merkle root
for i := uint64(0); i < tx_count; i++ {
//fmt.Printf("Parsing transaction hash %d tx_count %d\n", i, tx_count)
var h crypto.Hash
copy(h[:], buf[:32])
buf = buf[32:]
bl.Tx_hashes = append(bl.Tx_hashes, h)
}
//fmt.Printf("%d member in tx hashes \n",len(bl.Tx_hashes))
return
}

+ 42
- 0
blockchain/block_test.go

@ -0,0 +1,42 @@
package blockchain
import "bytes"
import "testing"
import "encoding/hex"
import "github.com/deroproject/derosuite/config"
import "github.com/deroproject/derosuite/crypto"
func Test_Genesis_block_serdes(t *testing.T) {
mainnet_genesis_block_hex := "010000000000000000000000000000000000000000000000000000000000000000000010270000023c01ff0001ffffffffffff07020bf6522f9152fa26cd1fc5c022b1a9e13dab697f3acf4b4d0ca6950a867a194321011d92826d0656958865a035264725799f39f6988faa97d532f972895de849496d0000"
mainnet_genesis_block, _ := hex.DecodeString(mainnet_genesis_block_hex)
var bl Block
err := bl.Deserialize(mainnet_genesis_block)
if err != nil {
t.Errorf("Deserialization test failed for NULL block err %s\n", err)
}
// test the block serializer and deserializer whether it gives the same
serialized := bl.Serialize()
if !bytes.Equal(serialized, mainnet_genesis_block) {
t.Errorf("Serialization test failed for Genesis block %X\n", serialized)
}
// calculate POW hash
powhash := bl.GetPoWHash()
if powhash != crypto.Hash([32]byte{0xa7, 0x3b, 0xd3, 0x7a, 0xba, 0x34, 0x54, 0x77, 0x6b, 0x40, 0x73, 0x38, 0x54, 0xa8, 0x34, 0x9f, 0xe6, 0x35, 0x9e, 0xb2, 0xc9, 0x1d, 0x93, 0xbc, 0x72, 0x7c, 0x69, 0x43, 0x1c, 0x1d, 0x1f, 0x95}) {
t.Errorf("genesis block POW failed %x\n", powhash[:])
}
// test block id
if bl.GetHash() != config.Mainnet.Genesis_Block_Hash {
t.Error("genesis block ID failed \n")
}
}

+ 593
- 0
blockchain/blockchain.go

@ -0,0 +1,593 @@
package blockchain
import "os"
import "fmt"
import "sort"
import "sync"
import "bytes"
import "sync/atomic"
import log "github.com/sirupsen/logrus"
import "github.com/romana/rlog"
import "github.com/deroproject/derosuite/config"
import "github.com/deroproject/derosuite/crypto"
import "github.com/deroproject/derosuite/globals"
import "github.com/deroproject/derosuite/storage"
import "github.com/deroproject/derosuite/difficulty"
import "github.com/deroproject/derosuite/crypto/ringct"
// all components requiring access to blockchain must use , this struct to communicate
// this structure must be update while mutex
type Blockchain struct {
store storage.Store // interface to storage layer
Height uint64 // chain height is always 1 more than block
height_seen uint64 // height seen on peers
Top_ID crypto.Hash // id of the top block
Difficulty uint64 // current cumulative difficulty
Mempool *Mempool
sync.RWMutex
}
var logger *log.Entry
// All blockchain activity is store in a single
/* do initialisation , setup storage, put genesis block and chain in store
This is the first component to get up
Global parameters are picked up from the config package
*/
func Blockchain_Start(params map[string]interface{}) (*Blockchain, error) {
var err error
var chain Blockchain
logger = globals.Logger.WithFields(log.Fields{"com": "BLKCHAIN"})
logger.Infof("Initialising blockchain")
init_checkpoints() // init some hard coded checkpoints
chain.store = storage.Bolt_backend // setup backend
chain.store.Init(params) // init backend
// genesis block not in chain, add it to chain, together with its miner tx
// make sure genesis is in the store
if !chain.Block_Exists(globals.Config.Genesis_Block_Hash) {
logger.Debugf("Genesis block not in store, add it now")
bl := Generate_Genesis_Block()
chain.Store_BL(&bl)
chain.Store_TOP_ID(globals.Config.Genesis_Block_Hash)
// store height mapping, genesis block is at id
chain.Store_BL_ID_at_Height(0, globals.Config.Genesis_Block_Hash)
}
// load the chain from the disk
chain.Initialise_Chain_From_DB()
// init mempool
chain.Mempool, err = Init_Mempool(params)
_ = err
atomic.AddUint32(&globals.Subsystem_Active, 1) // increment subsystem
// chain.Inject_Alt_Chain()
return &chain, nil
}
// this function is called to read blockchain state from DB
// It is callable at any point in time
func (chain *Blockchain) Initialise_Chain_From_DB() {
chain.Lock()
defer chain.Unlock()
// locate top block
chain.Top_ID = chain.Load_TOP_ID()
chain.Height = (chain.Load_Height_for_BL_ID(chain.Top_ID) + 1)
logger.Infof("Chain Top Block %x Height %d", chain.Top_ID, chain.Height)
}
// before shutdown , make sure p2p is confirmed stopped
func (chain *Blockchain) Shutdown() {
chain.Mempool.Shutdown() // shutdown mempool first
chain.Lock() // take the lock as chain is no longer in unsafe mode
logger.Infof("Stopping Blockchain")
chain.store.Shutdown()
atomic.AddUint32(&globals.Subsystem_Active, ^uint32(0)) // this decrement 1 fom subsystem
}
func (chain *Blockchain) Get_Height() uint64 {
return chain.Height
}
func (chain *Blockchain) Get_Top_ID() crypto.Hash {
return chain.Top_ID
}
func (chain *Blockchain) Get_Difficulty() uint64 {
return chain.Get_Difficulty_At_Block(chain.Top_ID)
}
func (chain *Blockchain) Get_Network_HashRate() uint64 {
return chain.Get_Difficulty_At_Block(chain.Top_ID) / config.BLOCK_TIME
}
// confirm whether the block exist in the data
// this only confirms whether the block has been downloaded
// a separate check is required, whether the block is valid ( satifies PoW and other conditions)
// we will not add a block to store, until it satisfies PoW
func (chain *Blockchain) Block_Exists(h crypto.Hash) bool {
_, err := chain.Load_BL_FROM_ID(h)
if err == nil {
return true
}
return false
}
/* this function will extend the chain and increase the height,
this function trigger checks for the block and transactions for validity, recursively
this is the only function which change chain height and top id
*/
func (chain *Blockchain) Chain_Add(bl *Block) (result bool) {
chain.Lock()
defer chain.Unlock()
result = false
block_hash := bl.GetHash()
// make sure that the block refers to some where in the chain
// and also make sure block is not the genesis block
if block_hash == globals.Config.Genesis_Block_Hash {
logger.Debugf("Genesis block already in chain skipping it")
return
}
// check if block already exist skip it
if chain.Block_Exists(block_hash) {
logger.Debugf("block already in chain skipping it %x", block_hash)
return
}
if chain.Height > 16996 {
// os.Exit(0)
}
if chain.Height > 17000 {
//os.Exit(0)
}
if chain.Height > 17010 {
// os.Exit(0)
}
if chain.Height > 16996000 {
os.Exit(0)
}
// make sure prev_hash refers to some point in our our chain
// there is an edge case, where we know child but still donot know parent
// this might be some some corrupted miner or initial sync
if !chain.Block_Exists(bl.Prev_Hash) {
// TODO we must queue this block for say 60 minutes, if parents donot appear it, discard it
logger.Warnf("Prev_Hash no where in the chain, skipping it till we get a parent %x", block_hash)
return
}
PoW := bl.GetPoWHash()
current_difficulty := chain.Get_Difficulty_At_Block(bl.Prev_Hash)
logger.Debugf("Difficulty at height %d is %d", chain.Load_Height_for_BL_ID(bl.Prev_Hash), current_difficulty)
// check if the PoW is satisfied
if !difficulty.CheckPowHash(PoW, current_difficulty) { // if invalid Pow, reject the bloc
logger.Warnf("Block %x has invalid PoW, ignoring it", block_hash)
return false
}
// check we need to extend the chain or do a soft fork
if bl.Prev_Hash == chain.Top_ID {
// we need to extend the chain
//log.Debugf("Extendin chain using block %x", block_hash )
chain.Store_BL(bl)
chain.Store_TOP_ID(block_hash) // make new block top block
//chain.Add_Child(bl.Prev_Hash, block_hash) // add the new block as chil
chain.Store_Block_Child(bl.Prev_Hash, block_hash)
chain.Store_BL_ID_at_Height(chain.Height, block_hash)
// lower the window, where top_id and chain height are different
chain.Height = chain.Height + 1 // increment height
chain.Top_ID = block_hash // set new top block id
logger.Debugf("Chain extended using block %x new height %d", block_hash[:], chain.Height)
// every 10 block print a line
if chain.Height%20 == 0 {
logger.Infof("Chain Height %d using block %x", chain.Height, block_hash[:])
}
} else { // a soft fork is in progress
logger.Debugf("Soft Fork is in progress block due to %x", block_hash)
chain.Chain_Add_And_Reorganise(bl)
}
// fmt.Printf("We should add the block to DB, reorganise if required\n")
return false
}
/* the block we have is NOT at the top, it either belongs to an altchain or is an alternative */
func (chain *Blockchain) Chain_Add_And_Reorganise(bl *Block) (result bool) {
block_hash := bl.GetHash()
// check whether the parent already has a child
parent_has_child := chain.Does_Block_Have_Child(bl.Prev_Hash)
// first lets add ourselves to the chain
chain.Store_BL(bl)
if !parent_has_child {
chain.Store_Block_Child(bl.Prev_Hash, block_hash)
logger.Infof("Adding alternative block %x to alt chain top\n", block_hash)
} else {
logger.Infof("Adding alternative block %x\n", block_hash)
// load existing children, there can be more than 1 in extremely rare case or unknown attacks
children_list := chain.Load_Block_Children(bl.Prev_Hash)
children_list = append(children_list, block_hash) // add ourselves to children list
// store children excluding main child of prev block
chain.Store_Block_Children(bl.Prev_Hash, children_list, chain.Load_Block_Child(bl.Prev_Hash))
}
// now we must trigger the recursive reorganise process from the parent block,
// the recursion should always end at the genesis block
// adding a block can cause chain reorganisation 1 time in 99.99% cases
// but we are prepared for the case, which might occur due to alt-alt-chains
chain.reorganise(block_hash)
return true
}
type chain_data struct {
hash crypto.Hash
cdifficulty uint64
foundat uint64 // when block was found
}
// NOTE: below algorithm is the core and and is used to network consensus
// the best chain found using the following algorithm
// cryptonote protocol algo is below
// compare cdiff, chain with higher diff wins, if diff is same, no reorg, this cause frequent splits
// new algo is this
// compare cdiff, chain with higher diff wins, if diff is same, go below
// compare time stamp, block with lower timestamp wins (since it has probable spread more than other blocks)
// if timestamps are same, block with lower block has (No PoW involved) wins
// block hash cannot be same
type bestChain []chain_data
func (s bestChain) Len() int { return len(s) }
func (s bestChain) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s bestChain) Less(i, j int) bool {
if s[i].cdifficulty > s[j].cdifficulty {
return true
}
if s[i].cdifficulty < s[j].cdifficulty {
return false
}
// we are here of if difficulty are same
if s[i].foundat < s[j].foundat { // check if timestamps are diff
return true
}
if s[i].foundat > s[j].foundat { // check if timestamps are diff
return false
}
if bytes.Compare(s[i].hash[:], s[j].hash[:]) < 0 {
return true
}
return false
}
// this function will recursive reorganise the chain, till the genesis block if required
// we are doing it this way as we can do away with too much book keeping
// this is processor and IO intensive in normal cases
func (chain *Blockchain) reorganise(block_hash crypto.Hash) {
var children_data bestChain
if block_hash == globals.Config.Genesis_Block_Hash {
logger.Infof("Reorganisation completed successfully, we reached genesis block")
return
}
// check if the block mentioned has more than 1 child
block_hash, found := chain.find_parent_with_children(block_hash)
if found {
// reorganise chain at this block
children := chain.Load_Block_Children(block_hash)
if len(children) < 2 {
panic(fmt.Sprintf("Children disappeared for block %x", block_hash))
}
main_chain := chain.Load_Block_Child(block_hash)
_ = main_chain
// choose the best chain and make it parent
for i := range children {
top_hash := chain.Get_Top_Block(children[i])
top_cdiff := chain.Load_Block_Cumulative_Difficulty(top_hash)
timestamp := chain.Load_Block_Timestamp(children[i])
children_data = append(children_data, chain_data{hash: children[i], cdifficulty: top_cdiff, foundat: timestamp})
}
sort.Sort(children_data)
logger.Infof("Choosing best chain\n")
for i := range children {
fmt.Printf("%d %+v\n", i, children_data[i])
}
best_chain := children_data[0].hash
if main_chain == best_chain {
logger.Infof("Main chain is already best, nothing to do")
return
} else {
logger.Infof("Making alt chain -> main chain and vice-versa")
// first lets fix up the connection
chain.Store_Block_Child(block_hash, best_chain) // store main connection
chain.Store_Block_Children(block_hash, children, best_chain) // store remaining child
// setup new height
new_height := chain.Load_Height_for_BL_ID(chain.Get_Top_Block(best_chain)) + 1
// invalidate all transactionw contained within old main chain
// validate all transactions in new main chain
logger.Debugf("Invalidating all transactions with old main chain")
logger.Debugf("Validating all transactions with old alt chain")
logger.Infof("Reorganise old height %d, new height %d", chain.Get_Height(), new_height)
chain.Top_ID = chain.Get_Top_Block(best_chain)
chain.Height = new_height
chain.Store_TOP_ID(chain.Top_ID) // make new block top block
logger.Infof("Reorganise success")
}
// TODO if we need to support alt-alt chains, uncomment the code below
//chain.reorganise(chain.Load_Block_Parent_ID(block_hash))
}
}
/*
func (chain *Blockchain)find_best_chain(list []crypto.Hash) best_child crypto.Hash {
if len(list) < 2 {
panic("Cannot find best child, when child_count = 1")
}
}
*/
// find a block with 2 or more child,
// returns false, if we reach genesis block
func (chain *Blockchain) find_parent_with_children(block_hash crypto.Hash) (hash crypto.Hash, found bool) {
// TODO we can also stop on the heighest checkpointed state, to save computing resources and time
if block_hash == globals.Config.Genesis_Block_Hash {
return hash, false // we do not have parent of genesis block
}
for {
// load children
children := chain.Load_Block_Children(block_hash)
if len(children) >= 2 {
return block_hash, true
}
block_hash = chain.Load_Block_Parent_ID(block_hash)
if block_hash == globals.Config.Genesis_Block_Hash {
return hash, false // we do not have parent of genesis block
}
}
}
// make sure the block is valid before we even attempt to add it
func (chain *Blockchain) Is_Block_Valid(height uint64, bl *Block) bool {
return true
}
/*
// Parent's list is appended to add child
func (chain *Blockchain) Add_Child( Parent_Hash, Child_Hash crypto.Hash ){
fmt.Printf("caller %s\n", CallerName())
// child list is only appended and never truncated
// fetch old list
children_list := chain.Load_Chain(Parent_Hash)
if len(children_list) % 32 != 0 {
log.Fatalf("Database corruption has occurred at this hash %x", Parent_Hash[:])
}
if len(children_list) == 0 {
chain.Store_Chain(Parent_Hash, Child_Hash[:])
log.Debugf("%x is a child of %x", Child_Hash, Parent_Hash)
}else{ // we need to sort the children based on Pow
panic("Chain need reorganisation, Sorting on PoW NOT implemented")
}
return
}
*/
/* add a transaction to chain,, we are currently not verifying the TX,
its a BUG and disaster, implement it ASAP
*/
func (chain *Blockchain) Add_TX(tx *Transaction) {
chain.Store_TX(tx)
}
/* this will only give you access to transactions which have been mined
*/
func (chain *Blockchain) Get_TX(hash crypto.Hash) (*Transaction, error) {
tx, err := chain.Load_TX_FROM_ID(hash)
return tx, err
}
// get difficulty at specific height but height must be <= than current block chain height
func (chain *Blockchain) Get_Difficulty_At_Height(Height uint64) uint64 {
if Height > chain.Get_Height() {
logger.Warnf("Difficulty Requested for invalid Height Chain Height %d requested Height %d", chain.Get_Height(), Height)
panic("Difficulty Requested for invalid Height")
}
// get block id at that height
block_id, err := chain.Load_BL_ID_at_Height(Height)
if err != nil {
logger.Warnf("No Block at Height %d , chain height %d\n", Height, chain.Get_Height())
panic("No Block at Height")
}
// we have a block id, now Lets get the difficulty
return chain.Get_Difficulty_At_Block(block_id)
}
// get difficulty at specific block_id, only condition is block must exist and must be connected
func (chain *Blockchain) Get_Difficulty_At_Block(block_id crypto.Hash) uint64 {
var cumulative_difficulties []uint64
var timestamps []uint64
var zero_block crypto.Hash
current_block_id := block_id
// traverse chain from the block referenced, to max 30 blocks ot till genesis block is researched
for i := 0; i < config.DIFFICULTY_BLOCKS_COUNT_V2; i++ {
if current_block_id == globals.Config.Genesis_Block_Hash || current_block_id == zero_block {
rlog.Tracef(2, "Reached genesis block for difficulty calculation %x", block_id[:])
break // break we have reached genesis block
}
// read timestamp of block and cumulative difficulty at that block
timestamp := chain.Load_Block_Timestamp(current_block_id)
cdifficulty := chain.Load_Block_Cumulative_Difficulty(current_block_id)
timestamps = append([]uint64{timestamp}, timestamps...) // prepend timestamp
cumulative_difficulties = append([]uint64{cdifficulty}, cumulative_difficulties...) // prepend timestamp
current_block_id = chain.Load_Block_Parent_ID(current_block_id)
}
return difficulty.Next_Difficulty(timestamps, cumulative_difficulties, config.BLOCK_TIME)
}
// this function return the current top block, if we start at specific block
// this works for any blocks which were added
func (chain *Blockchain) Get_Top_Block(block_id crypto.Hash) crypto.Hash {
for {
// check if the block has child, if not , we are the top
if !chain.Does_Block_Have_Child(block_id) {
return block_id
}
block_id = chain.Load_Block_Child(block_id) // continue searching the new top
}
panic("We can never reach this point")
return block_id // we will never reach here
}
// verifies whether we are lagging
// return true if we need resync
// returns false if we are good
func (chain *Blockchain) IsLagging(peer_cdifficulty, peer_height uint64, peer_top_id crypto.Hash) bool {
top_id := chain.Get_Top_ID()
cdifficulty := chain.Load_Block_Cumulative_Difficulty(top_id)
height := chain.Load_Height_for_BL_ID(top_id) + 1
rlog.Tracef(3, "P_cdiff %d cdiff %d , P_BH %d BH %d, p_top %x top %x",
peer_cdifficulty, cdifficulty,
peer_height, height,
peer_top_id, top_id)
if peer_cdifficulty > cdifficulty{
return true // peer's cumulative difficulty is more than ours , active resync
}
if peer_cdifficulty == cdifficulty && peer_top_id != top_id {
return true // cumulative difficulty is same but tops are different , active resync
}
return false
}
// This function will expand a transaction with all the missing info being reconstitued from the blockchain
// this also increases security since data is coming from the chain or being calculated
// basically this places data for ring signature verification
// REMEMBER to expand key images from the blockchain
func (chain *Blockchain) Expand_Transaction_v2 (tx *Transaction){
if tx.Version != 2 {
panic("TX not version 2")
}
//if rctsignature is null
// fill up the message first
tx.RctSignature.Message = ringct.Key(tx.GetPrefixHash())
// fill up the key images
for i := 0; i < len(tx.Vin);i++{
tx.RctSignature.MlsagSigs[i].II[0]= ringct.Key(tx.Vin[i].(Txin_to_key).K_image)
}
// now we need to fill up the mixring ctkey
}
// this function count all the vouts of the block,
// this function exists here because only the chain knws the tx
//
func (chain *Blockchain) Block_Count_Vout(block_hash crypto.Hash) (count uint64){
count = 1 // miner tx is always present
bl, err := chain.Load_BL_FROM_ID(block_hash)
if err != nil {
panic(fmt.Errorf("Cannot load block for %x err %s", block_hash, err))
}
for i := 0 ; i < len(bl.Tx_hashes);i++{ // load all tx one by one
tx, err := chain.Load_TX_FROM_ID(bl.Tx_hashes[i])
if err != nil{
panic(fmt.Errorf("Cannot load tx for %x err %s", bl.Tx_hashes[i], err))
}
// tx has been loaded, now lets get the vout
vout_count := uint64(len(tx.Vout))
count += vout_count
}
return count
}

+ 17
- 0
blockchain/caller.go

@ -0,0 +1,17 @@
package blockchain
// this package identifies the 2nd level caller of this function
// this is done to ensure checks regarding locking etc and to be sure no spuros calls are possible
import "runtime"
func CallerName() string {
pc, _, _, ok := runtime.Caller(1)
details := runtime.FuncForPC(pc)
if ok && details != nil {
//fmt.Printf("called from %s\n", details.Name()) // we should only give last parse after .
return details.Name()
}
return ""
}

+ 49
- 0
blockchain/checkpoints_static.go

@ -0,0 +1,49 @@
package blockchain
import "fmt"
import "encoding/hex"
import log "github.com/sirupsen/logrus"
import "github.com/deroproject/derosuite/crypto"
import "github.com/deroproject/derosuite/globals"
var mainnet_static_checkpoints = map[crypto.Hash]uint64{}
var testnet_static_checkpoints = map[crypto.Hash]uint64{}
// initialize the checkpoints
func init_checkpoints() {
switch globals.Config.Name {
case "mainnet":
ADD_CHECKPOINT(mainnet_static_checkpoints, 1, "cea3eb82889a1d063aa6205011a27a108e727f2ac66721a46bec8b7ee9e83d7e")
ADD_CHECKPOINT(mainnet_static_checkpoints, 10, "a8feb533ad2ad021f356b964957f4880445f89d6c6658a9407d63ce5144fe8ea")
ADD_CHECKPOINT(mainnet_static_checkpoints, 100, "462ef1347bd00511ad6e7be463cba4d44a69dbf8b7d1d478ff1fd68507dfc9e2")
log.Debugf("Added %d static checkpoints to mainnnet\n", len(mainnet_static_checkpoints))
case "testnet":
default:
panic(fmt.Sprintf("Unknown Network \"%s\"", globals.Config.Name))
}
}
// add a checkpoint to specific network
func ADD_CHECKPOINT(x map[crypto.Hash]uint64, Height uint64, hash_hex string) {
var hash crypto.Hash
hash_raw, err := hex.DecodeString(hash_hex)
if err != nil {
panic(fmt.Sprintf("Cannot hex decode checkpint hash \"%s\"", hash_hex))
}
if len(hash_raw) != 32 {
panic(fmt.Sprintf(" hash not 32 byte size Cannot hex decode checkpint hash \"%s\"", hash_hex))
}
copy(hash[:], hash_raw)
x[hash] = Height
}

+ 50
- 0
blockchain/genesis.go

@ -0,0 +1,50 @@
package blockchain
import "encoding/hex"
import "github.com/romana/rlog"
import "github.com/deroproject/derosuite/address"
import "github.com/deroproject/derosuite/globals"
func Create_Miner_Transaction(height uint64, median_size uint64, already_generated_coins uint64,
current_block_size uint64, fee uint64,
miner_address address.Address, nonce []byte,
max_outs uint64, hard_fork uint64) (tx *Transaction, err error) {
return nil, nil
}
// genesis transaction hash 5a18d9489bcd353aeaf4a19323d04e90353f98f0d7cc2a030cfd76e19495547d
// genesis amount 35184372088831
func Generate_Genesis_Block() (bl Block) {
genesis_tx_blob, err := hex.DecodeString(globals.Config.Genesis_Tx)
if err != nil {
panic("Failed to hex decode genesis Tx")
}
err = bl.Miner_tx.DeserializeHeader(genesis_tx_blob)
if err != nil {
panic("Failed to parse genesis tx ")
}
rlog.Tracef(2, "Hash of Genesis Tx %x\n", bl.Miner_tx.GetHash())
// verify whether tx is coinbase and valid
// setup genesis block header
bl.Major_Version = 1
bl.Minor_Version = 0
bl.Timestamp = 0 // first block timestamp
//bl.Prev_hash is automatic zero
bl.Nonce = globals.Config.Genesis_Nonce
rlog.Tracef(2, "Hash of genesis block is %x", bl.GetHash())
rlog.Tracef(2, "Genesis Block PoW %x\n", bl.GetPoWHash())
return
}

+ 24
- 0
blockchain/genesis_test.go

@ -0,0 +1,24 @@
package blockchain
import "testing"
func Test_Genesis_block(t *testing.T) {
// Generate_Genesis_Block()
/*var bl Block
serialized := bl.Serialize()
err := bl.Deserialize(serialized)
if err != nil {
t.Error("Deserialization test failed for NULL block\n")
}
serialized2 := bl.Serialize()
if bytes.Equal(serialized, serialized2) {
t.Error("Deserialization test failed for NULL block\n")
return
}*/
}

+ 97
- 0
blockchain/mempool.go

@ -0,0 +1,97 @@
package blockchain
import "sync"
import "time"
import "sync/atomic"
import log "github.com/sirupsen/logrus"
import "github.com/deroproject/derosuite/globals"
import "github.com/deroproject/derosuite/crypto"
// at this point in time, this is an ultrafast written mempool,
// it will not scale for more than 10000 transactions but is good enough for now
// we can always come back and rewrite it
type Mempool struct {
txs map[crypto.Hash]mempool_object
// global variable , but don't see it utilisation here except fot tx verification
chain *Blockchain
sync.Mutex
}
type mempool_object struct {
Tx *Transaction
Added uint64
Reason int // why is the tx in the mempool
}
var loggerpool *log.Entry
func Init_Mempool(params map[string]interface{}) (*Mempool, error) {
var mempool Mempool
//mempool.chain = params["chain"].(*Blockchain)
loggerpool = globals.Logger.WithFields(log.Fields{"com": "POOL"}) // all components must use this logger
loggerpool.Infof("Mempool started")
atomic.AddUint32(&globals.Subsystem_Active, 1) // increment subsystem
//TODO load any trasactions saved at previous exit
return &mempool, nil
}
func (pool *Mempool) Shutdown() {
//TODO save mempool tx somewhere
loggerpool.Infof("Mempool stopped")
atomic.AddUint32(&globals.Subsystem_Active, ^uint32(0)) // this decrement 1 fom subsystem
}
func (pool *Mempool) Mempool_Add_TX(tx *Transaction, Reason int) {
pool.Lock()
defer pool.Unlock()
var object mempool_object
hash := crypto.Hash(tx.GetHash())
// check if tx already exists, skip it
if _, ok := pool.txs[hash]; ok {
loggerpool.Infof("Pool already contains %x, skipping \n", hash)
return
}
object.Tx = tx
object.Reason = Reason
object.Added = uint64(time.Now().Unix())
pool.txs[hash] = object
}
// delete specific tx from pool
func (pool *Mempool) Mempool_Delete_TX(crypto.Hash) {
}
// get specific tx from mem pool
func (pool *Mempool) Mempool_Get_TX(txid crypto.Hash) ([]byte, error) {
return nil, nil
}
// return list of all txs in pool
func (pool *Mempool) Mempool_List_TX() []crypto.Hash {
pool.Lock()
defer pool.Unlock()
var list []crypto.Hash
for k, _ := range pool.txs {
list = append(list, k)
}
return list
}

+ 56
- 0
blockchain/outputs_index.go

@ -0,0 +1,56 @@
package blockchain
// thhis file implements code which controls output indexes
// rewrites them during chain reorganisation
import "fmt"
import "encoding/binary"
import "github.com/deroproject/derosuite/crypto"
//import "github.com/deroproject/derosuite/crypto/ringct"
type Output_index struct {
Key crypto.Hash // stealth address key
Commitment crypto.Hash // commitment public key
Height uint64 // height to which this belongs
}
func (o *Output_index) Serialize() (result []byte) {
result = append(o.Key[:], o.Commitment[:]...)
result = append(result, itob(o.Height)...)
return
}
func (o *Output_index) Deserialize(buf []byte) (err error) {
if len(buf) != ( 32 + 32 + 8){
return fmt.Errorf("Output index needs to be 72 bytes in size but found to be %d bytes", len(buf))
}
copy(o.Key[:],buf[:32])
copy(o.Commitment[:],buf[32:64])
o.Height = binary.BigEndian.Uint64(buf[64:])
return
}
// this function writes or overwrites the data related to outputs
// the following data is collected from each output
// the secret key,
// the commitment ( for miner tx the commitment is created from scratch
// 8 bytes blockheight to which this output belongs
// this function should always succeed or panic showing something is not correct
// NOTE: this function should only be called after all the tx and the block has been stored to DB
func (chain *Blockchain)write_output_index(block_id crypto.Hash){
// load the block
bl, err := chain.Load_BL_FROM_ID(block_id)
if err != nil {
panic(fmt.Sprintf("No such block %x\n", block_id))
}
_ = bl
}

+ 37
- 0
blockchain/readme.txt

@ -0,0 +1,37 @@
The blockchain is stored as follows, assuming genesis block is stored
each block has a single child to store main chain
each block can have multiple children to store numerous blocks as alternative
each block stores the following extra info
1) height
2) single parent
3) cumulative difficulty
4) size of block ( size of all transactions included within block + size of block )
5) timestamp
6) cumulative coins
7) block blob itself
8) child
9) multiple children ( alternative or orphan blocks )
storing block mean storing all attributes except child/children
connecting a block means setting child/children attributes properly
imagine the chain as a doubly linked list, with traversal possible using blockids as ptrs
the psedo code is as follows
1) verify incoming blockor semantic errors
2) verify block pow
3) verify each and every transaction for correctnes
4) store the transactions
5) store the block, height, cumulative difficulty, coins emission etc
6) check whether block is the new top,
7) if yes,update main chain
8) if not we have to do chain reorganisation at the top
9) choose the block with higher poW as new top
10) push alt chain txs to mem pool
11) if block is being added somewhere in the middle, find the chain with higher Pow as main chain
12) push the orphan block txs to mempool

+ 169
- 0
blockchain/reorg_test.go

@ -0,0 +1,169 @@
package blockchain
import "sort"
import "testing"
import "github.com/deroproject/derosuite/crypto"
/* this function tests the core chain selection algorithm for reorganisation purpose */
func Test_chain_sort(t *testing.T) {
var first_chain crypto.Hash // all zero
var second_chain crypto.Hash // first bytes 1
var third_chain crypto.Hash // first bytes 2
first_chain[0] = 0
second_chain[0] = 1
third_chain[0] = 2
{
var chain bestChain
chain = append(chain, chain_data{
hash: first_chain, // this is the best chain based , if cumulative difficulty and time are same
cdifficulty: 99,
foundat: 99})
chain = append(chain, chain_data{ // this is the best chain based on cumulative difficulty
hash: second_chain,
cdifficulty: 123,
foundat: 96})
chain = append(chain, chain_data{
hash: third_chain,
cdifficulty: 80,
foundat: 94}) //if cumulative diff are same , this is the best chain
sort.Sort(chain)
if chain[0].hash != second_chain {
t.Errorf("Best chain not selected instead selected %d\n", chain[0].hash[0])
}
if chain[1].hash != first_chain || chain[2].hash != third_chain {
t.Errorf("Best chain not selected fault in sorting\n")
}
// lets trigger the case when cumulative difficulty are same
chain[0].cdifficulty = 5000
chain[1].cdifficulty = 5000
chain[2].cdifficulty = 5000
sort.Sort(chain)
if chain[0].hash != third_chain {
t.Errorf("Best chain not selected instead selected %d\n", chain[0].hash[0])
}
if chain[1].hash != second_chain || chain[2].hash != first_chain {
t.Errorf("Best chain not selected fault in sorting\n")
}
// lets trigger the case when cumulative difficulty are same and also find time are same
chain[0].foundat = 5000
chain[1].foundat = 5000
chain[2].foundat = 5000
sort.Sort(chain)
if chain[0].hash != first_chain {
t.Errorf("Best chain not selected instead selected %d\n", chain[0].hash[0])
}
if chain[1].hash != second_chain || chain[2].hash != third_chain {
t.Errorf("Best chain not selected fault in sorting\n")
}
}
}
/* since reorganisation based consensus is tough to test at this point this time
* we have extracted a snapshot based on dero chain wirh details below,
* an altchain 13 long
* 13 blocks long, from height 16996 (816 deep), diff 538095141258 :77d6b92961146232ba36ced359f6dbbf3170299b530d2c7d0da5c398f7713d23
*
1 blocks long, from height 16995 (817 deep), diff 537112495290:597ff30f0913834ecbc59e4273619e930c2166dc07c1362c6ed6387265b2ddd8
1 blocks long, from height 16859 (953 deep), diff 525062172399:5153884d9cf66bd6365b99fa7aad410a6fa219c4d749f186471f1166e76b4082
2 blocks long, from height 13619 (4193 deep), diff 288397472393:ff36451029ad3a021629f38f5f88a3d76bf4208589530b2a4b57a535f16aaf89
1 blocks long, from height 16163 (1649 deep), diff 468618212179:f9a3faa33054a4a1fa349321c546ee5f42cc416f13a991152c64fcbef994518b
1 blocks long, from height 15874 (1938 deep), diff 455084019052:143cc96808483da603dc704b75b6114699d99bdfb9c415a5be20d42e63b1cc4f
1 blocks long, from height 17397 (415 deep), diff 578283212318:6c3106b25a858c178a6e3eb7614438cf6d9faaecc1ea8fe416260d587867deb3
1 blocks long, from height 13618 (4194 deep), diff 288208474072:a3918ac81a08e8740f99f79ff788d9e147ceb7e530ed590ac1e0f5d1cbba28c5
13 blocks long, from height 16996 (816 deep), diff 538095141258:77d6b92961146232ba36ced359f6dbbf3170299b530d2c7d0da5c398f7713d23
*
2018-01-11 09:13:08.250 [P2P4] INFO global src/cryptonote_protocol/cryptonote_protocol_handler.inl:305 [78.131.18.216:
57948 INC] Sync data returned a new top block can
didate: 17822 -> 17022 [Your node is 800 blocks (0 days) ahead]
SYNCHRONIZATION started
2018-01-11 09:13:10.089 [P2P9] INFO global src/cryptonote_core/blockchain.cpp:1436 ----- BLOCK ADDED AS ALTERNATIVE ON HEI
GHT 16996
id: <1ae49ffd0caa6ffb6dc5617a06ae75e7d140303d69e06f4b6b0dcc4bc98383bc>
PoW: <ea2a23ce73c81d1a868749165264dda8b5bbf8016432fa9aed4aacb608000000>
difficulty: 96764713
2018-01-11 09:13:10.122 [P2P9] INFO global src/cryptonote_core/blockchain.cpp:1436 ----- BLOCK ADDED AS ALTERNATIVE ON HEI
GHT 16997
id: <78581a7ea3a71082ead85cfde06ebb2a4d7bbfcfef9a0fe69e373e78b807fa7f>
PoW: <cd989a29103f7d06b1b135ff196764bf490927f5a1dd81421ce45f3417000000>
difficulty: 105117242
2018-01-11 09:13:10.161 [P2P9] INFO global src/cryptonote_core/blockchain.cpp:1436 ----- BLOCK ADDED AS ALTERNATIVE ON HEI
GHT 16998
id: <03ca43b2dbfefe2b1854c516a4d30a7feeb5386dfe1169c3caf33ec3ff898582>
PoW: <4cf0c25ca3760cb69e5c664716ceb6bf2aaf5f5a399f8c6d9ca8c1f01d000000>
difficulty: 110587348
2018-01-11 09:13:10.200 [P2P9] INFO global src/cryptonote_core/blockchain.cpp:1436 ----- BLOCK ADDED AS ALTERNATIVE ON HEI
GHT 16999
id: <0eb61f1b78d810d314769e5e6157bc22894127b5d8e5bc417e99091005a7b858>
PoW: <bcb8cec71e4d6b000bc34deeaabba36605f759eab3ad2d0cb45d5c692a000000>
difficulty: 93615476
2018-01-11 09:13:10.246 [P2P9] INFO global src/cryptonote_core/blockchain.cpp:1436 ----- BLOCK ADDED AS ALTERNATIVE ON HEI
GHT 17000
id: <a39b5f88e39231ddebf80f8023724f9b7ee77dbb81e91f19b96933a8cf118313>
PoW: <cdd57823371698e564c30bf2e6e4a1740c08874baaba317e42b022b31c000000>
difficulty: 67324783
2018-01-11 09:13:10.278 [P2P9] INFO global src/cryptonote_core/blockchain.cpp:1436 ----- BLOCK ADDED AS ALTERNATIVE ON HEI
GHT 17001
id: <f63b50e5962bb7fec38a224f09935177a103c20bcd272a0df24c36559bdc2106>
PoW: <1186387bc636a097188298d9acccd73e4b7bb0722bc0c24eb2371cd70a000000>
difficulty: 62665212
2018-01-11 09:13:10.327 [P2P9] INFO global src/cryptonote_core/blockchain.cpp:1436 ----- BLOCK ADDED AS ALTERNATIVE ON HEI
GHT 17002
id: <f87dd7136998a2e56a963ca1387a9738a98c7aa72a6f8a570f9c71397af383ed>
PoW: <75144bd06a1acb7b1ecb90862a6d6044e89756816c18e9eea135452b33000000>
difficulty: 58400488
2018-01-11 09:13:10.363 [P2P9] INFO global src/cryptonote_core/blockchain.cpp:1436 ----- BLOCK ADDED AS ALTERNATIVE ON HEI
GHT 17003
id: <8e7a86de29068834e3dd3a498c4717159cceec7b6f0753b65cbf889d564a6982>
PoW: <84211ee2f3d370fb2ecab21158856d8ccb5f1f183d61a5d21bc84eae36000000>
difficulty: 61138607
2018-01-11 09:13:10.405 [P2P9] INFO global src/cryptonote_core/blockchain.cpp:1436 ----- BLOCK ADDED AS ALTERNATIVE ON HEI
GHT 17004
id: <038e2b878d3c204b8726a4159d8e2c113900a7180015ea30a48eda138ed043c7>
PoW: <b38f4d2a670f117cfdeb251fe82cc8b5d8fc936bcc291df5f7812ae841000000>
difficulty: 63855261
2018-01-11 09:13:10.435 [P2P9] INFO global src/cryptonote_core/blockchain.cpp:1436 ----- BLOCK ADDED AS ALTERNATIVE ON HEI
GHT 17005
id: <9249ac65bef9acb1b4375a50d9857627ef9f78961fe8fdd6068621572a174bd6>
PoW: <dff7efc9cd65b0867be8f67c18368bd190bf286e3d33da4fb3a03bd212000000>
difficulty: 67320973
2018-01-11 09:13:10.467 [P2P9] INFO global src/cryptonote_core/blockchain.cpp:1436 ----- BLOCK ADDED AS ALTERNATIVE ON HEI
GHT 17006
id: <1d9687780f854ea310bf5d7ea5e756be167fa043d81a58c3a3e602e08e7ad920>
PoW: <6126e42f0b6591db74345554fac27ef2fc5f115c5d680889485ebc3d0e000000>
difficulty: 70401048
2018-01-11 09:13:10.514 [P2P9] INFO global src/cryptonote_core/blockchain.cpp:1436 ----- BLOCK ADDED AS ALTERNATIVE ON HEI
GHT 17007
id: <6360ac8eba51cf1215461f304635e01935345f7fb7fa0c1ce3263ac131b5cb35>
PoW: <65b535014f8befdd9b503e1aa0b362e81f38b8fdad6d9751c2302f6e10000000>
difficulty: 74010875
2018-01-11 09:13:10.550 [P2P9] INFO global src/cryptonote_core/blockchain.cpp:1436 ----- BLOCK ADDED AS ALTERNATIVE ON HEI
GHT 17008
id: <77d6b92961146232ba36ced359f6dbbf3170299b530d2c7d0da5c398f7713d23>
PoW: <af18c3b12f0455a0b0fd88cc42be6ff9b877126e524f9ef8bb10fc1a2d000000>
difficulty: 67999998
2018-01-11 09:13:13.604 [P2P7] INFO global src/cryptonote_core/blockchain.cpp:1436 ----- BLOCK ADDED AS ALTERNATIVE ON HEI
GHT 16996
*/

+ 9
- 0
blockchain/signature.go

@ -0,0 +1,9 @@
package blockchain
// version 1 transaction support
const SIGNATURE_V1_LENGTH = 64
type Signature_v1 struct {
R [32]byte
C [32]byte
}

+ 455
- 0
blockchain/store.go

@ -0,0 +1,455 @@
package blockchain
import "fmt"
import "encoding/binary"
//import log "github.com/sirupsen/logrus"
import "github.com/deroproject/derosuite/crypto"
import "github.com/deroproject/derosuite/globals"
/* this file implements the only interface which translates comands to/from blockchain to storage layer *
*
*
*/
var TOP_ID = []byte("TOP_ID") // stores current TOP, only stores single value
var TX_ID = []byte("TX") // stores transactions
var BLOCK_ID = []byte("BLOCK") // stores blocks
var CHAIN = []byte("CHAIN") // this stores the actual chain, parents keeps child list, starts from genesis block
var HEIGHT_TO_BLOCK_ID = []byte("HEIGHT_TO_BLOCK_ID") // stores block height to block id mapping
var BLOCK_ID_TO_HEIGHT = []byte("BLOCK_ID_TO_HEIGHT") // stores block id to height mapping
var BLOCK_ID_POW = []byte("BLOCK_ID_POW") // stores block_id to pow, this is slow to calculate, do we even need to store it
// all orphan ids are stored here, it id exists its orphan,
// once a block is adden to orphan list it cannot be removed, since we have a better block
var ORPHAN = []byte("ORPHAN")
var ORPHAN_HEIGHT = []byte("ORPHAN_HEIGHT") // height wise orphans are store here
var OO_ID = []byte{0x4} // mapping of incremental numbers to respective transaction ID
var ALTERNATIVE_BLOCKS_ID = []byte{0x5} // each block contains lists of alternative child blocks
var BLOCKCHAIN_UNIVERSE = []byte("BLOCKCHAIN_UNIVERSE") // all block chain data is store in this BLOCKCHAIN_UNIVERSE
// there are only 3 galaxies
var GALAXY_BLOCK = []byte("BLOCK")
var GALAXY_TRANSACTION = []byte("TRANSACTION")
var GALAXY_KEYIMAGE = []byte("KEYIMAGE")
//2 galaxies store inverse mapping
var GALAXY_HEIGHT = []byte("HEIGHT") // height to block id mapping
var GALAXY_OUTPUT_INDEX = []byte("OUTPUT_INDEX") // incremental index over output index
// the unique TXID or block ID becomes the solar system , which is common and saves lot of space
// individual attributes becomes the planets
// individual attributes should be max 1 or 2 chars long, as they will be repeated millions of times and storing a static string millions of times shows foolishness
var PLANET_BLOB = []byte("BLOB") //it shows serialised block
var PLANET_HEIGHT = []byte("HEIGHT") // contains height
var PLANET_PARENT = []byte("PARENT") // parent of block
var PLANET_SIZE = []byte("SIZE") // sum of block + all txs
var PLANET_ALREADY_GENERATED_COINS = []byte("CCOINS") // all coins generated till this block
var PLANET_OUTPUT_INDEX = []byte("OUTPUT_INDEX") // tx outputs indexing starts from here for this block
var PLANET_CUMULATIVE_DIFFICULTY = []byte("CDIFFICULTY")
var PLANET_CHILD = []byte("CHILD")
//var PLANET_ORPHAN = []byte("ORPHAN")
var PLANET_TIMESTAMP = []byte("TIMESTAMP")
// this ill only be present if more tahn 1 child exists
var PLANET_CHILDREN = []byte("CHILREN") // children list excludes the main child, so its a multiple of 32
// the TX has the following attributes
var PLANET_TX_BLOB = []byte("BLOB") // contains serialised TX , this attribute is also found in BLOCK where
var PLANET_TX_MINED_IN_BLOCK = []byte("MINERBLOCK") // which block mined this tx
var PLANET_TX_SIZE = []byte("SIZE")
// the universe concept is there, as we bring in smart contracts, we will give each of them a universe to play within
// while communicating with external universe
/*
func (chain *Blockchain) Store_Main_Chain(parent_id crypto.Hash, child_id crypto.Hash){
err := chain.store.StoreObject(BLOCKCHAIN_UNIVERSE,GALAXY_BLOCK,parent_id[:],PLANET_CHILD, child_id[:] )
_ = err
}
func (chain *Blockchain) Load_Main_Chain(parent_id crypto.Hash) (child_id crypto.Hash ){
var err error
// store OO to TXID automatically
object_data,err = chain.store.LoadObject(BLOCKCHAIN_UNIVERSE,GALAXY_BLOCK,parent_id[:],PLANET_CHILD )
if err != nil {
return child_id,err
}
if len(object_data) == 0 {
return child_id, fmt.Errorf("No Block at such Height %d", Height)
}
if len(object_data) != 32 {
panic("Database corruption, invalid block hash ")
}
copy(child_id[:],object_data[:32])
_ = err
return child_id
}
*/
// check whether the block has a child
func (chain *Blockchain) Does_Block_Have_Child(block_id crypto.Hash) bool {
var err error
object_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, block_id[:], PLANET_CHILD)
if err != nil || len(object_data) == 0 {
return false
}
if len(object_data) != 32 {
panic("Database corruption, invalid block hash ")
}
return true
}
// load the main child
func (chain *Blockchain) Load_Block_Child(parent_id crypto.Hash) (child_id crypto.Hash) {
if !chain.Does_Block_Have_Child(parent_id) {
panic("Block does not have a child")
}
object_data, _ := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, parent_id[:], PLANET_CHILD)
copy(child_id[:], object_data)
return
}
// changes or set child block of a parent
// there can be only 1 child, rest all are alternatives and stored as
func (chain *Blockchain) Store_Block_Child(parent_id crypto.Hash, child_id crypto.Hash) {
err := chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, parent_id[:], PLANET_CHILD, child_id[:])
// load block children
_ = err
}
// while store children
func (chain *Blockchain) Store_Block_Children(parent_id crypto.Hash, children []crypto.Hash, exclude_child crypto.Hash) {
var children_bytes []byte
for i := range children {
if children[i] != exclude_child { // exclude main child
children_bytes = append(children_bytes, children[i][:]...)
}
}
err := chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, parent_id[:], PLANET_CHILDREN, children_bytes)
_ = err
}
func (chain *Blockchain) Load_Block_Children(parent_id crypto.Hash) (children []crypto.Hash) {
var child_hash crypto.Hash
if !chain.Does_Block_Have_Child(parent_id) { // block doesnot have a child, so it cannot have children
return
}
// we are here means parent does have child
children = append(children, chain.Load_Block_Child(parent_id))
// check for children
children_bytes, _ := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, parent_id[:], PLANET_CHILDREN)
if len(children_bytes)%32 != 0 {
panic(fmt.Sprintf("parent does not have child hash in multiples of 32, block_hash %x", parent_id))
}
for i := 0; i < len(children_bytes); i = i + 32 {
copy(child_hash[:], children_bytes[i:i+32])
children = append(children, child_hash)
}
return children
}
// store a tx
// this only occurs when a tx has been mined
func (chain *Blockchain) Store_TX(tx *Transaction) {
hash := tx.GetHash()
serialized := tx.Serialize()
err := chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, hash[:], PLANET_TX_BLOB, serialized)
// store size of tx
chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, hash[:], PLANET_TX_SIZE, uint64(len(serialized)))
_ = err
}
func (chain *Blockchain) Store_TX_Miner(txhash crypto.Hash, block_id crypto.Hash) {
// store block id which mined this tx
err := chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, txhash[:], PLANET_TX_MINED_IN_BLOCK, block_id[:])
_ = err
}
func (chain *Blockchain) Load_TX_Size(txhash crypto.Hash) uint64 {
// store block id which mined this tx
size, err := chain.store.LoadUint64(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, txhash[:], PLANET_TX_SIZE)
if err != nil {
logger.Warnf("Size not stored for tx %x", txhash)
}
return size
}
// BUG we should be able to delete any arbitrary key
// since a tx mined by one block, can be back in pool after chain reorganises
// TODO the miner tx should be extracted ands stored from somewhere else
// NOTE: before storing a block, its transactions must be stored
func (chain *Blockchain) Store_BL(bl *Block) {
// store block height BHID automatically
hash := bl.GetHash()
// we should deserialize the block here
serialized_bytes := bl.Serialize() // we are storing the miner transactions within
err := chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_BLOB, serialized_bytes)
// get height of parent, add 1 and store it
height := uint64(0)
if hash != globals.Config.Genesis_Block_Hash { // genesis block has no parent
height = chain.Load_Height_for_BL_ID(bl.Prev_Hash)
height++
}
// store new height
chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_HEIGHT, height)
// store timestamp
chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_TIMESTAMP, bl.Timestamp)
// store parent
chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_PARENT, bl.Prev_Hash[:])
// calculate cumulative difficulty at last block
difficulty_of_current_block := uint64(0)
cumulative_difficulty := uint64(0)
if hash != globals.Config.Genesis_Block_Hash { // genesis block has no parent
cumulative_difficulty = chain.Load_Block_Cumulative_Difficulty(bl.Prev_Hash)
difficulty_of_current_block = chain.Get_Difficulty_At_Block(bl.Prev_Hash)
} else {
cumulative_difficulty = 1 // genesis block cumulative difficulty is 1
}
total_difficulty := cumulative_difficulty + difficulty_of_current_block
chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_CUMULATIVE_DIFFICULTY, total_difficulty)
// total size of block = size of block + size of all transactions in block ( excludind miner tx)
size_of_block := uint64(len(serialized_bytes))
for i := 0; i < len(bl.Tx_hashes); i++ {
size_of_tx := chain.Load_TX_Size(bl.Tx_hashes[i])
size_of_block += size_of_tx
}
chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_SIZE, size_of_block)
// calculated position of vouts in global index
/* TODO below code has been disabled and should be enabled for extensive testing
index_pos := uint64(0)
if hash != globals.Config.Genesis_Block_Hash {
// load index pos from last block + add count of vouts from last block
index_pos = chain.Get_Block_Output_Index(bl.Prev_Hash)
vout_count_prev_block := chain.Block_Count_Vout(bl.Prev_Hash)
index_pos += vout_count_prev_block
}
chain.store.StoreUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_OUTPUT_INDEX, index_pos)
logger.Debugf("height %d output index %d\n",height, index_pos)
*/
// TODO calculate total coins emitted till this block
// also extract and store the miner tx separetly, fr direct querying purpose
chain.Store_TX(&bl.Miner_tx)
_ = err
}
func (chain *Blockchain) Load_TX_FROM_ID(hash [32]byte) (*Transaction, error) {
var tx Transaction
tx_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_TRANSACTION, hash[:], PLANET_TX_BLOB)
if err != nil {
return nil, err
}
// we should deserialize the block here
err = tx.DeserializeHeader(tx_data)
if err != nil {
logger.Printf("fError deserialiing tx, block id %x len(data) %d data %x\n", hash[:], len(tx_data), tx_data)
return nil, err
}
return &tx, nil
}
func (chain *Blockchain) Load_TX_FROM_OO(Offset uint64) {
}
func (chain *Blockchain) Load_BL_FROM_ID(hash [32]byte) (*Block, error) {
var bl Block
block_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_BLOB)
if err != nil {
return nil, err
}
if len(block_data) == 0 {
return nil, fmt.Errorf("Block not found in DB")
}
// we should deserialize the block here
err = bl.Deserialize(block_data)
if err != nil {
logger.Warnf("fError deserialiing block, block id %x len(data) %d data %x\n", hash[:], len(block_data), block_data)
return nil, err
}
return &bl, nil
}
// this will give you a block id at a specific height
func (chain *Blockchain) Load_BL_ID_at_Height(Height uint64) (hash crypto.Hash, err error) {
object_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_HEIGHT, PLANET_HEIGHT, itob(Height))
if err != nil {
return hash, err
}
if len(object_data) == 0 {
return hash, fmt.Errorf("No Block at such Height %d", Height)
}
if len(object_data) != 32 {
panic("Database corruption, invalid block hash ")
}
copy(hash[:], object_data[:32])
return hash, nil
}
// this will give you a block id at a specific height
func (chain *Blockchain) Store_BL_ID_at_Height(Height uint64, hash crypto.Hash) {
// store height to block id mapping
chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, GALAXY_HEIGHT, PLANET_HEIGHT, itob(Height), hash[:])
}
func (chain *Blockchain) Load_Height_for_BL_ID(hash crypto.Hash) (Height uint64) {
object_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_HEIGHT)
if err != nil {
logger.Warnf("Error while querying height for block %x\n", hash)
return
}
if len(object_data) == 0 {
//return hash, fmt.Errorf("No Height for block %x", hash[:])
return
}
if len(object_data) != 8 {
panic("Database corruption, invalid block hash ")
}
Height = binary.BigEndian.Uint64(object_data)
return Height
}
func (chain *Blockchain) Load_Block_Timestamp(hash crypto.Hash) uint64 {
timestamp, err := chain.store.LoadUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_TIMESTAMP)
if err != nil {
logger.Fatalf("Error while querying timestamp for block %x\n", hash)
panic("Error while querying timestamp for block")
}
return timestamp
}
func (chain *Blockchain) Load_Block_Cumulative_Difficulty(hash crypto.Hash) uint64 {
cdifficulty, err := chain.store.LoadUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_CUMULATIVE_DIFFICULTY)
if err != nil {
logger.Panicf("Error while querying cumulative difficulty for block %x\n", hash)
}
return cdifficulty
}
func (chain *Blockchain) Load_Block_Parent_ID(hash crypto.Hash) crypto.Hash {
var parent_id crypto.Hash
object_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, hash[:], PLANET_PARENT)
if err != nil || len(object_data) != 32 {
logger.Panicf("Error while querying parent id for block %x\n", hash)
}
copy(parent_id[:], object_data)
return parent_id
}
// store current top id
func (chain *Blockchain) Store_TOP_ID(hash crypto.Hash) {
chain.store.StoreObject(BLOCKCHAIN_UNIVERSE, TOP_ID, TOP_ID, TOP_ID, hash[:])
}
func (chain *Blockchain) Load_TOP_ID() (hash crypto.Hash) {
object_data, err := chain.store.LoadObject(BLOCKCHAIN_UNIVERSE, TOP_ID, TOP_ID, TOP_ID)
if err != nil {
panic("Backend failure")
}
if len(object_data) == 0 {
panic(fmt.Errorf("most probably Database corruption, No TOP_ID stored "))
}
if len(object_data) != 32 {
panic("Database corruption, invalid block hash ")
}
copy(hash[:], object_data[:32])
return hash
}
// itob returns an 8-byte big endian representation of v.
func itob(v uint64) []byte {
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, uint64(v))
return b
}
// get the position from where indexing must start for this block
// indexing mean vout based index
// cryptonote works by giving each vout a unique index
func (chain *Blockchain)Get_Block_Output_Index(block_id crypto.Hash) uint64 {
if block_id == globals.Config.Genesis_Block_Hash { // genesis block has no output index
return 0 ; // counting starts from zero
}
index, err := chain.store.LoadUint64(BLOCKCHAIN_UNIVERSE, GALAXY_BLOCK, block_id[:], PLANET_OUTPUT_INDEX)
if err != nil {
// TODO this panic must be enabled to catch some bugs
panic(fmt.Errorf("Cannot load output index for %x err %s", block_id, err))
return 0
}
return index
}

+ 661
- 0
blockchain/transaction.go

@ -0,0 +1,661 @@
package blockchain
import "fmt"
import "bytes"
import "encoding/binary"
import "github.com/romana/rlog"
import "github.com/deroproject/derosuite/crypto"
import "github.com/deroproject/derosuite/crypto/ringct"
const TXIN_GEN = byte(0xff)
const TXIN_TO_SCRIPT = byte(0)
const TXIN_TO_SCRIPTHASH = byte(1)
const TXIN_TO_KEY = byte(2)
const TXOUT_TO_SCRIPT = byte(0)
const TXOUT_TO_SCRIPTHASH = byte(1)
const TXOUT_TO_KEY = byte(2)
var TX_IN_NAME = map[byte]string{
TXIN_GEN: "Coinbase",
TXIN_TO_SCRIPT: "To Script",
TXIN_TO_SCRIPTHASH: "To Script hash",
TXIN_TO_KEY: "To key",
}
const TRANSACTION = byte(0xcc)
const BLOCK = byte(0xbb)
/*
VARIANT_TAG(binary_archive, cryptonote::txin_to_script, 0x0);
VARIANT_TAG(binary_archive, cryptonote::txin_to_scripthash, 0x1);
VARIANT_TAG(binary_archive, cryptonote::txin_to_key, 0x2);
VARIANT_TAG(binary_archive, cryptonote::txout_to_script, 0x0);
VARIANT_TAG(binary_archive, cryptonote::txout_to_scripthash, 0x1);
VARIANT_TAG(binary_archive, cryptonote::txout_to_key, 0x2);
VARIANT_TAG(binary_archive, cryptonote::transaction, 0xcc);
VARIANT_TAG(binary_archive, cryptonote::block, 0xbb);
*/
/* outputs */
type Txout_to_script struct {
// std::vector<crypto::public_key> keys;
// std::vector<uint8_t> script;
Keys [][32]byte
Script []byte
/* BEGIN_SERIALIZE_OBJECT()
FIELD(keys)
FIELD(script)
END_SERIALIZE()
*/
}
type Txout_to_scripthash struct {
//crypto::hash hash;
Hash [32]byte
}
type Txout_to_key struct {
Key crypto.Key
// Mask [32]byte `json:"-"`
/*txout_to_key() { }
txout_to_key(const crypto::public_key &_key) : key(_key) { }
crypto::public_key key;*/
}
// there can be only 4 types if inputs
// used by miner
type Txin_gen struct {
Height uint64 // stored as varint
}
type Txin_to_script struct {
Prev [32]byte
Prevout uint64
Sigset []byte
/* BEGIN_SERIALIZE_OBJECT()
FIELD(prev)
VARINT_FIELD(prevout)
FIELD(sigset)
END_SERIALIZE()
*/
}
type Txin_to_scripthash struct {
Prev [32]byte
Prevout uint64
Script Txout_to_script
Sigset []byte
/* BEGIN_SERIALIZE_OBJECT()
FIELD(prev)
VARINT_FIELD(prevout)
FIELD(script)
FIELD(sigset)
END_SERIALIZE()
*/
}
type Txin_to_key struct {
Amount uint64
Key_offsets []uint64 // this is encoded as a varint for length and then all offsets are stored as varint
//crypto::key_image k_image; // double spending protection
K_image crypto.Hash `json:"k_image"` // key image
/* BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(amount)
FIELD(key_offsets)
FIELD(k_image)
END_SERIALIZE()
*/
}
type Txin_v interface{} // it can only be txin_gen, txin_to_script, txin_to_scripthash, txin_to_key
type Tx_out struct {
Amount uint64
Target interface{} // txout_target_v ;, it can only be txout_to_script, txout_to_scripthash, txout_to_key
/* BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(amount)
FIELD(target)
END_SERIALIZE()
*/
}
// the core transaction
type Transaction_Prefix struct {
Version uint64 `json:"version"`
Unlock_Time uint64 `json:"unlock_time"`
Vin []Txin_v
Vout []Tx_out
Extra []byte
ExtraType byte `json:"-"`
}
type Transaction struct {
Transaction_Prefix
// same as Transaction_Prefix
// Signature not sure of what form
Signature []Signature_v1 `json:"-"` // old format, the array size is always equal to vin length,
//Signature_RCT RCT_Signature // version 2
RctSignature *ringct.RctSig
Expanded bool `json:"-"`
}
func (tx *Transaction) GetHash() (result crypto.Hash) {
switch tx.Version {
case 1:
result = crypto.Hash(crypto.Keccak256(tx.SerializeHeader()))
case 2:
// version 2 requires first computing 3 separate hashes
// prefix, rctBase and rctPrunable
// and then hashing the hashes together to get the final hash
prefixHash := tx.GetPrefixHash()
rctBaseHash := tx.RctSignature.BaseHash()
rctPrunableHash := tx.RctSignature.PrunableHash()
result = crypto.Hash(crypto.Keccak256(prefixHash[:], rctBaseHash[:], rctPrunableHash[:]))
default:
panic("Transaction version cannot be zero")
return
}
return
}
func (tx *Transaction) GetPrefixHash() (result crypto.Hash) {
result = crypto.Keccak256(tx.SerializeHeader())
return result
}
func (tx *Transaction) DeserializeHeader(buf []byte) (err error) {
Key_offset_count := uint64(0) // used to calculate expected signatures in v1
Mixin := -1
tx.Clear() // clear existing
//Mixin_count := 0 // for signature purpose
done := 0
tx.Version, done = binary.Uvarint(buf)
if done <= 0 {
return fmt.Errorf("Invalid Version in Transaction\n")
}
rlog.Tracef(10, "transaction version %d\n", tx.Version)
buf = buf[done:]
tx.Unlock_Time, done = binary.Uvarint(buf)
if done <= 0 {
return fmt.Errorf("Invalid Unlock_Time in Transaction\n")
}
buf = buf[done:]
// parse vin length
vin_length, done := binary.Uvarint(buf)
if done <= 0 {
return fmt.Errorf("Invalid Vin length in Transaction\n")
}
buf = buf[done:]
if vin_length == 0 {
return fmt.Errorf("Vin input cannot be zero in Transaction\n")
}
coinbase_done := false
rlog.Tracef(10, "vin length %d\n", vin_length)
for i := uint64(0); i < vin_length; i++ {
vin_type := buf[0]
buf = buf[1:] // consume 1 more byte
rlog.Tracef(10, "Processing i %d vin_type %s hex %x\n", i, TX_IN_NAME[vin_type], buf[:40])
switch vin_type {
case TXIN_GEN:
rlog.Tracef(10, "Coinbase transaction\n")
if coinbase_done {
return fmt.Errorf("Transaction cannot have multiple coin base transaction\n")
}
var current_vin Txin_gen
current_vin.Height, done = binary.Uvarint(buf)
if done <= 0 {
return fmt.Errorf("Invalid Height for Txin_gen vin in Transaction\n")
}
buf = buf[done:]
tx.Vin = append(tx.Vin, current_vin)
coinbase_done = true // we can no longer have coin base
case TXIN_TO_SCRIPT:
panic("TXIN_TO_SCRIPT not implemented")
case TXIN_TO_SCRIPTHASH:
panic("TXIN_TO_SCRIPTHASH not implemented")
case TXIN_TO_KEY:
var current_vin Txin_to_key
// parse Amount
current_vin.Amount, done = binary.Uvarint(buf)
if done <= 0 {
return fmt.Errorf("Invalid Amount for Txin_to_key vin in Transaction\n")
}
buf = buf[done:]
//fmt.Printf("Remaining data %x\n", buf[:20]);
mixin_count, done := binary.Uvarint(buf)
if done <= 0 {
return fmt.Errorf("Invalid offset_count for Txin_to_key vin in Transaction\n")
}
buf = buf[done:]
// safety check mixin cannot be larger than say x
if mixin_count > 40 {
return fmt.Errorf("Mixin cannot be larger than 40\n")
}
if Mixin < 0 {
Mixin = int(mixin_count)
}
if Mixin != int(mixin_count) { // all vins must have same mixin
return fmt.Errorf("Different mixin in Transaction\n")
}
//Mixin_input_count += Mixin
for j := uint64(0); j < mixin_count; j++ {
offset, done := binary.Uvarint(buf)
if done <= 0 {
return fmt.Errorf("Invalid key offset for Txin_to_key vin in Transaction\n")
}
buf = buf[done:]
current_vin.Key_offsets = append(current_vin.Key_offsets, offset)
}
Key_offset_count += mixin_count
copy(current_vin.K_image[:], buf[:32]) // copy key image
buf = buf[32:] // consume key image bytes
tx.Vin = append(tx.Vin, current_vin)
// panic("TXIN_TO_KEY not implemented")
default:
panic("Invalid VIN type in Transaction")
fmt.Errorf("Invalid VIN type in Transaction\n")
}
}
//fmt.Printf("TX before vout %+v\n", tx)
//fmt.Printf("buf before vout length %x\n", buf)
vout_length, done := binary.Uvarint(buf)
if done <= 0 {
return fmt.Errorf("Invalid Vout length in Transaction\n")
}
buf = buf[done:]
if vout_length == 0 {
return fmt.Errorf("Vout cannot be zero in Transaction\n")
}
for i := uint64(0); i < vout_length; i++ {
// amount is decoded earlier
amount, done := binary.Uvarint(buf)
if done <= 0 {
return fmt.Errorf("Invalid Amount in Transaction\n")
}
buf = buf[done:]
// decode vout type
vout_type := buf[0]
buf = buf[1:] // consume 1 more byte
rlog.Tracef(10, "Vout Amount length %d vout type %d \n", amount, vout_type)
if tx.Version == 1 && amount == 0 { // version 2 can have any amount
return fmt.Errorf("Amount cannot be zero in Transaction\n")
}
switch vout_type {
case TXOUT_TO_SCRIPT:
//fmt.Printf("out to script\n")
panic("TXOUT_TO_SCRIPT not implemented")
case TXOUT_TO_SCRIPTHASH:
//fmt.Printf("out to scripthash\n")
var current_vout Txout_to_scripthash
copy(current_vout.Hash[:], buf[0:32])
tx.Vout = append(tx.Vout, Tx_out{Amount: amount, Target: current_vout})
buf = buf[32:]
//panic("TXOUT_TO_SCRIPTHASH not implemented")
case TXOUT_TO_KEY:
//fmt.Printf("out to key\n")
var current_vout Txout_to_key
copy(current_vout.Key[:], buf[0:32])
buf = buf[32:]
//Mixin_input_count++
tx.Vout = append(tx.Vout, Tx_out{Amount: amount, Target: current_vout})
default:
fmt.Errorf("Invalid VOUT type in Transaction\n")
}
}
// fmt.Printf("Extra %x\n", buf)
// decode extra
extra_length, done := binary.Uvarint(buf)
if done <= 0 {
return fmt.Errorf("Invalid Extra length in Transaction\n")
}
buf = buf[done:]
// BUG extra needs to be processed in a loop till we load all extra fields
//tx.ExtraType = buf[0]
// buf = buf[1:] // consume 1 more byte
// extra_length--
rlog.Tracef(8, "extra len %d have %d \n", extra_length, len(buf))
tx.Extra = buf[:extra_length]
// whatever is leftover is signature
buf = buf[extra_length:] // consume more bytes
switch tx.Version {
case 1: // old style signatures, load value
for i := uint64(0); i < Key_offset_count; i++ {
var s Signature_v1
copy(s.R[:], buf[:32])
copy(s.C[:], buf[32:64])
tx.Signature = append(tx.Signature, s)
buf = buf[SIGNATURE_V1_LENGTH:]
}
case 2:
bufreader := bytes.NewReader(buf)
Mixin -= 1 // one is ours, rest are mixin
tx.RctSignature, err = ringct.ParseRingCtSignature(bufreader, len(tx.Vin), len(tx.Vout), Mixin)
if err != nil {
return err
}
}
/* we must deserialize signature some where else
//fmt.Printf("extra bytes %x\n",buf)
//fmt.Printf("signature len %d should be %d\n",len(buf),len(tx.Vin)*SIGNATURE_V1_LENGTH)
fmt.Printf("signature len %d should be %d\n",len(buf),Key_offset_count*SIGNATURE_V1_LENGTH)
switch tx.Version {
case 1 : // old style signatures, load value
for i := uint64(0); i < Key_offset_count;i++{
var s Signature_v1
copy(s.R[:],buf[:32])
copy(s.C[:],buf[32:64])
tx.Signature = append(tx.Signature, s)
buf = buf[SIGNATURE_V1_LENGTH:]
}
case 2:
tx.Signature_RCT.Type, done = binary.Uvarint(buf)
if done <= 0 {
return fmt.Errorf("Invalid RCT signature in Transaction\n")
}
buf = buf[done:]
switch tx.Signature_RCT.Type {
case 0 : // no signature break
case 1 :
tx.Signature_RCT.TxnFee, done = binary.Uvarint(buf)
if done <= 0 {
return fmt.Errorf("Invalid txn fee in Transaction\n")
}
buf = buf[done:]
fmt.Printf("RCT signature type %d Fee %d\n",tx.Signature_RCT.Type, tx.Signature_RCT.TxnFee)
// how many masked inputs depends on number of masked outouts
for i := (0); i < len(tx.Vout);i++{
// read masked input
var info ECDHinfo
copy(info.Mask[:], buf[0:32])
copy(info.Amount[:], buf[32:64])
tx.Signature_RCT.Amounts = append(tx.Signature_RCT.Amounts, info)
buf = buf[64:]
}
// now parse the public keys
for i := (0); i < len(tx.Vout);i++{
// read masked input
var tmp [32]byte
copy(tmp[:], buf[0:32])
tx.Signature_RCT.OutPK = append(tx.Signature_RCT.OutPK, tmp)
buf = buf[32:]
}
case 2 : // panic("ringct type 2 currently not handled")
default:
panic("unknown signature style")
}
default:
panic("unknown transaction version \n")
}
*/
rlog.Tracef(8, "TX deserialized %+v\n", tx)
/*
data.Local_time = binary.LittleEndian.Uint64( buf[24:], )
data.Local_Port = binary.LittleEndian.Uint32( buf[41:])
_ = data.Network_UUID.UnmarshalBinary(buf[58:58+16])
data.Peer_ID = binary.LittleEndian.Uint64( buf[83:] )
*/
return nil //fmt.Errorf("Done Transaction\n")
}
// calculated prefi has signature
func (tx *Transaction) PrefixHash() {
}
// calculated prefi has signature
func (tx *Transaction) Clear() {
// clean the transaction everything
tx.Version = 0
tx.Unlock_Time = 0
tx.Vin = tx.Vin[:0]
tx.Vout = tx.Vout[:0]
tx.Extra = tx.Extra[:0]
}
func (tx *Transaction) SerializeHeader() []byte {
var serialised_header bytes.Buffer
buf := make([]byte, binary.MaxVarintLen64)
n := binary.PutUvarint(buf, tx.Version)
serialised_header.Write(buf[:n])
n = binary.PutUvarint(buf, tx.Unlock_Time)
serialised_header.Write(buf[:n])
/*if len(tx.Vin) < 1 {
panic("No vins")
}*/
n = binary.PutUvarint(buf, uint64(len(tx.Vin)))
serialised_header.Write(buf[:n])
for _, current_vin := range tx.Vin {
switch current_vin.(type) {
case Txin_gen:
serialised_header.WriteByte(TXIN_GEN)
n = binary.PutUvarint(buf, current_vin.(Txin_gen).Height)
serialised_header.Write(buf[:n])
case Txin_to_key:
serialised_header.WriteByte(TXIN_TO_KEY)
n = binary.PutUvarint(buf, current_vin.(Txin_to_key).Amount)
serialised_header.Write(buf[:n])
// number of Ring member
n = binary.PutUvarint(buf, uint64(len(current_vin.(Txin_to_key).Key_offsets)))
serialised_header.Write(buf[:n])
// write ring members
for _, offset := range current_vin.(Txin_to_key).Key_offsets {
n = binary.PutUvarint(buf, offset)
serialised_header.Write(buf[:n])
}
// dump key image, interface needs a concrete type feor accessing array
cvin := current_vin.(Txin_to_key)
serialised_header.Write(cvin.K_image[:])
}
}
// time to serialize vouts
if len(tx.Vout) < 1 {
panic("No vout")
}
n = binary.PutUvarint(buf, uint64(len(tx.Vout)))
serialised_header.Write(buf[:n])
for _, current_vout := range tx.Vout {
// dump amount
n := binary.PutUvarint(buf, current_vout.Amount)
serialised_header.Write(buf[:n])
switch current_vout.Target.(type) {
case Txout_to_key:
serialised_header.WriteByte(TXOUT_TO_KEY)
target := current_vout.Target.(Txout_to_key)
serialised_header.Write(target.Key[:])
//serialised_header.Write(current_vout.Target.(Txout_to_key).Key[:])
default:
panic("This type of Txout not suppported")
}
}
// dump any extras
n = binary.PutUvarint(buf, uint64(len(tx.Extra)))
serialised_header.Write(buf[:n])
//rlog.Tracef("Extra length %d while serializing\n ", len(tx.Extra))
serialised_header.Write(tx.Extra[:])
return serialised_header.Bytes()
}
// serialize entire transaction include signature
func (tx *Transaction) Serialize() []byte {
header_bytes := tx.SerializeHeader()
base_bytes := tx.RctSignature.SerializeBase()
prunable := tx.RctSignature.SerializePrunable()
buf := append(header_bytes, base_bytes...)
buf = append(buf, prunable...)
return buf
}
/*
func (tx *Transaction) IsCoinbase() (result bool){
// check whether the type is Txin.get
if len(tx.Vin) != 0 { // coinbase transactions have no vin
return
}
if tx.Vout[0].(Target) != 0 { // coinbase transactions have no vin
return
}
}*/

+ 83
- 0
blockchain/transaction_ringct_test.go
File diff suppressed because it is too large
View File


+ 186
- 0
blockchain/transaction_test.go
File diff suppressed because it is too large
View File


+ 73
- 0
build_all.sh

@ -0,0 +1,73 @@
#!/usr/bin/env bash
package=$1
package="github.com/deroproject/derosuite/cmd/derod"
package_split=(${package//\// })
package_name=${package_split[-1]}
CURDIR=`/bin/pwd`
BASEDIR=$(dirname $0)
ABSPATH=$(readlink -f $0)
ABSDIR=$(dirname $ABSPATH)
PLATFORMS="darwin/amd64" # amd64 only as of go1.5
PLATFORMS="$PLATFORMS windows/amd64 windows/386" # arm compilation not available for Windows
PLATFORMS="$PLATFORMS linux/amd64 linux/386"
#PLATFORMS="$PLATFORMS linux/ppc64le" is it common enough ??
#PLATFORMS="$PLATFORMS linux/mips64le" # experimental in go1.6 is it common enough ??
PLATFORMS="$PLATFORMS freebsd/amd64 freebsd/386"
PLATFORMS="$PLATFORMS netbsd/amd64" # amd64 only as of go1.6
PLATFORMS="$PLATFORMS openbsd/amd64" # amd64 only as of go1.6
PLATFORMS="$PLATFORMS dragonfly/amd64" # amd64 only as of go1.5
#PLATFORMS="$PLATFORMS plan9/amd64 plan9/386" # as of go1.4, is it common enough ??
PLATFORMS="$PLATFORMS solaris/amd64" # as of go1.3
PLATFORMS_ARM="linux freebsd netbsd"
type setopt >/dev/null 2>&1
SCRIPT_NAME=`basename "$0"`
FAILURES=""
CURRENT_DIRECTORY=${PWD##*/}
OUTPUT="$package_name" # if no src file given, use current dir name
OUTPUT_DIR="$ABSDIR/build/"
for PLATFORM in $PLATFORMS; do
GOOS=${PLATFORM%/*}
GOARCH=${PLATFORM#*/}
BIN_FILENAME="${OUTPUT}-${GOOS}-${GOARCH}"
if [[ "${GOOS}" == "windows" ]]; then BIN_FILENAME="${BIN_FILENAME}.exe"; fi
CMD="GOOS=${GOOS} GOARCH=${GOARCH} go build -o $OUTPUT_DIR/${BIN_FILENAME} $package"
echo "${CMD}"
eval $CMD || FAILURES="${FAILURES} ${PLATFORM}"
done
# ARM64 builds only for linux
if [[ $PLATFORMS_ARM == *"linux"* ]]; then
CMD="GOOS=linux GOARCH=arm64 go build -o $OUTPUT_DIR/${OUTPUT}-linux-arm64 $package"
echo "${CMD}"
eval $CMD || FAILURES="${FAILURES} ${PLATFORM}"
fi
for GOOS in $PLATFORMS_ARM; do
GOARCH="arm"
# build for each ARM version
for GOARM in 7 6 5; do
BIN_FILENAME="${OUTPUT}-${GOOS}-${GOARCH}${GOARM}"
CMD="GOARM=${GOARM} GOOS=${GOOS} GOARCH=${GOARCH} go build -o $OUTPUT_DIR/${BIN_FILENAME} $package"
echo "${CMD}"
eval "${CMD}" || FAILURES="${FAILURES} ${GOOS}/${GOARCH}${GOARM}"
done
done
# eval errors
if [[ "${FAILURES}" != "" ]]; then
echo ""
echo "${SCRIPT_NAME} failed on: ${FAILURES}"
exit 1
fi

+ 8
- 0
cmd/derod/dummy_test.go

@ -0,0 +1,8 @@
package main
import "testing"
func Test_Part1(t *testing.T){
}

+ 438
- 0
cmd/derod/main.go

@ -0,0 +1,438 @@
package main
import "io"
import "os"
import "time"
import "fmt"
import "bytes"
import "strings"
import "strconv"
import "encoding/hex"
import "encoding/json"
import "path/filepath"
import "github.com/chzyer/readline"
import "github.com/docopt/docopt-go"
import log "github.com/sirupsen/logrus"
import "github.com/deroproject/derosuite/p2p"
import "github.com/deroproject/derosuite/globals"
import "github.com/deroproject/derosuite/blockchain"
import "github.com/deroproject/derosuite/crypto"
var command_line string = `derod
DERO : A secure, private blockchain with smart-contracts
Usage:
derod [--help] [--version] [--testnet] [--debug] [--socks-proxy=<socks_ip:port>] [--p2p-bind-port=<18090>] [--add-exclusive-node=<ip:port>]...
derod -h | --help
derod --version
Options:
-h --help Show this screen.
--version Show version.
--testnet Run in testnet mode.
--debug Debug mode enabled, print log messages
--socks-proxy=<socks_ip:port> Use a proxy to connect to network.
--p2p-bind-port=<18090> p2p server listens on this port.
--add-exclusive-node=<ip:port> Connect to this peer only (disabled for this version)`
func main() {
var err error
globals.Arguments, err = docopt.Parse(command_line, nil, true, "DERO daemon : work in progress", false)
if err != nil {
log.Fatalf("Error while parsing options err: %s\n", err)
}
// We need to initialize readline first, so it changes stderr to ansi processor on windows
l, err := readline.NewEx(&readline.Config{
//Prompt: "\033[92mDERO:\033[32m»\033[0m",
Prompt: "\033[92mDERO:\033[32m>>>\033[0m ",
HistoryFile: filepath.Join(os.TempDir(), "derod_readline.tmp"),
AutoComplete: completer,
InterruptPrompt: "^C",
EOFPrompt: "exit",
HistorySearchFold: true,
FuncFilterInputRune: filterInput,
})
if err != nil {
panic(err)
}
defer l.Close()
// parse arguments and setup testnet mainnet
globals.Initialize() // setup network and proxy
globals.Logger.Infof("") // a dummy write is required to fully activate logrus
// all screen output must go through the readline
globals.Logger.Out = l.Stdout()
globals.Logger.Debugf("Arguments %+v", globals.Arguments)
globals.Logger.Infof("DERO daemon : This version is under heavy development, use it for testing/evaluations purpose only")
globals.Logger.Infof("Daemon in %s mode", globals.Config.Name)
chain, _ := blockchain.Blockchain_Start(nil)
params := map[string]interface{}{}
params["chain"] = chain
p2p.P2P_Init(params)
// This tiny goroutine continuously updates status as required
go func() {
last_our_height := uint64(0)
last_best_height := uint64(0)
last_peer_count := uint64(0)
for {
if globals.Exit_In_Progress {
return
}
our_height := chain.Get_Height()
best_height := p2p.Best_Peer_Height()
peer_count := p2p.Peer_Count()
// only update prompt if needed
if last_our_height != our_height || last_best_height != best_height || last_peer_count != peer_count {
// choose color based on urgency
color := "\033[32m" // default is green color
if our_height < best_height {
color = "\033[33m" // make prompt yellow
} else if our_height > best_height {
color = "\033[31m" // make prompt red
}
pcolor := "\033[32m" // default is green color
if peer_count < 1 {
pcolor = "\033[31m" // make prompt red
} else if peer_count <= 8 {
pcolor = "\033[33m" // make prompt yellow
}
l.SetPrompt(fmt.Sprintf("\033[1m\033[32mDERO: \033[0m"+color+"%d/%d "+pcolor+"P %d\033[32m>>>\033[0m ", our_height, best_height, peer_count))
l.Refresh()
last_our_height = our_height
last_best_height = best_height
last_peer_count = peer_count
}
time.Sleep(1 * time.Second)
}
}()
setPasswordCfg := l.GenPasswordConfig()
setPasswordCfg.SetListener(func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) {
l.SetPrompt(fmt.Sprintf("Enter password(%v): ", len(line)))
l.Refresh()
return nil, 0, false
})
l.Refresh() // refresh the prompt
for {
line, err := l.Readline()
if err == readline.ErrInterrupt {
if len(line) == 0 {
fmt.Print("Ctrl-C received, Exit in progress\n")
globals.Exit_In_Progress = true
break
} else {
continue
}
} else if err == io.EOF {
break
}
line = strings.TrimSpace(line)
line_parts := strings.Fields(line)
command := ""
if len(line_parts) >= 1 {
command = strings.ToLower(line_parts[0])
}
switch {
case strings.HasPrefix(line, "mode "):
switch line[5:] {
case "vi":
l.SetVimMode(true)
case "emacs":
l.SetVimMode(false)
default:
println("invalid mode:", line[5:])
}
case line == "mode":
if l.IsVimMode() {
println("current mode: vim")
} else {
println("current mode: emacs")
}
case line == "login":
pswd, err := l.ReadPassword("please enter your password: ")
if err != nil {
break
}
println("you enter:", strconv.Quote(string(pswd)))
case line == "help":
usage(l.Stderr())
case line == "setpassword":
pswd, err := l.ReadPasswordWithConfig(setPasswordCfg)
if err == nil {
println("you set:", strconv.Quote(string(pswd)))
}
case strings.HasPrefix(line, "setprompt"):
if len(line) <= 10 {
log.Println("setprompt <prompt>")
break
}
l.SetPrompt(line[10:])
case strings.HasPrefix(line, "say"):
line := strings.TrimSpace(line[3:])
if len(line) == 0 {
log.Println("say what?")
break
}
go func() {
for range time.Tick(time.Second) {
log.Println(line)
}
}()
case command == "print_bc":
log.Info("printing block chain")
// first is starting point, second is ending point
start := int64(0)
stop := int64(0)
if len(line_parts) != 3 {
fmt.Printf("This function requires 2 parameters, start and endpoint\n")
continue
}
if s, err := strconv.ParseInt(line_parts[1], 10, 64); err == nil {
start = s
} else {
fmt.Printf("Invalid start value")
continue
}
if s, err := strconv.ParseInt(line_parts[2], 10, 64); err == nil {
stop = s
} else {
fmt.Printf("Invalid stop value")
continue
}
if start < 0 || start >= int64(chain.Get_Height()) {
fmt.Printf("Start value should be be between 0 and current height\n")
continue
}
if start > stop && stop >= int64(chain.Get_Height()) {
fmt.Printf("Stop value should be > start and current height\n")
continue
}
fmt.Printf("Printing block chain from %d to %d\n", start, stop)
for i := start; i < stop; i++ {
// get block id at height
current_block_id, err := chain.Load_BL_ID_at_Height(uint64(i))
if err != nil {
fmt.Printf("Skipping block at height %d due to error %s\n", i, err)
continue
}
timestamp := chain.Load_Block_Timestamp(current_block_id)
parent_block_id := chain.Load_Block_Parent_ID(current_block_id)
// calculate difficulty
//parent_cdiff := chain.Load_Block_Cumulative_Difficulty(parent_block_id)
//block_cdiff := chain.Load_Block_Cumulative_Difficulty(current_block_id)
diff := chain.Get_Difficulty_At_Block(parent_block_id)
//size := chain.
fmt.Printf("height: %10d, timestamp: %10d, difficulty: %12d\n", i, timestamp, diff)
fmt.Printf("Block Id: %x , prev block id:%x\n", current_block_id, parent_block_id)
fmt.Printf("\n")
}
case command == "print_block":
fmt.Printf("printing block\n")
if len(line_parts) == 2 && len(line_parts[1]) == 64 {
txid, err := hex.DecodeString(strings.ToLower(line_parts[1]))
if err != nil {
fmt.Printf("err while decoding txid err %s\n", err)
continue
}
var hash crypto.Hash
copy(hash[:32], []byte(txid))
fmt.Printf("block id: %x\n", hash[:])
bl, err := chain.Load_BL_FROM_ID(hash)
if err == nil {
fmt.Printf("Block : %x\n", bl.Serialize())
} else {
fmt.Printf("Err %s\n", err)
}
} else if len(line_parts) == 2 {
if s, err := strconv.ParseInt(line_parts[1], 10, 64); err == nil {
// first load block id from height
hash, err := chain.Load_BL_ID_at_Height(uint64(s))
if err == nil {
bl, err := chain.Load_BL_FROM_ID(hash)
if err == nil {
fmt.Printf("block id: %x\n", hash[:])
fmt.Printf("Block : %x\n", bl.Serialize())
json_bytes, err := json.Marshal(bl)
fmt.Printf("%s err : %s\n", string(prettyprint_json(json_bytes)), err)
} else {
fmt.Printf("Err %s\n", err)
}
} else {
fmt.Printf("err %s\n", err)
}
}
} else {
fmt.Printf("print_tx needs a single transaction id as arugument\n")
}
case command == "print_tx":
if len(line_parts) == 2 && len(line_parts[1]) == 64 {
txid, err := hex.DecodeString(strings.ToLower(line_parts[1]))
if err != nil {
fmt.Printf("err while decoding txid err %s\n", err)
continue
}
var hash crypto.Hash
copy(hash[:32], []byte(txid))
tx, err := chain.Load_TX_FROM_ID(hash)
if err == nil {
s_bytes := tx.Serialize()
fmt.Printf("tx : %x\n", s_bytes)
json_bytes, err := json.MarshalIndent(tx, "", " ")
_ = err
fmt.Printf("%s\n", string(json_bytes))
} else {
fmt.Printf("Err %s\n", err)
}
} else {
fmt.Printf("print_tx needs a single transaction id as arugument\n")
}
case strings.ToLower(line) == "diff":
fmt.Printf("Network %s BH %d, Diff %d, NW Hashrate %0.03f MH/sec TH %x\n", globals.Config.Name, chain.Get_Height(), chain.Get_Difficulty(), float64(chain.Get_Network_HashRate())/1000000.0, chain.Get_Top_ID())
case strings.ToLower(line) == "status":
// fmt.Printf("chain diff %d\n",chain.Get_Difficulty_At_Block(chain.Top_ID))
//fmt.Printf("chain nw rate %d\n", chain.Get_Network_HashRate())
inc, out := p2p.Peer_Direction_Count()
fmt.Printf("Network %s Height %d NW Hashrate %0.03f MH/sec TH %x Peers %d INC, %d OUT\n", globals.Config.Name, chain.Get_Height(), float64(chain.Get_Network_HashRate())/1000000.0, chain.Get_Top_ID(), inc,out )
case strings.ToLower(line) == "sync_info":
p2p.Connection_Print()
case strings.ToLower(line) == "bye":
fallthrough
case strings.ToLower(line) == "exit":
fallthrough
case strings.ToLower(line) == "quit":
goto exit
case line == "sleep":
log.Println("sleep 4 second")
time.Sleep(4 * time.Second)
case line == "":
default:
log.Println("you said:", strconv.Quote(line))
}
}
exit:
globals.Logger.Infof("Exit in Progress, Please wait")
time.Sleep(100 * time.Millisecond) // give prompt update time to finish
p2p.P2P_Shutdown() // shutdown p2p subsystem
chain.Shutdown() // shutdown chain subsysem
for globals.Subsystem_Active > 0 {
time.Sleep(100 * time.Millisecond)
}
}
func prettyprint_json(b []byte) []byte {
var out bytes.Buffer
err := json.Indent(&out, b, "", " ")
_ = err
return out.Bytes()
}
func usage(w io.Writer) {
io.WriteString(w, "commands:\n")
//io.WriteString(w, completer.Tree(" "))
io.WriteString(w,"\t\033[1mhelp\033[0m\t\tthis help\n")
io.WriteString(w,"\t\033[1mdiff\033[0m\t\tShow difficulty\n")
io.WriteString(w,"\t\033[1mprint_bc\033[0m\tPrint blockchain info in a given blocks range, print_bc <begin_height> <end_height>\n")
io.WriteString(w,"\t\033[1mprint_block\033[0m\tPrint block, print_block <block_hash> or <block_height>\n")
io.WriteString(w,"\t\033[1mprint_height\033[0m\tPrint local blockchain height\n")
io.WriteString(w,"\t\033[1mprint_tx\033[0m\tPrint transaction, print_tx <transaction_hash>\n")
io.WriteString(w,"\t\033[1mstatus\033[0m\t\tShow genereal information\n")
io.WriteString(w,"\t\033[1msync_info\033[0m\tPrint information about connected peers and their state\n")
io.WriteString(w,"\t\033[1mbye\033[0m\t\tQuit the daemon\n")
io.WriteString(w,"\t\033[1mexit\033[0m\t\tQuit the daemon\n")
io.WriteString(w,"\t\033[1mquit\033[0m\t\tQuit the daemon\n")
}
var completer = readline.NewPrefixCompleter(
/* readline.PcItem("mode",
readline.PcItem("vi"),
readline.PcItem("emacs"),
),
readline.PcItem("login"),
readline.PcItem("say",
readline.PcItem("hello"),
readline.PcItem("bye"),
),
readline.PcItem("setprompt"),
readline.PcItem("setpassword"),
readline.PcItem("bye"),
*/
readline.PcItem("help"),
/* readline.PcItem("go",
readline.PcItem("build", readline.PcItem("-o"), readline.PcItem("-v")),
readline.PcItem("install",
readline.PcItem("-v"),
readline.PcItem("-vv"),
readline.PcItem("-vvv"),
),
readline.PcItem("test"),
),
readline.PcItem("sleep"),
*/
readline.PcItem("diff"),
readline.PcItem("print_bc"),
readline.PcItem("print_block"),
readline.PcItem("print_height"),
readline.PcItem("print_tx"),
readline.PcItem("status"),
readline.PcItem("sync_info"),
readline.PcItem("bye"),
readline.PcItem("exit"),
readline.PcItem("quit"),
)
func filterInput(r rune) (rune, bool) {
switch r {
// block CtrlZ feature
case readline.CharCtrlZ:
return r, false
}
return r, true
}

+ 119
- 0
config/config.go

@ -0,0 +1,119 @@
package config
import "github.com/satori/go.uuid"
import "github.com/deroproject/derosuite/crypto"
// all global configuration variables are picked from here
var BLOCK_TIME = uint64(120)
// we are ignoring leap seconds from calculations
// coin emiision related settings
var COIN_MONEY_SUPPLY = uint64(18446744073709551615) // 2^64-1
var COIN_EMISSION_SPEED_FACTOR = uint64(20)
var COIN_DIFFICULTY_TARGET = uint64(120) // this is a feeder to emission formula
var COIN_FINAL_SUBSIDY_PER_MINUTE = uint64(300000000000) // 0.3 DERO per minute = 157680 per year roughly
var CRYPTONOTE_REWARD_BLOCKS_WINDOW = uint64(100) // last 100 blocks are used to create
var MAX_CHAIN_HEIGHT = uint64(2147483648) // 2^31
// we use this for scheduled hardforks
var CURRENT_BLOCK_MAJOR_VERSION = 6
var CURRENT_BLOCK_MINOR_VERSION = 6
var CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE = uint64(300000) // after this block size , reward calculated differently
// consider last 30 blocks for calculating difficulty
var DIFFICULTY_BLOCKS_COUNT_V2 = 30
const PROJECT_NAME = "dero"
const POOLDATA_FILENAME = "poolstate.bin"
//const CRYPTONOTE_BLOCKCHAINDATA_FILENAME "data.mdb" // these decisions are made by storage layer
//#define CRYPTONOTE_BLOCKCHAINDATA_LOCK_FILENAME "lock.mdb"
const P2P_NET_DATA_FILENAME = "p2pstate.bin"
// we can have number of chains running for testing reasons
type CHAIN_CONFIG struct {
Name string
Network_ID uuid.UUID // network ID
Public_Address_Prefix uint64
Public_Address_Prefix_Integrated uint64
P2P_Default_Port uint32
RPC_Default_Port uint32
Genesis_Nonce uint32
Genesis_Block_Hash crypto.Hash
Genesis_Tx string
}
var Mainnet = CHAIN_CONFIG{Name: "mainnet",
Network_ID: uuid.FromBytesOrNil([]byte{0x59, 0xd7, 0xf7, 0xe9, 0xdd, 0x48, 0xd5, 0xfd, 0x13, 0x0a, 0xf6, 0xe0, 0x9a, 0xec, 0xb9, 0x23}),
Public_Address_Prefix: 0xc8ed8, //for dERo
Public_Address_Prefix_Integrated: 0xa0ed8, //for dERi
P2P_Default_Port: 18090,
RPC_Default_Port: 18091,
Genesis_Nonce: 10000,
Genesis_Block_Hash: crypto.Hash([32]byte{0x36, 0x2d, 0x61, 0x48, 0xd6, 0x83, 0x08, 0x2d,
0x94, 0x2e, 0x53, 0xdd, 0xb5, 0x0d, 0xaf, 0x54,
0x6a, 0x10, 0x92, 0xda, 0x76, 0x98, 0x2d, 0x5b,
0xd4, 0xf1, 0x3d, 0x0d, 0xf0, 0x74, 0xec, 0x2f}),
Genesis_Tx: "" +
"02" + // version
"3c" + // unlock time
"01" + // vin length
"ff" + // vin #1
"00" + // height gen input
"01" + // vout length
"ffffffffffff07" + // output #1 amount
"02" + // output 1 type
"0bf6522f9152fa26cd1fc5c022b1a9e13dab697f3acf4b4d0ca6950a867a1943" + // output #1 key
"21" + // extra length in bytes
"01" + // extra pubkey tag
"1d92826d0656958865a035264725799f39f6988faa97d532f972895de849496d" + // tx pubkey
"00", // RCT signature none
}
var Testnet = CHAIN_CONFIG{Name: "testnet",
Network_ID: uuid.FromBytesOrNil([]byte{0x59, 0xd7, 0xf7, 0xe9, 0xdd, 0x48, 0xd5, 0xfd, 0x13, 0x0a, 0xf6, 0xe0, 0x9a, 0xec, 0xb9, 0x24}),
Public_Address_Prefix: 0x6cf58, // for dETo
Public_Address_Prefix_Integrated: 0x44f58, //for dETi
P2P_Default_Port: 28090,
RPC_Default_Port: 28091,
Genesis_Nonce: 10001,
Genesis_Block_Hash: crypto.Hash([32]byte{0x63, 0x34, 0x12, 0xde, 0x21, 0xea, 0xcb, 0xf0,
0x03, 0xe0, 0xfb, 0x9b, 0x7f, 0xcb, 0xca, 0x97,
0x6d, 0xff, 0xd4, 0x3e, 0x3f, 0x06, 0x9e, 0x55,
0xfa, 0xf1, 0xc5, 0xb4, 0x46, 0x2b, 0x59, 0x3a}),
Genesis_Tx: "" +
"02" + // version
"3c" + // unlock time
"01" + // vin length
"ff" + // vin #1
"00" + // height gen input
"01" + // vout length
"ffffffffffff07" + // output #1 amount
"02" + // output 1 type
"0bf6522f9152fa26cd1fc5c022b1a9e13dab697f3acf4b4d0ca6950a867a1943" + // output #1 key
"21" + // extra length in bytes
"01" + // extra pubkey tag
"1d92826d0656958865a035264725799f39f6988faa97d532f972895de849496d" + // tx pubkey
"00", // RCT signature none
}
// on init this variable is updated to setup global config in 1 go
//var Current_Config CHAIN_CONFIG
func init() {
//Current_Config = Mainnet // default is mainnnet
//Current_Config = Testnet // default is mainnnet
}

+ 8
- 0
config/dummy_test.go

@ -0,0 +1,8 @@
package config
import "testing"
func Test_Part1(t *testing.T){
}

+ 35
- 0
crypto/hash.go

@ -0,0 +1,35 @@
package crypto
import "fmt"
import "encoding/hex"
const (
ChecksumLength = 4 // for addresses
HashLength = 32
)
type Hash [HashLength]byte
type Checksum [ChecksumLength]byte
func (h Hash) MarshalText() ([]byte, error) {
return []byte(fmt.Sprintf("%x", h[:])), nil
}
// convert a hash of hex form to binary form, returns a zero hash if any error
// TODO this should be in crypto
func HashHexToHash(hash_hex string) (hash Hash) {
hash_raw, err := hex.DecodeString(hash_hex)
if err != nil {
//panic(fmt.Sprintf("Cannot hex decode checkpint hash \"%s\"", hash_hex))
return hash
}
if len(hash_raw) != 32 {
//panic(fmt.Sprintf(" hash not 32 byte size Cannot hex decode checkpint hash \"%s\"", hash_hex))
return hash
}
copy(hash[:], hash_raw)
return
}

+ 26
- 0
crypto/keccak.go

@ -0,0 +1,26 @@
package crypto
import "github.com/ebfe/keccak"
// quick keccak wrapper
func Keccak256(data ...[]byte) (result Hash) {
h := keccak.New256()
for _, b := range data {
h.Write(b)
}
r := h.Sum(nil)
copy(result[:], r)
return
}
func Keccak512(data ...[]byte) (result Hash) {
h := keccak.New512()
for _, b := range data {
h.Write(b)
}
r := h.Sum(nil)
copy(result[:], r)
return
}

+ 63
- 0
crypto/keccak_test.go

@ -0,0 +1,63 @@
package crypto
import "testing"
import "encoding/hex"
// convert a hex string to a key
func HexToKey(h string) (result Key) {
byteSlice, _ := hex.DecodeString(h)
if len(byteSlice) != 32 {
panic("Incorrect key size")
}
copy(result[:], byteSlice)
return
}
func HexToHash(h string) (result Hash) {
byteSlice, _ := hex.DecodeString(h)
if len(byteSlice) != 32 {
panic("Incorrect key size")
}
copy(result[:], byteSlice)
return
}
func TestKeccak256(t *testing.T) {
tests := []struct {
name string
messageHex string
wantHex string
}{
{
name: "from monero 1",
messageHex: "c8fedd380dbae40ffb52",
wantHex: "8e41962058b7422e7404253121489a3e63d186ed115086919a75105661483ba9",
},
{
name: "from monero 2",
messageHex: "5020c4d530b6ec6cb4d9",
wantHex: "8a597f11961935e32e0adeab2ce48b3df2d907c9b26619dad22f42ff65ab7593",
},
{
name: "hello",
messageHex: "68656c6c6f",
wantHex: "1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8",
},
{
name: "from monero cryptotest.pl",
messageHex: "0f3fe9c20b24a11bf4d6d1acd335c6a80543f1f0380590d7323caf1390c78e88",
wantHex: "73b7a236f2a97c4e1805f7a319f1283e3276598567757186c526caf9a49e0a92",
},
}
for _, test := range tests {
message, _ := hex.DecodeString(test.messageHex)
got := Keccak256(message)
want := HexToHash(test.wantHex)
if want != got {
t.Errorf("want %x, got %x", want, got)
}
}
}

+ 13
- 0
crypto/key.go

@ -0,0 +1,13 @@
package crypto
import "fmt"
const KeyLength = 32
// Key can be a Scalar or a Point
type Key [KeyLength]byte
func (k Key) MarshalText() ([]byte, error) {
return []byte(fmt.Sprintf("%x", k[:])), nil
}

+ 47
- 0
crypto/ringct/basic.go

@ -0,0 +1,47 @@
package ringct
import "encoding/hex"
import "github.com/deroproject/derosuite/crypto"
// convert a hex string to a key
// a copy of these functions exists in the crypto package also
func HexToKey(h string) (result Key) {
byteSlice, _ := hex.DecodeString(h)
if len(byteSlice) != 32 {
panic("Incorrect key size")
}
copy(result[:], byteSlice)
return
}
func HexToHash(h string) (result crypto.Hash) {
byteSlice, _ := hex.DecodeString(h)
if len(byteSlice) != 32 {
panic("Incorrect key size")
}
copy(result[:], byteSlice)
return
}
// zero fill the key
func Sc_0(k *Key) {
for i:=0; i < 32;i++{
k[i]=0
}
}
// RandomPubKey takes a random scalar, interprets it as a point on the curve
// and then multiplies by 8 to make it a point in the Group
// remember the low order bug and do more auditing of the entire thing
func RandomPubKey() (result *Key) {
result = new(Key)
p3 := new(ExtendedGroupElement)
var p1 ProjectiveGroupElement
var p2 CompletedGroupElement
h := RandomScalar()
p1.FromBytes(h)
GeMul8(&p2, &p1)
p2.ToExtended(p3)
p3.ToBytes(result)
return
}

+ 1490
- 0
crypto/ringct/const.go
File diff suppressed because it is too large
View File


+ 3053
- 0
crypto/ringct/edwards25519.go
File diff suppressed because it is too large
View File


+ 1614
- 0
crypto/ringct/edwards25519_test.go
File diff suppressed because it is too large
View File


+ 175
- 0
crypto/ringct/key.go

@ -0,0 +1,175 @@
package ringct
import "io"
import "crypto/rand"
import "github.com/deroproject/derosuite/crypto"
const KeyLength = 32
// Key can be a Scalar or a Point
type Key [KeyLength]byte
func (p *Key) FromBytes(b [KeyLength]byte) {
*p = b
}
func (p *Key) ToBytes() (result [KeyLength]byte) {
result = [KeyLength]byte(*p)
return
}
func (p *Key) PubKey() (pubKey *Key) {
point := new(ExtendedGroupElement)
GeScalarMultBase(point, p)
pubKey = new(Key)
point.ToBytes(pubKey)
return
}
// Creates a point on the Edwards Curve by hashing the key
func (p *Key) HashToEC() (result *ExtendedGroupElement) {
result = new(ExtendedGroupElement)
var p1 ProjectiveGroupElement
var p2 CompletedGroupElement
h := Key(crypto.Keccak256(p[:]))
p1.FromBytes(&h)
GeMul8(&p2, &p1)
p2.ToExtended(result)
return
}
func RandomScalar() (result *Key) {
result = new(Key)
var reduceFrom [KeyLength * 2]byte
tmp := make([]byte, KeyLength*2)
rand.Read(tmp)
copy(reduceFrom[:], tmp)
ScReduce(result, &reduceFrom)
return
}
func NewKeyPair() (privKey *Key, pubKey *Key) {
privKey = RandomScalar()
pubKey = privKey.PubKey()
return
}
func ParseKey(buf io.Reader) (result Key, err error) {
key := make([]byte, KeyLength)
if _, err = buf.Read(key); err != nil {
return
}
copy(result[:], key)
return
}
/*
//does a * G where a is a scalar and G is the curve basepoint
key scalarmultBase(const key & a) {
ge_p3 point;
key aG;
sc_reduce32copy(aG.bytes, a.bytes); //do this beforehand
ge_scalarmult_base(&point, aG.bytes);
ge_p3_tobytes(aG.bytes, &point);
return aG;
}
*/
//does a * G where a is a scalar and G is the curve basepoint
func ScalarmultBase(a Key) (aG Key){
reduce32copy := a
ScReduce32(&reduce32copy)
point := new(ExtendedGroupElement)
GeScalarMultBase(point, &a)
point.ToBytes(&aG)
return aG
}
// generates a key which can be used as private key or mask
// this function is similiar to RandomScalar except for reduce32, TODO can we merge both
func skGen() Key {
skey := RandomScalar()
ScReduce32(skey)
return *skey
}
func (k *Key) ToExtended() (result *ExtendedGroupElement) {
result = new(ExtendedGroupElement)
result.FromBytes(k)
return
}
func identity() (result *Key) {
result = new(Key)
result[0] = 1
return
}
// convert a uint64 to a scalar
func d2h(val uint64) (result *Key) {
result = new(Key)
for i := 0; val > 0; i++ {
result[i] = byte(val & 0xFF)
val /= 256
}
return
}
func HashToScalar(data ...[]byte) (result *Key) {
result = new(Key)
*result = Key(crypto.Keccak256(data...))
ScReduce32(result)
return
}
// multiply a scalar by H (second curve point of Pedersen Commitment)
func ScalarMultH(scalar *Key) (result *Key) {
h := new(ExtendedGroupElement)
h.FromBytes(&H)
resultPoint := new(ProjectiveGroupElement)
GeScalarMult(resultPoint, scalar, h)
result = new(Key)
resultPoint.ToBytes(result)
return
}
// add two points together
func AddKeys(sum, k1, k2 *Key) {
a := k1.ToExtended()
b := new(CachedGroupElement)
k2.ToExtended().ToCached(b)
c := new(CompletedGroupElement)
geAdd(c, a, b)
tmp := new(ExtendedGroupElement)
c.ToExtended(tmp)
tmp.ToBytes(sum)
return
}
// compute a*G + b*B
func AddKeys2(result, a, b, B *Key) {
BPoint := B.ToExtended()
RPoint := new(ProjectiveGroupElement)
GeDoubleScalarMultVartime(RPoint, b, BPoint, a)
RPoint.ToBytes(result)
return
}
// subtract two points A - B
func SubKeys(diff, k1, k2 *Key) {
a := k1.ToExtended()
b := new(CachedGroupElement)
k2.ToExtended().ToCached(b)
c := new(CompletedGroupElement)
geSub(c, a, b)
tmp := new(ExtendedGroupElement)
c.ToExtended(tmp)
tmp.ToBytes(diff)
return
}

+ 39
- 0
crypto/ringct/mlsag.go

@ -0,0 +1,39 @@
package ringct
import "fmt"
import "github.com/deroproject/derosuite/crypto"
/* This file implements MLSAG signatures for the transactions */
// get the hash of the transaction which is used to create the mlsag later on, this hash is input to MLSAG
// the hash is = hash( message + hash(basehash) + hash(pederson and borromean data))
func Get_pre_mlsag_hash(sig *RctSig) (crypto.Hash) {
message_hash := sig.Message
base_hash := crypto.Keccak256(sig.SerializeBase())
fmt.Printf("Message hash %x\n", message_hash)
fmt.Printf("Base hash %x\n", base_hash)
// now join the borromean signature and extract a sig
var other_data []byte
for i := range sig.rangeSigs {
other_data = append(other_data,sig.rangeSigs[i].asig.s0.Serialize()...)
other_data = append(other_data,sig.rangeSigs[i].asig.s1.Serialize()...)
other_data = append(other_data, sig.rangeSigs[i].ci.Serialize()...)
}
other_data_hash := crypto.Keccak256(other_data)
fmt.Printf("other hash %x\n", other_data_hash)
// join all 3 hashes and hash them again to get the data
final_data := append(message_hash[:], base_hash[:]...)
final_data = append(final_data, other_data_hash[:]...)
final_data_hash := crypto.Keccak256(final_data)
fmt.Printf("final_data_hash hash %x\n", other_data_hash)
return final_data_hash
}

+ 159
- 0
crypto/ringct/range.go

@ -0,0 +1,159 @@
package ringct
//import "fmt"
const ATOMS = 64 // 64 bit in the amount field
type bits64 [ATOMS]bool
// implementation of d2b from rctTypes.cpp
// lays out the number from lowest bit at pos 0 and highest at bit 63
func d2b_uint64_to_bits(amount uint64)(bits64){
var bits bits64
for i := 0; amount != 0; i++ {
if (amount&1) == 1 {
bits[i] = true
}
amount = amount >> 1
}
return bits
}
//ProveRange and VerifyRange
//ProveRange gives C, and mask such that \sumCi = C
// c.f. http://eprint.iacr.org/2015/1098 section 5.1
// and Ci is a commitment to either 0 or 2^i, i=0,...,63
// thus this proves that "amount" is in [0, 2^64]
// mask is a such that C = aG + bH, and b = amount
//VerifyRange verifies that \sum Ci = C and that each Ci is a commitment to 0 or 2^i
// this function proves a range using Pedersen commitment and borromean signatures
// implemented in cryptonote rctSigs.cpp
func ProveRange (C *Key, mask *Key, amount uint64) ( *RangeSig){
Sc_0(mask)
copy(C[:], (*identity())[:]) // set C to identity
var ai Key64
var Cih Key64
var sig RangeSig
bits := d2b_uint64_to_bits(amount)
//fmt.Printf("bits %+v\n", bits)
for i := 0; i < ATOMS;i++{
ai[i] = *(RandomScalar()) // grab a random key
// Sc_0(&ai[i]); // make random key zero for tesing puprpose // BUG if line is uncommented
ScReduce32(&ai[i]) // reduce it
// fmt.Printf("ai[%2d] %x\n",i, ai[i])
sig.ci[i] = ScalarmultBase(ai[i])
// fmt.Printf("ci[%2d] %x\n",i, sig.ci[i])
if bits[i] {
AddKeys(&sig.ci[i],&sig.ci[i],&H2[i])
}
SubKeys(&Cih[i],&sig.ci[i],&H2[i])
ScAdd(mask,mask,&ai[i])
AddKeys(C,C,&sig.ci[i])
}
//fmt.Print("C %x\n", *C)
// TODO caculate Borromean signature here
sig.asig = GenerateBorromean(ai, sig.ci, Cih, bits);
return &sig
}
func VerifyRange(c *Key, as RangeSig) bool {
var CiH Key64
tmp := identity()
for i := 0; i < 64; i++ {
SubKeys(&CiH[i], &as.ci[i], &H2[i])
AddKeys(tmp, tmp, &as.ci[i])
}
// fmt.Printf("C %x\n", *c)
// fmt.Printf("tmp %x\n", *tmp)
if *c != *tmp {
return false
}
//return true
return VerifyBorromean(&as.asig, &as.ci, &CiH)
}
//Borromean (c.f. gmax/andytoshi's paper)
func GenerateBorromean(x Key64, P1 Key64, P2 Key64, indices bits64) (BoroSig){
var bb BoroSig
var alpha Key64
var L [2]Key64
var c Key
var data_bytes []byte
for ii := 0; ii < ATOMS;ii++{
var naught,prime int
if indices[ii]{
naught = 1
}else{
naught = 0
}
prime = (naught+1)%2 // basically it is the inverse of naught
alpha[ii] = skGen() // generate a new random scalar
L[naught][ii] = ScalarmultBase(alpha[ii])
if naught == 0 {
bb.s1[ii] = skGen()
c = *(HashToScalar(L[naught][ii][:]))
AddKeys2(&L[prime][ii], &bb.s1[ii], &c, &P2[ii])
}
// original cryptonote does NOT clear out some unset bytes, verify whether it may be a problem for them
data_bytes = append(data_bytes, L[1][ii][:]...)
}
// take the hash of the L1 keys all 64 of them
// we have been collecting them above
bb.ee = *(HashToScalar(data_bytes));
// fmt.Printf("bb.ee %x\n", bb.ee)
var LL, cc Key
for jj := 0 ; jj < ATOMS;jj++{
if indices[jj] == false {
ScMulSub(&bb.s0[jj], &x[jj], &bb.ee, &alpha[jj])
}else{
bb.s0[jj] = skGen()
AddKeys2(&LL, &bb.s0[jj], &bb.ee, &P1[jj])
cc = *(HashToScalar(LL[:]))
ScMulSub(&bb.s1[jj], &x[jj], &cc, &alpha[jj])
}
}
return bb
}
// Verify the Borromean sig
func VerifyBorromean(b *BoroSig, p1, p2 *Key64) bool {
var data []byte
tmp, tmp2 := new(Key), new(Key)
for i := 0; i < 64; i++ {
AddKeys2(tmp, &b.s0[i], &b.ee, &p1[i])
tmp3 := HashToScalar(tmp[:])
AddKeys2(tmp2, &b.s1[i], tmp3, &p2[i])
data = append(data, tmp2[:]...)
}
computed := HashToScalar(data)
// fmt.Printf("comp %x\n", computed)
return *computed == b.ee
}

+ 23
- 0
crypto/ringct/range_test.go

@ -0,0 +1,23 @@
package ringct
import "testing"
import "math/rand"
func Test_Range_and_Borromean_Signature(t *testing.T){
var c,mask Key
for i := 0; i < 50;i++{ // test it 500 times
var amount uint64 = rand.Uint64()
sig := ProveRange(&c,&mask,amount)
if VerifyRange(&c, *sig) == false {
t.Errorf("Range Test failed")
return
}
}
}

+ 357
- 0
crypto/ringct/ringct.go

@ -0,0 +1,357 @@
package ringct
import "io"
import "fmt"
import "github.com/deroproject/derosuite/crypto"
// TODO this package need serious love of atleast few weeks
// but atleast the parser and serdes works
// we neeed to expand everthing so as chances of a bug slippping in becomes very low
// NOTE:DO NOT waste time implmenting pre-RCT code
const (
RCTTypeNull = iota
RCTTypeFull
RCTTypeSimple
)
// Pedersen Commitment is generated from this struct
// C = aG + bH where a = mask and b = amount
// senderPk is the one-time public key for ECDH exchange
type ecdhTuple struct {
mask Key
amount Key
senderPk Key
}
// Range proof commitments
type Key64 [64]Key
// Range Signature
// Essentially data for a Borromean Signature
type RangeSig struct {
asig BoroSig
ci Key64
}
// Borromean Signature
type BoroSig struct {
s0 Key64
s1 Key64
ee Key
}
// MLSAG (Multilayered Linkable Spontaneous Anonymous Group) Signature
type MlsagSig struct {
ss [][]Key
cc Key // this stores the starting point
II []Key // this stores the keyimage, but is taken from the tx,it is NOT serialized
}
// Confidential Transaction Keys, mask is Pedersen Commitment
// most of the time, it holds public keys, except where it holds private keys
type CtKey struct {
Destination Key // this is the destination and needs to expanded from blockchain
Mask Key // this is the public key mask
}
// Ring Confidential Signature parts that we have to keep
type RctSigBase struct {
sigType uint8
Message Key // transaction prefix hash
MixRing [][]CtKey // this is not serialized
pseudoOuts []Key
ecdhInfo []ecdhTuple
outPk []CtKey // only mask amount is serialized
txFee uint64
Txid crypto.Hash // this field is extra and only used for logging purposes to track which txid was at fault
}
// Ring Confidential Signature parts that we can just prune later
type RctSigPrunable struct {
rangeSigs []RangeSig
MlsagSigs []MlsagSig
}
// Ring Confidential Signature struct that can verify everything
type RctSig struct {
RctSigBase
RctSigPrunable
}
func (k *Key64) Serialize() (result []byte) {
for _, key := range k {
result = append(result, key[:]...)
}
return
}
func (b *BoroSig) Serialize() (result []byte) {
result = append(b.s0.Serialize(), b.s1.Serialize()...)
result = append(result, b.ee[:]...)
return
}
func (r *RangeSig) Serialize() (result []byte) {
result = append(r.asig.Serialize(), r.ci.Serialize()...)
return
}
func (m *MlsagSig) Serialize() (result []byte) {
for i := 0; i < len(m.ss); i++ {
for j := 0; j < len(m.ss[i]); j++ {
result = append(result, m.ss[i][j][:]...)
}
}
result = append(result, m.cc[:]...)
return
}
func (r *RctSigBase) SerializeBase() (result []byte) {
result = []byte{r.sigType}
// Null type returns right away
if r.sigType == RCTTypeNull {
return
}
result = append(result, Uint64ToBytes(r.txFee)...)
if r.sigType == RCTTypeSimple {
for _, input := range r.pseudoOuts {
result = append(result, input[:]...)
}
}
for _, ecdh := range r.ecdhInfo {
result = append(result, ecdh.mask[:]...)
result = append(result, ecdh.amount[:]...)
}
for _, ctKey := range r.outPk {
result = append(result, ctKey.Mask[:]...)
}
return
}
func (r *RctSigBase) BaseHash() (result crypto.Hash) {
result = crypto.Keccak256(r.SerializeBase())
return
}
func (r *RctSig) SerializePrunable() (result []byte) {
if r.sigType == RCTTypeNull {
return
}
for _, rangeSig := range r.rangeSigs {
result = append(result, rangeSig.Serialize()...)
}
for _, mlsagSig := range r.MlsagSigs {
result = append(result, mlsagSig.Serialize()...)
}
return
}
func (r *RctSig) Get_Sig_Type() (byte) {
return r.sigType
}
func (r *RctSig) Get_TX_Fee() (result uint64) {
if r.sigType == RCTTypeNull {
panic("RCTTypeNull cannot have TX fee")
}
return r.txFee
}
func (r *RctSig) PrunableHash() (result crypto.Hash) {
if r.sigType == RCTTypeNull {
return
}
result = crypto.Keccak256(r.SerializePrunable())
return
}
// this is the function which should be used by external world
func (r *RctSig) Verify() (result bool) {
result = false
defer func() { // safety so if anything wrong happens, verification fails
if r := recover(); r != nil {
//connection.logger.Fatalf("Recovered while Verify transaction", r)
fmt.Printf("Recovered while Verify transaction")
result = false
}}()
switch r.sigType {
case RCTTypeNull: return true /// this is only possible for miner tx
case RCTTypeFull : return r.VerifyRctFull()
case RCTTypeSimple: return r.VerifyRctSimple()
default :
return false
}
return false
}
// Verify a RCTTypeSimple RingCT Signature
func (r *RctSig) VerifyRctSimple() bool {
sumOutPks := identity()
for _, ctKey := range r.outPk {
AddKeys(sumOutPks, sumOutPks, &ctKey.Mask)
}
txFeeKey := ScalarMultH(d2h(r.txFee))
AddKeys(sumOutPks, sumOutPks, txFeeKey)
sumPseudoOuts := identity()
for _, pseudoOut := range r.pseudoOuts {
AddKeys(sumPseudoOuts, sumPseudoOuts, &pseudoOut)
}
if *sumPseudoOuts != *sumOutPks {
return false
}
for i, ctKey := range r.outPk {
if !VerifyRange(&ctKey.Mask, r.rangeSigs[i]) {
return false
}
}
// BUG BUG we are not verifying mlsag here, Do it once the core finishes
return true
}
func (r *RctSig) VerifyRctFull() bool {
for i, ctKey := range r.outPk {
if !VerifyRange(&ctKey.Mask, r.rangeSigs[i]) {
return false
}
}
// BUG BUG we are not verifying mlsag here, Do it once the core is finished
return true
}
func ParseCtKey(buf io.Reader) (result CtKey, err error) {
if result.Mask, err = ParseKey(buf); err != nil {
return
}
return
}
func ParseKey64(buf io.Reader) (result Key64, err error) {
for i := 0; i < 64; i++ {
if result[i], err = ParseKey(buf); err != nil {
return
}
}
return
}
// parse Borromean signature
func ParseBoroSig(buf io.Reader) (result BoroSig, err error) {
if result.s0, err = ParseKey64(buf); err != nil {
return
}
if result.s1, err = ParseKey64(buf); err != nil {
return
}
if result.ee, err = ParseKey(buf); err != nil {
return
}
return
}
// range data consists of Single Borromean sig and 64 keys for 64 bits
func ParseRangeSig(buf io.Reader) (result RangeSig, err error) {
if result.asig, err = ParseBoroSig(buf); err != nil {
return
}
if result.ci, err = ParseKey64(buf); err != nil {
return
}
return
}
// parser for ringct signature
// we need to be extra cautious as almost anything cam come as input
func ParseRingCtSignature(buf io.Reader, nInputs, nOutputs, nMixin int) (result *RctSig, err error) {
r := new(RctSig)
sigType := make([]byte, 1)
_, err = buf.Read(sigType)
if err != nil {
return
}
r.sigType = uint8(sigType[0])
if r.sigType == RCTTypeNull {
result = r
return
}
if r.sigType != RCTTypeFull || r.sigType != RCTTypeSimple {
err = fmt.Errorf("Bad signature Type %d", r.sigType)
}
r.txFee, err = ReadVarInt(buf)
if err != nil {
return
}
var nMg, nSS int
if r.sigType == RCTTypeSimple {
nMg = nInputs
nSS = 2
r.pseudoOuts = make([]Key, nInputs)
for i := 0; i < nInputs; i++ {
if r.pseudoOuts[i], err = ParseKey(buf); err != nil {
return
}
}
} else {
nMg = 1
nSS = nInputs + 1
}
r.ecdhInfo = make([]ecdhTuple, nOutputs)
for i := 0; i < nOutputs; i++ {
if r.ecdhInfo[i].mask, err = ParseKey(buf); err != nil {
return
}
if r.ecdhInfo[i].amount, err = ParseKey(buf); err != nil {
return
}
}
r.outPk = make([]CtKey, nOutputs)
for i := 0; i < nOutputs; i++ {
if r.outPk[i], err = ParseCtKey(buf); err != nil {
return
}
}
r.rangeSigs = make([]RangeSig, nOutputs)
for i := 0; i < nOutputs; i++ {
if r.rangeSigs[i], err = ParseRangeSig(buf); err != nil {
return
}
}
r.MlsagSigs = make([]MlsagSig, nMg)
for i := 0; i < nMg; i++ {
r.MlsagSigs[i].ss = make([][]Key, nMixin+1)
for j := 0; j < nMixin+1; j++ {
r.MlsagSigs[i].ss[j] = make([]Key, nSS)
for k := 0; k < nSS; k++ {
if r.MlsagSigs[i].ss[j][k], err = ParseKey(buf); err != nil {
return
}
}
}
if r.MlsagSigs[i].cc, err = ParseKey(buf); err != nil {
return
}
}
result = r
return
}

+ 36
- 0
crypto/ringct/varint.go

@ -0,0 +1,36 @@
package ringct
import "io"
import "fmt"
// these functions basically what golang varint does, ( however theere is a minor spec diff, so these are here for exact compatibility)
func ReadVarInt(buf io.Reader) (result uint64, err error) {
b := make([]byte, 1)
var r uint64
var n int
for i := 0; ; i++ {
n, err = buf.Read(b)
if err != nil {
return
}
if n != 1 {
err = fmt.Errorf("Buffer ended prematurely for varint")
return
}
r += (uint64(b[0]) & 0x7f) << uint(i*7)
if uint64(b[0])&0x80 == 0 {
break
}
}
result = r
return
}
func Uint64ToBytes(num uint64) (result []byte) {
for ; num >= 0x80; num >>= 7 {
result = append(result, byte((num&0x7f)|0x80))
}
result = append(result, byte(num))
return
}

+ 59
- 0
crypto/ringct/varint_test.go

@ -0,0 +1,59 @@
package ringct
import "bytes"
import "testing"
// this package needs to be verified for bug,
// just in case, the top bit is set, it is impossible to do varint 64 bit number into 8 bytes, if the number is too big
// in that case go needs 9 bytes, we should verify whether the number can ever reach there and thus place
// suitable checks to avoid falling into the trap later on
func TestVarInt(t *testing.T) {
tests := []struct {
name string
varInt []byte
want uint64
}{
{
name: "1 byte",
varInt: []byte{0x01},
want: 1,
},
{
name: "3 bytes",
varInt: []byte{0x8f, 0xd6, 0x17},
want: 387855,
},
{
name: "4 bytes",
varInt: []byte{0x80, 0x92, 0xf4, 0x01},
want: 4000000,
},
{
name: "7 bytes",
varInt: []byte{0x80, 0xc0, 0xca, 0xf3, 0x84, 0xa3, 0x02},
want: 10000000000000,
},
}
var got uint64
var err error
var gotVarInt []byte
buf := new(bytes.Buffer)
for _, test := range tests {
gotVarInt = Uint64ToBytes(test.want)
if bytes.Compare(gotVarInt, test.varInt) != 0 {
t.Errorf("%s: varint want %x, got %x", test.name, test.varInt, gotVarInt)
continue
}
buf.Reset()
buf.Write(test.varInt)
got, err = ReadVarInt(buf)
if err != nil {
t.Errorf("%s: %s", test.name, err)
continue
}
if test.want != got {
t.Errorf("%s: want %d, got %d", test.name, test.want, got)
continue
}
}
}

+ 526
- 0
cryptonight/cryptonight.go

@ -0,0 +1,526 @@
package cryptonight
import "fmt"
import "unsafe"
import "encoding/hex"
import "encoding/binary"
import "github.com/aead/skein"
import "github.com/dchest/blake256"
const MAX_ARRAY_LIMIT = (4 * 1024 * 1024)
func cryptonight(input []byte) []byte {
var dummy [256]byte
var S [25]uint64
var key1 [64]uint32
var key2 [64]uint32
var a [2]uint64
var b [2]uint64
var c [2]uint64
a_uint32 := (*(*[4]uint32)(unsafe.Pointer(&a[0])))[:len(a)*2]
//b_uint32 := (*(*[4]uint32)(unsafe.Pointer(&b[0])))[:len(b)*2]
c_uint32 := (*(*[4]uint32)(unsafe.Pointer(&c[0])))[:len(c)*2]
// same array is accessed as 3 different ways, buts it bettter than copying
var ScratchPad = make([]uint64, 1<<18, 1<<18)
ScratchPad_uint32 := (*(*[MAX_ARRAY_LIMIT]uint32)(unsafe.Pointer(&ScratchPad[0])))[:len(ScratchPad)*2]
// ScratchPad_byte := (*(*[MAX_ARRAY_LIMIT]byte)(unsafe.Pointer(&ScratchPad[0])))[:len(ScratchPad)*8]
copy(dummy[:], input)
for i := 0; i < 16; i++ {
S[i] = binary.LittleEndian.Uint64(dummy[i<<3:])
}
S[16] = 0x8000000000000000
keccakf(&S)
// lets convert everything back to bytes
for i := 0; i < 25; i++ {
binary.LittleEndian.PutUint64(dummy[i<<3:], S[i])
}
// extract keys
/*for i := 0 ; i <8;i++{
key1[i] = binary.LittleEndian.Uint32(dummy[i<<2:])
key2[i] = binary.LittleEndian.Uint32(dummy[32+(i<<2):])
}*/
expandKeyGo(dummy[0:32], key1[:], nil)
expandKeyGo(dummy[32:64], key2[:], nil)
/* for i :=0; i< 60;i++{
fmt.Printf("%2d %X\n", i, key2[i])
}*/
// var text [128]byte
var text_uint32 [32]uint32
//copy(text[:],dummy[64:64+128]) // copy 128 bytes
for i := 0; i < 32; i++ {
text_uint32[i] = binary.LittleEndian.Uint32(dummy[64+(i<<2):])
}
/* for i :=0; i< 32;i++{
fmt.Printf("%2d %X i %08X %08X\n", i, text_uint32[i] , key1[i],key1[i<<1])
}*/
for i := 0; i < 0x4000; i++ {
for j := 0; j < 8; j++ {
CNAESTransform((text_uint32[(j << 2):]), key1[:])
}
//memcpy(CNCtx.Scratchpad + (i << 4), text, 128);
copy(ScratchPad_uint32[i*32:], text_uint32[:])
}
a[0] = S[0] ^ S[4]
a[1] = S[1] ^ S[5]
b[0] = S[2] ^ S[6]
b[1] = S[3] ^ S[7]
for i := 0; i < 0x80000; i++ {
// {
c[0] = ScratchPad[((a[0]&0x1FFFF0)>>3)+0]
c[1] = ScratchPad[((a[0]&0x1FFFF0)>>3)+1]
CNAESRnd(c_uint32, a_uint32)
b[0] ^= c[0]
b[1] ^= c[1]
ScratchPad[((a[0]&0x1FFFF0)>>3)+0] = b[0]
ScratchPad[((a[0]&0x1FFFF0)>>3)+1] = b[1]
b[0] = ScratchPad[((c[0]&0x1FFFF0)>>3)+0]
b[1] = ScratchPad[((c[0]&0x1FFFF0)>>3)+1]
// time to do 64 bit * 64 bit multiply
var lower64, upper64 uint64
{
x := c[0]
y := b[0]
a := x >> 32
b := x & 0xffffffff
c := y >> 32
d := y & 0xffffffff
ac := a * c
bc := b * c
ad := a * d
bd := b * d
mid34 := (bd >> 32) + (bc & 0xffffffff) + (ad & 0xffffffff)
upper64 = ac + (bc >> 32) + (ad >> 32) + (mid34 >> 32)
lower64 = (mid34 << 32) | (bd & 0xffffffff)
_ = lower64
_ = upper64
}
a[1] += lower64
a[0] += upper64
ScratchPad[((c[0]&0x1FFFF0)>>3)+0] = a[0]
ScratchPad[((c[0]&0x1FFFF0)>>3)+1] = a[1]
a[0] ^= b[0]
a[1] ^= b[1]
b[0] = c[0]
b[1] = c[1]
}
// fmt.Printf(" a %X %X\n", a[0],a[1]);
// fmt.Printf(" b %X %X\n", b[0],b[1]);
for i := 0; i < 32; i++ {
text_uint32[i] = binary.LittleEndian.Uint32(dummy[64+(i<<2):])
}
for i := 0; i < 0x4000; i++ {
for j := 0; j < 32; j++ {
text_uint32[j] ^= ScratchPad_uint32[(i*32)+j]
}
for j := 0; j < 8; j++ {
CNAESTransform((text_uint32[(j << 2):]), key2[:])
}
}
/*for i :=0; i< 32;i++{
fmt.Printf("%2d %X\n", i, text_uint32[i])
}*/
for i := 0; i < 32; i++ {
binary.LittleEndian.PutUint32(dummy[64+(i<<2):], text_uint32[i])
}
for i := 8; i < 25; i++ {
S[i] = binary.LittleEndian.Uint64(dummy[i<<3:])
}
keccakf(&S) // do the keccak round
/* for i :=0; i< 25;i++{
fmt.Printf("S %02d %X\n", i, S[i])
}*/
// lets convert everything back to bytes
for i := 0; i < 25; i++ {
binary.LittleEndian.PutUint64(dummy[i<<3:], S[i])
}
var resulthash []byte
switch S[0] & 3 {
case 0:
// fmt.Printf("blake\n")
// blake
blakehash := blake256.New()
blakehash.Write(dummy[:200])
resulthash = blakehash.Sum(nil) //matching
case 1: // groestl
// fmt.Printf("groestl not implemented\n")
var output [32]byte
var input = dummy[:200]
crypto_hash(output[:], input, uint64(len(input)))
resulthash = output[:]
case 2: // jh
//fmt.Printf("jh not implemented\n")
myjhash := NewJhash256()
myjhash.Write(dummy[:200])
resulthash = myjhash.Sum(nil)
case 3: // skein
// fmt.Printf("skein\n")
skeinhash := skein.New256(nil)
skeinhash.Write(dummy[:200])
resulthash = skeinhash.Sum(nil) //matchin
}
//fmt.Printf("result hash %x\n", resulthash)
return resulthash
}
/* from original cryptonote source
0100000000000000000000000000000000000000000000000000000000000000000000102700005a18d9489bcd353aeaf4a19323d04e90353f98f0d7cc2a030cfd76e19495547d01
a73bd37aba3454776b40733854a8349fe6359eb2c91d93bc727c69431c1d1f95hash of blob
// from our implementation
Get long hash a73bd37aba3454776b40733854a8349fe6359eb2c91d93bc727c69431c1d1f95
*/
func SlowHash(msg []byte) []byte {
hash := cryptonight(append(msg, byte(0x01)))
// hash := cryptonight(msg)
return hash
}
func main() {
fmt.Printf("Hello World\n")
// convert initial input to first 15 uint64
var msg = []byte("This is a testi" + "\x01") // append final 1 blake
// var msg = []byte("This is a test" + "\x01") // append final 1 groestl
// var msg = []byte("This is a test2" + "\x01") // append final 1 jh hash
//var msg = []byte("This is a testw" + "\x01") // append final 1 skein
hash := cryptonight(msg)
fmt.Printf("hash in hex %s\n", hex.EncodeToString(hash))
//keccak := New256()
//keccak.Sum([]byte("This is a test"));
/* var JH256_H0 = [128]uint8{0xeb,0x98,0xa3,0x41,0x2c,0x20,0xd3,0xeb,0x92,0xcd,0xbe,0x7b,0x9c,0xb2,0x45,0xc1,0x1c,0x93,0x51,0x91,0x60,0xd4,0xc7,0xfa,0x26,0x0,0x82,0xd6,0x7e,0x50,0x8a,0x3,0xa4,0x23,0x9e,0x26,0x77,0x26,0xb9,0x45,0xe0,0xfb,0x1a,0x48,0xd4,0x1a,0x94,0x77,0xcd,0xb5,0xab,0x26,0x2,0x6b,0x17,0x7a,0x56,0xf0,0x24,0x42,0xf,0xff,0x2f,0xa8,0x71,0xa3,0x96,0x89,0x7f,0x2e,0x4d,0x75,0x1d,0x14,0x49,0x8,0xf7,0x7d,0xe2,0x62,0x27,0x76,0x95,0xf7,0x76,0x24,0x8f,0x94,0x87,0xd5,0xb6,0x57,0x47,0x80,0x29,0x6c,0x5c,0x5e,0x27,0x2d,0xac,0x8e,0xd,0x6c,0x51,0x84,0x50,0xc6,0x57,0x5,0x7a,0xf,0x7b,0xe4,0xd3,0x67,0x70,0x24,0x12,0xea,0x89,0xe3,0xab,0x13,0xd3,0x1c,0xd7,0x69}
for i := 0 ; i <16;i++{
// fmt.Printf("%d %X\n",i, binary.LittleEndian.Uint64(JH256_H0[i<<3:]))
}*/
var output [32]byte
var input = []byte("hello")
crypto_hash(output[:], input, uint64(len(input)))
fmt.Printf("Output GROEestl hash %x\n", output)
}
// Rotate
func ROTL32(x uint32, y uint32) uint32 { return (((x) << (y)) | ((x) >> (32 - (y)))) }
func BYTE(x, y uint32) uint32 { return (((x) >> ((y) << 3)) & 0xFF) }
func SubWord(inw uint32) uint32 {
return ((CNAESSbox[BYTE(inw, 3)] << 24) | (CNAESSbox[BYTE(inw, 2)] << 16) | (CNAESSbox[BYTE(inw, 1)] << 8) | CNAESSbox[BYTE(inw, 0)])
}
/*
func AESExpandKey256(keybuf []uint32){
i := uint32(1)
t := uint32(0)
for c := 8 ; c < 60; c++ {
// For 256-bit keys, an sbox permutation is done every other 4th uint generated, AND every 8th
// t := ((!(c & 3))) ? SubWord(keybuf[c - 1]) : keybuf[c - 1];
if (c & 3) == 0 {
t = SubWord(keybuf[c - 1])
}else{
t = keybuf[c - 1];
}
// If the uint we're generating has an index that is a multiple of 8, rotate and XOR with the round constant,
// then XOR this with previously generated uint. If it's 4 after a multiple of 8, only the sbox permutation
// is done, followed by the XOR. If neither are true, only the XOR with the previously generated uint is done.
//keybuf[c] = keybuf[c - 8] ^ ((!(c & 7)) ? ROTL32(t, 24U) ^ ((uint32_t)(CNAESRcon[i++])) : t);
if (keybuf[c - 8] ^ ((!(c & 7))) > 0) {
keybuf[c] = ROTL32(t, 24) ^ ((uint32)(CNAESRcon[i]))
i++
}else{
keybuf[c] = t;
}
}
}
*/
func CNAESTransform(X, Key []uint32) {
// fmt.Printf("X %08X %08X \n", X[0],X[1]) ;
for i := uint32(0); i < 10; i++ {
CNAESRnd(X, Key[(i<<2):])
}
}
func CNAESRnd(X, key []uint32) {
var Y [4]uint32
Y[0] = CNAESTbl[BYTE(X[0], 0)] ^ ROTL32(CNAESTbl[BYTE(X[1], 1)], 8) ^ ROTL32(CNAESTbl[BYTE(X[2], 2)], 16) ^ ROTL32(CNAESTbl[BYTE(X[3], 3)], 24)
Y[1] = CNAESTbl[BYTE(X[1], 0)] ^ ROTL32(CNAESTbl[BYTE(X[2], 1)], 8) ^ ROTL32(CNAESTbl[BYTE(X[3], 2)], 16) ^ ROTL32(CNAESTbl[BYTE(X[0], 3)], 24)
Y[2] = CNAESTbl[BYTE(X[2], 0)] ^ ROTL32(CNAESTbl[BYTE(X[3], 1)], 8) ^ ROTL32(CNAESTbl[BYTE(X[0], 2)], 16) ^ ROTL32(CNAESTbl[BYTE(X[1], 3)], 24)
Y[3] = CNAESTbl[BYTE(X[3], 0)] ^ ROTL32(CNAESTbl[BYTE(X[0], 1)], 8) ^ ROTL32(CNAESTbl[BYTE(X[1], 2)], 16) ^ ROTL32(CNAESTbl[BYTE(X[2], 3)], 24)
for i := 0; i < 4; i++ {
X[i] = Y[i] ^ key[i]
}
}
// copied from https://golang.org/src/crypto/aes/block.go
// Apply sbox0 to each byte in w.
func subw(w uint32) uint32 {
return uint32(sbox0[w>>24])<<24 |
uint32(sbox0[w>>16&0xff])<<16 |
uint32(sbox0[w>>8&0xff])<<8 |
uint32(sbox0[w&0xff])
}
// Rotate
func rotw(w uint32) uint32 { return w<<8 | w>>24 }
func swap_uint32(val uint32) uint32 {
val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF)
return (val << 16) | (val >> 16)
}
// Key expansion algorithm. See FIPS-197, Figure 11.
// Their rcon[i] is our powx[i-1] << 24.
func expandKeyGo(key []byte, enc, dec []uint32) {
// Encryption key setup.
var i int
nk := len(key) / 4
for i = 0; i < nk; i++ {
enc[i] = uint32(key[4*i])<<24 | uint32(key[4*i+1])<<16 | uint32(key[4*i+2])<<8 | uint32(key[4*i+3])
}
for ; i < len(enc); i++ {
t := enc[i-1]
if i%nk == 0 {
t = subw(rotw(t)) ^ (uint32(powx[i/nk-1]) << 24)
} else if nk > 6 && i%nk == 4 {
t = subw(t)
}
enc[i] = enc[i-nk] ^ t
// fmt.Printf("%2d %X\n", i, enc[i])
}
// key generated by golang need to be swapped
for i := 0; i < 60; i++ {
enc[i] = swap_uint32(enc[i])
//fmt.Printf("%2d %X\n", i, enc[i])
}
/*
// Derive decryption key from encryption key.
// Reverse the 4-word round key sets from enc to produce dec.
// All sets but the first and last get the MixColumn transform applied.
if dec == nil {
return
}
n := len(enc)
for i := 0; i < n; i += 4 {
ei := n - i - 4
for j := 0; j < 4; j++ {
x := enc[ei+j]
if i > 0 && i+4 < n {
x = td0[sbox0[x>>24]] ^ td1[sbox0[x>>16&0xff]] ^ td2[sbox0[x>>8&0xff]] ^ td3[sbox0[x&0xff]]
}
dec[i+j] = x
}
}
*/
}
// copied from https://golang.org/src/crypto/aes/const.go
// Powers of x mod poly in GF(2).
var powx = [16]byte{
0x01,
0x02,
0x04,
0x08,
0x10,
0x20,
0x40,
0x80,
0x1b,
0x36,
0x6c,
0xd8,
0xab,
0x4d,
0x9a,
0x2f,
}
// FIPS-197 Figure 7. S-box substitution values in hexadecimal format.
var sbox0 = [256]byte{
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
}
var CNAESSbox = [256]uint32{
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16,
}
var CNAESRcon = [8]uint32{0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40}
var CNAESTbl = [256]uint32{0xA56363C6, 0x847C7CF8, 0x997777EE, 0x8D7B7BF6,
0x0DF2F2FF, 0xBD6B6BD6, 0xB16F6FDE, 0x54C5C591,
0x50303060, 0x03010102, 0xA96767CE, 0x7D2B2B56,
0x19FEFEE7, 0x62D7D7B5, 0xE6ABAB4D, 0x9A7676EC,
0x45CACA8F, 0x9D82821F, 0x40C9C989, 0x877D7DFA,
0x15FAFAEF, 0xEB5959B2, 0xC947478E, 0x0BF0F0FB,
0xECADAD41, 0x67D4D4B3, 0xFDA2A25F, 0xEAAFAF45,
0xBF9C9C23, 0xF7A4A453, 0x967272E4, 0x5BC0C09B,
0xC2B7B775, 0x1CFDFDE1, 0xAE93933D, 0x6A26264C,
0x5A36366C, 0x413F3F7E, 0x02F7F7F5, 0x4FCCCC83,
0x5C343468, 0xF4A5A551, 0x34E5E5D1, 0x08F1F1F9,
0x937171E2, 0x73D8D8AB, 0x53313162, 0x3F15152A,
0x0C040408, 0x52C7C795, 0x65232346, 0x5EC3C39D,
0x28181830, 0xA1969637, 0x0F05050A, 0xB59A9A2F,
0x0907070E, 0x36121224, 0x9B80801B, 0x3DE2E2DF,
0x26EBEBCD, 0x6927274E, 0xCDB2B27F, 0x9F7575EA,
0x1B090912, 0x9E83831D, 0x742C2C58, 0x2E1A1A34,
0x2D1B1B36, 0xB26E6EDC, 0xEE5A5AB4, 0xFBA0A05B,
0xF65252A4, 0x4D3B3B76, 0x61D6D6B7, 0xCEB3B37D,
0x7B292952, 0x3EE3E3DD, 0x712F2F5E, 0x97848413,
0xF55353A6, 0x68D1D1B9, 0x00000000, 0x2CEDEDC1,
0x60202040, 0x1FFCFCE3, 0xC8B1B179, 0xED5B5BB6,
0xBE6A6AD4, 0x46CBCB8D, 0xD9BEBE67, 0x4B393972,
0xDE4A4A94, 0xD44C4C98, 0xE85858B0, 0x4ACFCF85,
0x6BD0D0BB, 0x2AEFEFC5, 0xE5AAAA4F, 0x16FBFBED,
0xC5434386, 0xD74D4D9A, 0x55333366, 0x94858511,
0xCF45458A, 0x10F9F9E9, 0x06020204, 0x817F7FFE,
0xF05050A0, 0x443C3C78, 0xBA9F9F25, 0xE3A8A84B,
0xF35151A2, 0xFEA3A35D, 0xC0404080, 0x8A8F8F05,
0xAD92923F, 0xBC9D9D21, 0x48383870, 0x04F5F5F1,
0xDFBCBC63, 0xC1B6B677, 0x75DADAAF, 0x63212142,
0x30101020, 0x1AFFFFE5, 0x0EF3F3FD, 0x6DD2D2BF,
0x4CCDCD81, 0x140C0C18, 0x35131326, 0x2FECECC3,
0xE15F5FBE, 0xA2979735, 0xCC444488, 0x3917172E,
0x57C4C493, 0xF2A7A755, 0x827E7EFC, 0x473D3D7A,
0xAC6464C8, 0xE75D5DBA, 0x2B191932, 0x957373E6,
0xA06060C0, 0x98818119, 0xD14F4F9E, 0x7FDCDCA3,
0x66222244, 0x7E2A2A54, 0xAB90903B, 0x8388880B,
0xCA46468C, 0x29EEEEC7, 0xD3B8B86B, 0x3C141428,
0x79DEDEA7, 0xE25E5EBC, 0x1D0B0B16, 0x76DBDBAD,
0x3BE0E0DB, 0x56323264, 0x4E3A3A74, 0x1E0A0A14,
0xDB494992, 0x0A06060C, 0x6C242448, 0xE45C5CB8,
0x5DC2C29F, 0x6ED3D3BD, 0xEFACAC43, 0xA66262C4,
0xA8919139, 0xA4959531, 0x37E4E4D3, 0x8B7979F2,
0x32E7E7D5, 0x43C8C88B, 0x5937376E, 0xB76D6DDA,
0x8C8D8D01, 0x64D5D5B1, 0xD24E4E9C, 0xE0A9A949,
0xB46C6CD8, 0xFA5656AC, 0x07F4F4F3, 0x25EAEACF,
0xAF6565CA, 0x8E7A7AF4, 0xE9AEAE47, 0x18080810,
0xD5BABA6F, 0x887878F0, 0x6F25254A, 0x722E2E5C,
0x241C1C38, 0xF1A6A657, 0xC7B4B473, 0x51C6C697,
0x23E8E8CB, 0x7CDDDDA1, 0x9C7474E8, 0x211F1F3E,
0xDD4B4B96, 0xDCBDBD61, 0x868B8B0D, 0x858A8A0F,
0x907070E0, 0x423E3E7C, 0xC4B5B571, 0xAA6666CC,
0xD8484890, 0x05030306, 0x01F6F6F7, 0x120E0E1C,
0xA36161C2, 0x5F35356A, 0xF95757AE, 0xD0B9B969,
0x91868617, 0x58C1C199, 0x271D1D3A, 0xB99E9E27,
0x38E1E1D9, 0x13F8F8EB, 0xB398982B, 0x33111122,
0xBB6969D2, 0x70D9D9A9, 0x898E8E07, 0xA7949433,
0xB69B9B2D, 0x221E1E3C, 0x92878715, 0x20E9E9C9,
0x49CECE87, 0xFF5555AA, 0x78282850, 0x7ADFDFA5,
0x8F8C8C03, 0xF8A1A159, 0x80898909, 0x170D0D1A,
0xDABFBF65, 0x31E6E6D7, 0xC6424284, 0xB86868D0,
0xC3414182, 0xB0999929, 0x772D2D5A, 0x110F0F1E,
0xCBB0B07B, 0xFC5454A8, 0xD6BBBB6D, 0x3A16162C,
}

+ 44
- 0
cryptonight/cryptonight_test.go

@ -0,0 +1,44 @@
package cryptonight
import "testing"
import "encoding/hex"
func Test_Cryptonight_Hash(t *testing.T) {
// there are 4 sub algorithms, basically blake , groestl , jh , skein
// other things are common, these tect vectors have been manually pulled from c codes
blake_hash := cryptonight([]byte("This is a testi" + "\x01"))
if hex.EncodeToString(blake_hash) != "7958d1afe0c46670642c0341f92e89bf6de6a2573ef89742237162e66ea4a121" {
t.Error("Cryptonight blake_hash testing Failed\n")
return
}
// this is from cryptonote whitepaper
groestl_hash := cryptonight([]byte("This is a test" + "\x01"))
if hex.EncodeToString(groestl_hash) != "a084f01d1437a09c6985401b60d43554ae105802c5f5d8a9b3253649c0be6605" {
t.Error("Cryptonight testing Failed\n")
return
}
jh_hash := cryptonight([]byte("This is a test2" + "\x01"))
if hex.EncodeToString(jh_hash) != "6f93b51852d1a47277c62e720bf0e10bf90e92123847be246f67e3fd2639f4b4" {
t.Error("Cryptonight testing Failed\n")
return
}
skein_hash := cryptonight([]byte("This is a testw" + "\x01"))
if hex.EncodeToString(skein_hash) != "3174ef437b24fd30e81d307d9b7d02ba21eb6f627cafc9d8134ea63adc4985b0" {
t.Error("Cryptonight testing Failed\n")
return
}
}

+ 416
- 0
cryptonight/hash.go

@ -0,0 +1,416 @@
package cryptonight
type size_t uint32
type __locale_data struct {
}
type __locale_t struct {
__locales [][]__locale_data
__ctype_b []uint16
__ctype_tolower []int
__ctype_toupper []int
__names [][]byte
}
type locale_t struct {
__locales [][]__locale_data
__ctype_b []uint16
__ctype_tolower []int
__ctype_toupper []int
__names [][]byte
}
type ptrdiff_t int32
type wchar_t int
type max_align_t struct {
__clang_max_align_nonce1 int64
__clang_max_align_nonce2 float64
}
var sbox []uint8 = []uint8{99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22}
var mul2 []uint8 = []uint8{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222, 224, 226, 228, 230, 232, 234, 236, 238, 240, 242, 244, 246, 248, 250, 252, 254, 27, 25, 31, 29, 19, 17, 23, 21, 11, 9, 15, 13, 3, 1, 7, 5, 59, 57, 63, 61, 51, 49, 55, 53, 43, 41, 47, 45, 35, 33, 39, 37, 91, 89, 95, 93, 83, 81, 87, 85, 75, 73, 79, 77, 67, 65, 71, 69, 123, 121, 127, 125, 115, 113, 119, 117, 107, 105, 111, 109, 99, 97, 103, 101, 155, 153, 159, 157, 147, 145, 151, 149, 139, 137, 143, 141, 131, 129, 135, 133, 187, 185, 191, 189, 179, 177, 183, 181, 171, 169, 175, 173, 163, 161, 167, 165, 219, 217, 223, 221, 211, 209, 215, 213, 203, 201, 207, 205, 195, 193, 199, 197, 251, 249, 255, 253, 243, 241, 247, 245, 235, 233, 239, 237, 227, 225, 231, 229}
func mix_bytes(i0 uint8, i1 uint8, i2 uint8, i3 uint8, i4 uint8, i5 uint8, i6 uint8, i7 uint8, output []uint8) {
var t0 uint8
var t1 uint8
var t2 uint8
var t3 uint8
var t4 uint8
var t5 uint8
var t6 uint8
var t7 uint8
var x0 uint8
var x1 uint8
var x2 uint8
var x3 uint8
var x4 uint8
var x5 uint8
var x6 uint8
var x7 uint8
var y0 uint8
var y1 uint8
var y2 uint8
var y3 uint8
var y4 uint8
var y5 uint8
var y6 uint8
var y7 uint8
t0 = i0 ^ i1
t1 = i1 ^ i2
t2 = i2 ^ i3
t3 = i3 ^ i4
t4 = i4 ^ i5
t5 = i5 ^ i6
t6 = i6 ^ i7
t7 = i7 ^ i0
x0 = t0 ^ t3
x1 = t1 ^ t4
x2 = t2 ^ t5
x3 = t3 ^ t6
x4 = t4 ^ t7
x5 = t5 ^ t0
x6 = t6 ^ t1
x7 = t7 ^ t2
y0 = t0 ^ t2 ^ i6
y1 = t1 ^ t3 ^ i7
y2 = t2 ^ t4 ^ i0
y3 = t3 ^ t5 ^ i1
y4 = t4 ^ t6 ^ i2
y5 = t5 ^ t7 ^ i3
y6 = t6 ^ t0 ^ i4
y7 = t7 ^ t1 ^ i5
x3 = func() uint8 {
if (x3 & 128) != 0 {
return (x3 << uint64(1)) ^ 27
} else {
return (x3 << uint64(1))
}
}()
x0 = func() uint8 {
if (x0 & 128) != 0 {
return (x0 << uint64(1)) ^ 27
} else {
return (x0 << uint64(1))
}
}()
t0 = x3 ^ y7
t0 = func() uint8 {
if (t0 & 128) != 0 {
return (t0 << uint64(1)) ^ 27
} else {
return (t0 << uint64(1))
}
}()
t5 = x0 ^ y4
t5 = func() uint8 {
if (t5 & 128) != 0 {
return (t5 << uint64(1)) ^ 27
} else {
return (t5 << uint64(1))
}
}()
output[0] = t0 ^ y4
output[5] = t5 ^ y1
output[1] = mul2[mul2[x4]^y0] ^ y5
output[2] = mul2[mul2[x5]^y1] ^ y6
output[3] = mul2[mul2[x6]^y2] ^ y7
output[4] = mul2[mul2[x7]^y3] ^ y0
output[6] = mul2[mul2[x1]^y5] ^ y2
output[7] = mul2[mul2[x2]^y6] ^ y3
}
func perm_P(input []uint8, output []uint8) {
var r0 uint8
var r1 uint8
var r2 uint8
var r3 uint8
var r4 uint8
var r5 uint8
var r6 uint8
var r7 uint8
var round uint8
var state []uint8 = make([]uint8, 64, 64)
var write []uint8 = state
var read []uint8 = input
var p_tmp []uint8
for {
break
}
{
for round = uint8(0); round < 10; func() uint8 {
round += 1
return round
}() {
r0 = sbox[read[0]^round]
r1 = sbox[read[9]]
r2 = sbox[read[18]]
r3 = sbox[read[27]]
r4 = sbox[read[36]]
r5 = sbox[read[45]]
r6 = sbox[read[54]]
r7 = sbox[read[63]]
mix_bytes(r0, r1, r2, r3, r4, r5, r6, r7, write)
r0 = sbox[read[8]^round^16]
r1 = sbox[read[17]]
r2 = sbox[read[26]]
r3 = sbox[read[35]]
r4 = sbox[read[44]]
r5 = sbox[read[53]]
r6 = sbox[read[62]]
r7 = sbox[read[7]]
mix_bytes(r0, r1, r2, r3, r4, r5, r6, r7, write[8:])
r0 = sbox[read[16]^round^32]
r1 = sbox[read[25]]
r2 = sbox[read[34]]
r3 = sbox[read[43]]
r4 = sbox[read[52]]
r5 = sbox[read[61]]
r6 = sbox[read[6]]
r7 = sbox[read[15]]
mix_bytes(r0, r1, r2, r3, r4, r5, r6, r7, write[16:])
r0 = sbox[read[24]^round^48]
r1 = sbox[read[33]]
r2 = sbox[read[42]]
r3 = sbox[read[51]]
r4 = sbox[read[60]]
r5 = sbox[read[5]]
r6 = sbox[read[14]]
r7 = sbox[read[23]]
mix_bytes(r0, r1, r2, r3, r4, r5, r6, r7, write[24:])
r0 = sbox[read[32]^round^64]
r1 = sbox[read[41]]
r2 = sbox[read[50]]
r3 = sbox[read[59]]
r4 = sbox[read[4]]
r5 = sbox[read[13]]
r6 = sbox[read[22]]
r7 = sbox[read[31]]
mix_bytes(r0, r1, r2, r3, r4, r5, r6, r7, write[32:])
r0 = sbox[read[40]^round^80]
r1 = sbox[read[49]]
r2 = sbox[read[58]]
r3 = sbox[read[3]]
r4 = sbox[read[12]]
r5 = sbox[read[21]]
r6 = sbox[read[30]]
r7 = sbox[read[39]]
mix_bytes(r0, r1, r2, r3, r4, r5, r6, r7, write[40:])
r0 = sbox[read[48]^round^96]
r1 = sbox[read[57]]
r2 = sbox[read[2]]
r3 = sbox[read[11]]
r4 = sbox[read[20]]
r5 = sbox[read[29]]
r6 = sbox[read[38]]
r7 = sbox[read[47]]
mix_bytes(r0, r1, r2, r3, r4, r5, r6, r7, write[48:])
r0 = sbox[read[56]^round^112]
r1 = sbox[read[1]]
r2 = sbox[read[10]]
r3 = sbox[read[19]]
r4 = sbox[read[28]]
r5 = sbox[read[37]]
r6 = sbox[read[46]]
r7 = sbox[read[55]]
mix_bytes(r0, r1, r2, r3, r4, r5, r6, r7, write[56:])
if round == uint8(0) {
read = output
}
p_tmp = read
read = write
write = p_tmp
}
}
}
func perm_Q(input []uint8, output []uint8) {
var r0 uint8
var r1 uint8
var r2 uint8
var r3 uint8
var r4 uint8
var r5 uint8
var r6 uint8
var r7 uint8
var round uint8
var state []uint8 = make([]uint8, 64, 64)
var write []uint8 = state
var read []uint8 = input
var p_tmp []uint8
for {
break
}
{
for round = uint8(0); round < 10; func() uint8 {
round += 1
return round
}() {
r0 = sbox[read[8]^255]
r1 = sbox[read[25]^255]
r2 = sbox[read[42]^255]
r3 = sbox[read[59]^255]
r4 = sbox[read[4]^255]
r5 = sbox[read[21]^255]
r6 = sbox[read[38]^255]
r7 = sbox[read[55]^159^round]
mix_bytes(r0, r1, r2, r3, r4, r5, r6, r7, write)
r0 = sbox[read[16]^255]
r1 = sbox[read[33]^255]
r2 = sbox[read[50]^255]
r3 = sbox[read[3]^255]
r4 = sbox[read[12]^255]
r5 = sbox[read[29]^255]
r6 = sbox[read[46]^255]
r7 = sbox[read[63]^143^round]
mix_bytes(r0, r1, r2, r3, r4, r5, r6, r7, write[8:])
r0 = sbox[read[24]^255]
r1 = sbox[read[41]^255]
r2 = sbox[read[58]^255]
r3 = sbox[read[11]^255]
r4 = sbox[read[20]^255]
r5 = sbox[read[37]^255]
r6 = sbox[read[54]^255]
r7 = sbox[read[7]^255^round]
mix_bytes(r0, r1, r2, r3, r4, r5, r6, r7, write[16:])
r0 = sbox[read[32]^255]
r1 = sbox[read[49]^255]
r2 = sbox[read[2]^255]
r3 = sbox[read[19]^255]
r4 = sbox[read[28]^255]
r5 = sbox[read[45]^255]
r6 = sbox[read[62]^255]
r7 = sbox[read[15]^239^round]
mix_bytes(r0, r1, r2, r3, r4, r5, r6, r7, write[24:])
r0 = sbox[read[40]^255]
r1 = sbox[read[57]^255]
r2 = sbox[read[10]^255]
r3 = sbox[read[27]^255]
r4 = sbox[read[36]^255]
r5 = sbox[read[53]^255]
r6 = sbox[read[6]^255]
r7 = sbox[read[23]^223^round]
mix_bytes(r0, r1, r2, r3, r4, r5, r6, r7, write[32:])
r0 = sbox[read[48]^255]
r1 = sbox[read[1]^255]
r2 = sbox[read[18]^255]
r3 = sbox[read[35]^255]
r4 = sbox[read[44]^255]
r5 = sbox[read[61]^255]
r6 = sbox[read[14]^255]
r7 = sbox[read[31]^207^round]
mix_bytes(r0, r1, r2, r3, r4, r5, r6, r7, write[40:])
r0 = sbox[read[56]^255]
r1 = sbox[read[9]^255]
r2 = sbox[read[26]^255]
r3 = sbox[read[43]^255]
r4 = sbox[read[52]^255]
r5 = sbox[read[5]^255]
r6 = sbox[read[22]^255]
r7 = sbox[read[39]^191^round]
mix_bytes(r0, r1, r2, r3, r4, r5, r6, r7, write[48:])
r0 = sbox[read[0]^255]
r1 = sbox[read[17]^255]
r2 = sbox[read[34]^255]
r3 = sbox[read[51]^255]
r4 = sbox[read[60]^255]
r5 = sbox[read[13]^255]
r6 = sbox[read[30]^255]
r7 = sbox[read[47]^175^round]
mix_bytes(r0, r1, r2, r3, r4, r5, r6, r7, write[56:])
if round == uint8(0) {
read = output
}
p_tmp = read
read = write
write = p_tmp
}
}
}
func crypto_hash(out []uint8, in []uint8, inlen uint64) int {
if inlen >= (1 << uint64(16)) {
return -1
}
var msg_len uint32 = uint32(uint32(inlen))
var padded_len uint32 = ((msg_len+9-1)/64)*64 + 64
var pad_block_len uint8 = uint8(padded_len - msg_len)
var pad_block []uint8 = make([]uint8, pad_block_len, pad_block_len)
pad_block[0] = uint8(128)
var blocks uint32 = uint32((padded_len >> uint64(6)))
pad_block[pad_block_len-1] = (uint8(blocks) & 255)
var h_state []uint8 = make([]uint8, 64, 64)
var p_state []uint8 = make([]uint8, 64, 64)
var q_state []uint8 = make([]uint8, 64, 64)
var x_state []uint8 = make([]uint8, 64, 64)
var buf []uint8 = make([]uint8, 64, 64)
h_state[62] = uint8(1)
var i uint8
var block uint32
var remaining uint32 = uint32(0)
var message_left_len uint32 = msg_len
for {
break
}
{
for block = uint32(0); block < blocks; func() uint32 {
block += 1
return block
}() {
if block*64+64 < msg_len {
//memcpy(buf, in+64*block, uint32(64))
copy(buf, in[64*block:64*block+64]) //copy full block
message_left_len -= 64
} else {
if message_left_len > 0 {
remaining = uint32(64 - message_left_len)
//memcpy(buf, in+64*block, message_left_len)
copy(buf, in[64*block:64*block+message_left_len])
//memcpy(buf+message_left_len, pad_block, uint32(remaining))
copy(buf[message_left_len:], pad_block[:remaining])
message_left_len = uint32(0)
} else {
//memcpy(buf, pad_block+remaining, uint32(64))
copy(buf, pad_block[remaining:remaining+64])
}
}
for {
break
}
{
for i = uint8(0); i < 64; func() uint8 {
i += 1
return i
}() {
x_state[i] = buf[i] ^ h_state[i]
}
}
perm_P(x_state, p_state)
perm_Q(buf, q_state)
for {
break
}
{
for i = uint8(0); i < 64; func() uint8 {
i += 1
return i
}() {
h_state[i] ^= p_state[i] ^ q_state[i]
}
}
}
}
perm_P(h_state, p_state)
for {
break
}
{
for i = uint8(32); i < 64; func() uint8 {
i += 1
return i
}() {
out[i-32] = h_state[i] ^ p_state[i]
}
}
return 0
}
func __init() {
}

+ 546
- 0
cryptonight/jhash.go

@ -0,0 +1,546 @@
package cryptonight
import "fmt"
import "gitlab.com/nitya-sattva/go-x11/hash"
// HashSize holds the size of a hash in bytes.
const HashSize = int(32)
// BlockSize holds the size of a block in bytes.
const BlockSize = uintptr(64)
////////////////
type digest struct {
ptr uintptr
cnt uintptr
h [16]uint64
b [BlockSize]byte
}
// New returns a new digest compute a JH256 hash.
func NewJhash256() hash.Digest {
ref := &digest{}
ref.Reset()
return ref
}
////////////////
// Reset resets the digest to its initial state.
func (ref *digest) Reset() {
ref.ptr = 0
ref.cnt = 0
copy(ref.h[:], kInit[:])
}
// Sum appends the current hash to dst and returns the result
// as a slice. It does not change the underlying hash state.
func (ref *digest) Sum(dst []byte) []byte {
dgt := *ref
hsh := [64]byte{}
dgt.Close(hsh[:], 0, 0)
return append(dst, hsh[32:]...)
}
// Write more data to the running hash, never returns an error.
func (ref *digest) Write(src []byte) (int, error) {
sln := uintptr(len(src))
fln := len(src)
buf := ref.b[:]
ptr := ref.ptr
if sln < (BlockSize - ptr) {
copy(buf[ptr:], src)
ref.ptr += sln
return int(sln), nil
}
var hi, lo [8]uint64
hi[0] = ref.h[0x0]
lo[0] = ref.h[0x1]
hi[1] = ref.h[0x2]
lo[1] = ref.h[0x3]
hi[2] = ref.h[0x4]
lo[2] = ref.h[0x5]
hi[3] = ref.h[0x6]
lo[3] = ref.h[0x7]
hi[4] = ref.h[0x8]
lo[4] = ref.h[0x9]
hi[5] = ref.h[0xA]
lo[5] = ref.h[0xB]
hi[6] = ref.h[0xC]
lo[6] = ref.h[0xD]
hi[7] = ref.h[0xE]
lo[7] = ref.h[0xF]
for sln > 0 {
cln := BlockSize - ptr
if cln > sln {
cln = sln
}
sln -= cln
copy(ref.b[ptr:], src[:cln])
src = src[cln:]
ptr += cln
if ptr == BlockSize {
m0h := decUInt64le(buf[0:])
m0l := decUInt64le(buf[8:])
m1h := decUInt64le(buf[16:])
m1l := decUInt64le(buf[24:])
m2h := decUInt64le(buf[32:])
m2l := decUInt64le(buf[40:])
m3h := decUInt64le(buf[48:])
m3l := decUInt64le(buf[56:])
hi[0] ^= m0h
lo[0] ^= m0l
hi[1] ^= m1h
lo[1] ^= m1l
hi[2] ^= m2h
lo[2] ^= m2l
hi[3] ^= m3h
lo[3] ^= m3l
for r := uint64(0); r < 42; r += 7 {
slMutateExtend(r+0, 0, hi[:], lo[:])
slMutateExtend(r+1, 1, hi[:], lo[:])
slMutateExtend(r+2, 2, hi[:], lo[:])
slMutateExtend(r+3, 3, hi[:], lo[:])
slMutateExtend(r+4, 4, hi[:], lo[:])
slMutateExtend(r+5, 5, hi[:], lo[:])
slMutateBasic(r+6, hi[:], lo[:])
}
hi[4] ^= m0h
lo[4] ^= m0l
hi[5] ^= m1h
lo[5] ^= m1l
hi[6] ^= m2h
lo[6] ^= m2l
hi[7] ^= m3h
lo[7] ^= m3l
ref.cnt++
ptr = 0
}
}
ref.h[0x0] = hi[0]
ref.h[0x1] = lo[0]
ref.h[0x2] = hi[1]
ref.h[0x3] = lo[1]
ref.h[0x4] = hi[2]
ref.h[0x5] = lo[2]
ref.h[0x6] = hi[3]
ref.h[0x7] = lo[3]
ref.h[0x8] = hi[4]
ref.h[0x9] = lo[4]
ref.h[0xA] = hi[5]
ref.h[0xB] = lo[5]
ref.h[0xC] = hi[6]
ref.h[0xD] = lo[6]
ref.h[0xE] = hi[7]
ref.h[0xF] = lo[7]
ref.ptr = ptr
return fln, nil
}
// Close the digest by writing the last bits and storing the hash
// in dst. This prepares the digest for reuse by calling reset. A call
// to Close with a dst that is smaller then HashSize will return an error.
func (ref *digest) Close(dst []byte, bits uint8, bcnt uint8) error {
if ln := len(dst); HashSize > ln {
return fmt.Errorf("JHash Close: dst min length: %d, got %d", HashSize, ln)
}
var ocnt uintptr
var buf [128]uint8
{
off := uint8(0x80) >> bcnt
buf[0] = uint8((bits & -off) | off)
}
if ref.ptr == 0 && bcnt == 0 {
ocnt = 47
} else {
ocnt = 111 - ref.ptr
}
l0 := uint64(bcnt)
l0 += uint64(ref.cnt << 9)
l0 += uint64(ref.ptr << 3)
l1 := uint64(ref.cnt >> 55)
encUInt64be(buf[ocnt+1:], l1)
encUInt64be(buf[ocnt+9:], l0)
ref.Write(buf[:ocnt+17])
for u := uintptr(0); u < 8; u++ {
encUInt64le(dst[(u<<3):], ref.h[u+8])
}
ref.Reset()
return nil
}
// Size returns the number of bytes required to store the hash.
func (*digest) Size() int {
return HashSize
}
// BlockSize returns the block size of the hash.
func (*digest) BlockSize() int {
return int(BlockSize)
}
////////////////
func decUInt64le(src []byte) uint64 {
return (uint64(src[0]) |
uint64(src[1])<<8 |
uint64(src[2])<<16 |
uint64(src[3])<<24 |
uint64(src[4])<<32 |
uint64(src[5])<<40 |
uint64(src[6])<<48 |
uint64(src[7])<<56)
}
func encUInt64le(dst []byte, src uint64) {
dst[0] = uint8(src)
dst[1] = uint8(src >> 8)
dst[2] = uint8(src >> 16)
dst[3] = uint8(src >> 24)
dst[4] = uint8(src >> 32)
dst[5] = uint8(src >> 40)
dst[6] = uint8(src >> 48)
dst[7] = uint8(src >> 56)
}
func encUInt64be(dst []byte, src uint64) {
dst[0] = uint8(src >> 56)
dst[1] = uint8(src >> 48)
dst[2] = uint8(src >> 40)
dst[3] = uint8(src >> 32)
dst[4] = uint8(src >> 24)
dst[5] = uint8(src >> 16)
dst[6] = uint8(src >> 8)
dst[7] = uint8(src)
}
func slMutateBasic(r uint64, hi, lo []uint64) {
var tmp uint64
tmp = kSpec[(r<<2)+0]
hi[6] = ^hi[6]
hi[0] ^= tmp & ^hi[4]
tmp = tmp ^ (hi[0] & hi[2])
hi[0] ^= hi[4] & hi[6]
hi[6] ^= ^hi[2] & hi[4]
hi[2] ^= hi[0] & hi[4]
hi[4] ^= hi[0] & ^hi[6]
hi[0] ^= hi[2] | hi[6]
hi[6] ^= hi[2] & hi[4]
hi[2] ^= tmp & hi[0]
hi[4] ^= tmp
tmp = kSpec[(r<<2)+1]
lo[6] = ^lo[6]
lo[0] ^= tmp & ^lo[4]
tmp = tmp ^ (lo[0] & lo[2])
lo[0] ^= lo[4] & lo[6]
lo[6] ^= ^lo[2] & lo[4]
lo[2] ^= lo[0] & lo[4]
lo[4] ^= lo[0] & ^lo[6]
lo[0] ^= lo[2] | lo[6]
lo[6] ^= lo[2] & lo[4]
lo[2] ^= tmp & lo[0]
lo[4] ^= tmp
tmp = kSpec[(r<<2)+2]
hi[7] = ^hi[7]
hi[1] ^= tmp & ^hi[5]
tmp = tmp ^ (hi[1] & hi[3])
hi[1] ^= hi[5] & hi[7]
hi[7] ^= ^hi[3] & hi[5]
hi[3] ^= hi[1] & hi[5]
hi[5] ^= hi[1] & ^hi[7]
hi[1] ^= hi[3] | hi[7]
hi[7] ^= hi[3] & hi[5]
hi[3] ^= tmp & hi[1]
hi[5] ^= tmp
tmp = kSpec[(r<<2)+3]
lo[7] = ^lo[7]
lo[1] ^= tmp & ^lo[5]
tmp = tmp ^ (lo[1] & lo[3])
lo[1] ^= lo[5] & lo[7]
lo[7] ^= ^lo[3] & lo[5]
lo[3] ^= lo[1] & lo[5]
lo[5] ^= lo[1] & ^lo[7]
lo[1] ^= lo[3] | lo[7]
lo[7] ^= lo[3] & lo[5]
lo[3] ^= tmp & lo[1]
lo[5] ^= tmp
hi[1] ^= hi[2]
hi[3] ^= hi[4]
hi[5] ^= hi[6] ^ hi[0]
hi[7] ^= hi[0]
hi[0] ^= hi[3]
hi[2] ^= hi[5]
hi[4] ^= hi[7] ^ hi[1]
hi[6] ^= hi[1]
lo[1] ^= lo[2]
lo[3] ^= lo[4]
lo[5] ^= lo[6] ^ lo[0]
lo[7] ^= lo[0]
lo[0] ^= lo[3]
lo[2] ^= lo[5]
lo[4] ^= lo[7] ^ lo[1]
lo[6] ^= lo[1]
tmp = hi[1]
hi[1] = lo[1]
lo[1] = tmp
tmp = hi[3]
hi[3] = lo[3]
lo[3] = tmp
tmp = hi[5]
hi[5] = lo[5]
lo[5] = tmp
tmp = hi[7]
hi[7] = lo[7]
lo[7] = tmp
}
func slMutateExtend(r, ro uint64, hi, lo []uint64) {
var tmp uint64
tmp = kSpec[(r<<2)+0]
hi[6] = ^hi[6]
hi[0] ^= tmp & ^hi[4]
tmp = tmp ^ (hi[0] & hi[2])
hi[0] ^= hi[4] & hi[6]
hi[6] ^= ^hi[2] & hi[4]
hi[2] ^= hi[0] & hi[4]
hi[4] ^= hi[0] & ^hi[6]
hi[0] ^= hi[2] | hi[6]
hi[6] ^= hi[2] & hi[4]
hi[2] ^= tmp & hi[0]
hi[4] ^= tmp
tmp = kSpec[(r<<2)+1]
lo[6] = ^lo[6]
lo[0] ^= tmp & ^lo[4]
tmp = tmp ^ (lo[0] & lo[2])
lo[0] ^= lo[4] & lo[6]
lo[6] ^= ^lo[2] & lo[4]
lo[2] ^= lo[0] & lo[4]
lo[4] ^= lo[0] & ^lo[6]
lo[0] ^= lo[2] | lo[6]
lo[6] ^= lo[2] & lo[4]
lo[2] ^= tmp & lo[0]
lo[4] ^= tmp
tmp = kSpec[(r<<2)+2]
hi[7] = ^hi[7]
hi[1] ^= tmp & ^hi[5]
tmp = tmp ^ (hi[1] & hi[3])
hi[1] ^= hi[5] & hi[7]
hi[7] ^= ^hi[3] & hi[5]
hi[3] ^= hi[1] & hi[5]
hi[5] ^= hi[1] & ^hi[7]
hi[1] ^= hi[3] | hi[7]
hi[7] ^= hi[3] & hi[5]
hi[3] ^= tmp & hi[1]
hi[5] ^= tmp
tmp = kSpec[(r<<2)+3]
lo[7] = ^lo[7]
lo[1] ^= tmp & ^lo[5]
tmp = tmp ^ (lo[1] & lo[3])
lo[1] ^= lo[5] & lo[7]
lo[7] ^= ^lo[3] & lo[5]
lo[3] ^= lo[1] & lo[5]
lo[5] ^= lo[1] & ^lo[7]
lo[1] ^= lo[3] | lo[7]
lo[7] ^= lo[3] & lo[5]
lo[3] ^= tmp & lo[1]
lo[5] ^= tmp
hi[1] ^= hi[2]
hi[3] ^= hi[4]
hi[5] ^= hi[6] ^ hi[0]
hi[7] ^= hi[0]
hi[0] ^= hi[3]
hi[2] ^= hi[5]
hi[4] ^= hi[7] ^ hi[1]
hi[6] ^= hi[1]
lo[1] ^= lo[2]
lo[3] ^= lo[4]
lo[5] ^= lo[6] ^ lo[0]
lo[7] ^= lo[0]
lo[0] ^= lo[3]
lo[2] ^= lo[5]
lo[4] ^= lo[7] ^ lo[1]
lo[6] ^= lo[1]
tmp = (hi[1] & (kWrapValue[ro])) << (kWrapOffset[ro])
hi[1] = ((hi[1] >> (kWrapOffset[ro])) & (kWrapValue[ro])) | tmp
tmp = (lo[1] & (kWrapValue[ro])) << (kWrapOffset[ro])
lo[1] = ((lo[1] >> (kWrapOffset[ro])) & (kWrapValue[ro])) | tmp
tmp = (hi[3] & (kWrapValue[ro])) << (kWrapOffset[ro])
hi[3] = ((hi[3] >> (kWrapOffset[ro])) & (kWrapValue[ro])) | tmp
tmp = (lo[3] & (kWrapValue[ro])) << (kWrapOffset[ro])
lo[3] = ((lo[3] >> (kWrapOffset[ro])) & (kWrapValue[ro])) | tmp
tmp = (hi[5] & (kWrapValue[ro])) << (kWrapOffset[ro])
hi[5] = ((hi[5] >> (kWrapOffset[ro])) & (kWrapValue[ro])) | tmp
tmp = (lo[5] & (kWrapValue[ro])) << (kWrapOffset[ro])
lo[5] = ((lo[5] >> (kWrapOffset[ro])) & (kWrapValue[ro])) | tmp
tmp = (hi[7] & (kWrapValue[ro])) << (kWrapOffset[ro])
hi[7] = ((hi[7] >> (kWrapOffset[ro])) & (kWrapValue[ro])) | tmp
tmp = (lo[7] & (kWrapValue[ro])) << (kWrapOffset[ro])
lo[7] = ((lo[7] >> (kWrapOffset[ro])) & (kWrapValue[ro])) | tmp
}
////////////////
/* these constants are for 512 bit hash */
/*var kInit = []uint64{
uint64(0x17aa003e964bd16f), uint64(0x43d5157a052e6a63),
uint64(0x0bef970c8d5e228a), uint64(0x61c3b3f2591234e9),
uint64(0x1e806f53c1a01d89), uint64(0x806d2bea6b05a92a),
uint64(0xa6ba7520dbcc8e58), uint64(0xf73bf8ba763a0fa9),
uint64(0x694ae34105e66901), uint64(0x5ae66f2e8e8ab546),
uint64(0x243c84c1d0a74710), uint64(0x99c15a2db1716e3b),
uint64(0x56f8b19decf657cf), uint64(0x56b116577c8806a7),
uint64(0xfb1785e6dffcc2e3), uint64(0x4bdd8ccc78465a54),
}*/
// these constants are for 256 bit hash
var kInit = []uint64{
uint64(0xEBD3202C41A398EB), uint64(0xC145B29C7BBECD92),
uint64(0xFAC7D4609151931C), uint64(0x38A507ED6820026),
uint64(0x45B92677269E23A4), uint64(0x77941AD4481AFBE0),
uint64(0x7A176B0226ABB5CD), uint64(0xA82FFF0F4224F056),
uint64(0x754D2E7F8996A371), uint64(0x62E27DF70849141D),
uint64(0x948F2476F7957627), uint64(0x6C29804757B6D587),
uint64(0x6C0D8EAC2D275E5C), uint64(0xF7A0557C6508451),
uint64(0xEA12247067D3E47B), uint64(0x69D71CD313ABE389),
}
var kSpec = []uint64{
uint64(0x67f815dfa2ded572), uint64(0x571523b70a15847b),
uint64(0xf6875a4d90d6ab81), uint64(0x402bd1c3c54f9f4e),
uint64(0x9cfa455ce03a98ea), uint64(0x9a99b26699d2c503),
uint64(0x8a53bbf2b4960266), uint64(0x31a2db881a1456b5),
uint64(0xdb0e199a5c5aa303), uint64(0x1044c1870ab23f40),
uint64(0x1d959e848019051c), uint64(0xdccde75eadeb336f),
uint64(0x416bbf029213ba10), uint64(0xd027bbf7156578dc),
uint64(0x5078aa3739812c0a), uint64(0xd3910041d2bf1a3f),
uint64(0x907eccf60d5a2d42), uint64(0xce97c0929c9f62dd),
uint64(0xac442bc70ba75c18), uint64(0x23fcc663d665dfd1),
uint64(0x1ab8e09e036c6e97), uint64(0xa8ec6c447e450521),
uint64(0xfa618e5dbb03f1ee), uint64(0x97818394b29796fd),
uint64(0x2f3003db37858e4a), uint64(0x956a9ffb2d8d672a),
uint64(0x6c69b8f88173fe8a), uint64(0x14427fc04672c78a),
uint64(0xc45ec7bd8f15f4c5), uint64(0x80bb118fa76f4475),
uint64(0xbc88e4aeb775de52), uint64(0xf4a3a6981e00b882),
uint64(0x1563a3a9338ff48e), uint64(0x89f9b7d524565faa),
uint64(0xfde05a7c20edf1b6), uint64(0x362c42065ae9ca36),
uint64(0x3d98fe4e433529ce), uint64(0xa74b9a7374f93a53),
uint64(0x86814e6f591ff5d0), uint64(0x9f5ad8af81ad9d0e),
uint64(0x6a6234ee670605a7), uint64(0x2717b96ebe280b8b),
uint64(0x3f1080c626077447), uint64(0x7b487ec66f7ea0e0),
uint64(0xc0a4f84aa50a550d), uint64(0x9ef18e979fe7e391),
uint64(0xd48d605081727686), uint64(0x62b0e5f3415a9e7e),
uint64(0x7a205440ec1f9ffc), uint64(0x84c9f4ce001ae4e3),
uint64(0xd895fa9df594d74f), uint64(0xa554c324117e2e55),
uint64(0x286efebd2872df5b), uint64(0xb2c4a50fe27ff578),
uint64(0x2ed349eeef7c8905), uint64(0x7f5928eb85937e44),
uint64(0x4a3124b337695f70), uint64(0x65e4d61df128865e),
uint64(0xe720b95104771bc7), uint64(0x8a87d423e843fe74),
uint64(0xf2947692a3e8297d), uint64(0xc1d9309b097acbdd),
uint64(0xe01bdc5bfb301b1d), uint64(0xbf829cf24f4924da),
uint64(0xffbf70b431bae7a4), uint64(0x48bcf8de0544320d),
uint64(0x39d3bb5332fcae3b), uint64(0xa08b29e0c1c39f45),
uint64(0x0f09aef7fd05c9e5), uint64(0x34f1904212347094),
uint64(0x95ed44e301b771a2), uint64(0x4a982f4f368e3be9),
uint64(0x15f66ca0631d4088), uint64(0xffaf52874b44c147),
uint64(0x30c60ae2f14abb7e), uint64(0xe68c6eccc5b67046),
uint64(0x00ca4fbd56a4d5a4), uint64(0xae183ec84b849dda),
uint64(0xadd1643045ce5773), uint64(0x67255c1468cea6e8),
uint64(0x16e10ecbf28cdaa3), uint64(0x9a99949a5806e933),
uint64(0x7b846fc220b2601f), uint64(0x1885d1a07facced1),
uint64(0xd319dd8da15b5932), uint64(0x46b4a5aac01c9a50),
uint64(0xba6b04e467633d9f), uint64(0x7eee560bab19caf6),
uint64(0x742128a9ea79b11f), uint64(0xee51363b35f7bde9),
uint64(0x76d350755aac571d), uint64(0x01707da3fec2463a),
uint64(0x42d8a498afc135f7), uint64(0x79676b9e20eced78),
uint64(0xa8db3aea15638341), uint64(0x832c83324d3bc3fa),
uint64(0xf347271c1f3b40a7), uint64(0x9a762db734f04059),
uint64(0xfd4f21d26c4e3ee7), uint64(0xef5957dc398dfdb8),
uint64(0xdaeb492b490c9b8d), uint64(0x0d70f36849d7a25b),
uint64(0x84558d7ad0ae3b7d), uint64(0x658ef8e4f0e9a5f5),
uint64(0x533b1036f4a2b8a0), uint64(0x5aec3e759e07a80c),
uint64(0x4f88e85692946891), uint64(0x4cbcbaf8555cb05b),
uint64(0x7b9487f3993bbbe3), uint64(0x5d1c6b72d6f4da75),
uint64(0x6db334dc28acae64), uint64(0x71db28b850a5346c),
uint64(0x2a518d10f2e261f8), uint64(0xfc75dd593364dbe3),
uint64(0xa23fce43f1bcac1c), uint64(0xb043e8023cd1bb67),
uint64(0x75a12988ca5b0a33), uint64(0x5c5316b44d19347f),
uint64(0x1e4d790ec3943b92), uint64(0x3fafeeb6d7757479),
uint64(0x21391abef7d4a8ea), uint64(0x5127234c097ef45c),
uint64(0xd23c32ba5324a326), uint64(0xadd5a66d4a17a344),
uint64(0x08c9f2afa63e1db5), uint64(0x563c6b91983d5983),
uint64(0x4d608672a17cf84c), uint64(0xf6c76e08cc3ee246),
uint64(0x5e76bcb1b333982f), uint64(0x2ae6c4efa566d62b),
uint64(0x36d4c1bee8b6f406), uint64(0x6321efbc1582ee74),
uint64(0x69c953f40d4ec1fd), uint64(0x26585806c45a7da7),
uint64(0x16fae0061614c17e), uint64(0x3f9d63283daf907e),
uint64(0x0cd29b00e3f2c9d2), uint64(0x300cd4b730ceaa5f),
uint64(0x9832e0f216512a74), uint64(0x9af8cee3d830eb0d),
uint64(0x9279f1b57b9ec54b), uint64(0xd36886046ee651ff),
uint64(0x316796e6574d239b), uint64(0x05750a17f3a6e6cc),
uint64(0xce6c3213d98176b1), uint64(0x62a205f88452173c),
uint64(0x47154778b3cb2bf4), uint64(0x486a9323825446ff),
uint64(0x65655e4e0758df38), uint64(0x8e5086fc897cfcf2),
uint64(0x86ca0bd0442e7031), uint64(0x4e477830a20940f0),
uint64(0x8338f7d139eea065), uint64(0xbd3a2ce437e95ef7),
uint64(0x6ff8130126b29721), uint64(0xe7de9fefd1ed44a3),
uint64(0xd992257615dfa08b), uint64(0xbe42dc12f6f7853c),
uint64(0x7eb027ab7ceca7d8), uint64(0xdea83eaada7d8d53),
uint64(0xd86902bd93ce25aa), uint64(0xf908731afd43f65a),
uint64(0xa5194a17daef5fc0), uint64(0x6a21fd4c33664d97),
uint64(0x701541db3198b435), uint64(0x9b54cdedbb0f1eea),
uint64(0x72409751a163d09a), uint64(0xe26f4791bf9d75f6),
}
var kWrapValue = []uint64{
uint64(0x5555555555555555),
uint64(0x3333333333333333),
uint64(0x0F0F0F0F0F0F0F0F),
uint64(0x00FF00FF00FF00FF),
uint64(0x0000FFFF0000FFFF),
uint64(0x00000000FFFFFFFF),
}
var kWrapOffset = []uint64{
1, 2, 4, 8, 16, 32,
}

+ 334
- 0
cryptonight/keccak.go

@ -0,0 +1,334 @@
// Package keccak implements the Keccak (SHA-3) hash algorithm.
// http://keccak.noekeon.org / FIPS 202 draft.
package cryptonight
// import "fmt"
import "hash"
const (
domainNone = 1
domainSHA3 = 0x06
domainSHAKE = 0x1f
)
const rounds = 24
var roundConstants = []uint64{
0x0000000000000001, 0x0000000000008082,
0x800000000000808A, 0x8000000080008000,
0x000000000000808B, 0x0000000080000001,
0x8000000080008081, 0x8000000000008009,
0x000000000000008A, 0x0000000000000088,
0x0000000080008009, 0x000000008000000A,
0x000000008000808B, 0x800000000000008B,
0x8000000000008089, 0x8000000000008003,
0x8000000000008002, 0x8000000000000080,
0x000000000000800A, 0x800000008000000A,
0x8000000080008081, 0x8000000000008080,
0x0000000080000001, 0x8000000080008008,
}
type keccak struct {
S [25]uint64
size int
blockSize int
buf []byte
domain byte
}
func newKeccak(capacity, output int, domain byte) hash.Hash {
var h keccak
h.size = output / 8
h.blockSize = (200 - capacity/8)
h.domain = domain
return &h
}
func New224() hash.Hash {
return newKeccak(224*2, 224, domainNone)
}
func New256() hash.Hash {
return newKeccak(256*2, 256, domainNone)
}
func New384() hash.Hash {
return newKeccak(384*2, 384, domainNone)
}
func New512() hash.Hash {
return newKeccak(512*2, 512, domainNone)
}
func (k *keccak) Write(b []byte) (int, error) {
n := len(b)
if len(k.buf) > 0 {
x := k.blockSize - len(k.buf)
if x > len(b) {
x = len(b)
}
k.buf = append(k.buf, b[:x]...)
b = b[x:]
if len(k.buf) < k.blockSize {
return n, nil
}
k.absorb(k.buf)
k.buf = nil
}
for len(b) >= k.blockSize {
k.absorb(b[:k.blockSize])
b = b[k.blockSize:]
}
k.buf = b
return n, nil
}
func (k0 *keccak) Sum(b []byte) []byte {
k := *k0
k.final()
return k.squeeze(b)
}
func (k *keccak) Reset() {
for i := range k.S {
k.S[i] = 0
}
k.buf = nil
}
func (k *keccak) Size() int {
return k.size
}
func (k *keccak) BlockSize() int {
return k.blockSize
}
func (k *keccak) absorb(block []byte) {
if len(block) != k.blockSize {
panic("absorb() called with invalid block size")
}
for i := 0; i < k.blockSize/8; i++ {
k.S[i] ^= uint64le(block[i*8:])
}
keccakf(&k.S)
}
func (k *keccak) pad(block []byte) []byte {
padded := make([]byte, k.blockSize)
copy(padded, k.buf)
padded[len(k.buf)] = k.domain
padded[len(padded)-1] |= 0x80
return padded
}
func (k *keccak) final() {
last := k.pad(k.buf)
k.absorb(last)
}
func (k *keccak) squeeze(b []byte) []byte {
buf := make([]byte, 8*len(k.S))
n := k.size
for {
for i := range k.S {
putUint64le(buf[i*8:], k.S[i])
}
if n <= k.blockSize {
b = append(b, buf[:n]...)
break
}
b = append(b, buf[:k.blockSize]...)
n -= k.blockSize
keccakf(&k.S)
}
return b
}
func keccakf(S *[25]uint64) {
var bc0, bc1, bc2, bc3, bc4 uint64
var S0, S1, S2, S3, S4 uint64
var S5, S6, S7, S8, S9 uint64
var S10, S11, S12, S13, S14 uint64
var S15, S16, S17, S18, S19 uint64
var S20, S21, S22, S23, S24 uint64
var tmp uint64
//S[0]=0x2073692073696854;
//S[1]=0x1747365742061
// S[16]=0x8000000000000000;
/* for i :=0; i< 25;i++{
fmt.Printf("%2d %X\n", i, S[i])
}
*/
S0, S1, S2, S3, S4 = S[0], S[1], S[2], S[3], S[4]
S5, S6, S7, S8, S9 = S[5], S[6], S[7], S[8], S[9]
S10, S11, S12, S13, S14 = S[10], S[11], S[12], S[13], S[14]
S15, S16, S17, S18, S19 = S[15], S[16], S[17], S[18], S[19]
S20, S21, S22, S23, S24 = S[20], S[21], S[22], S[23], S[24]
for r := 0; r < rounds; r++ {
// theta
bc0 = S0 ^ S5 ^ S10 ^ S15 ^ S20
bc1 = S1 ^ S6 ^ S11 ^ S16 ^ S21
bc2 = S2 ^ S7 ^ S12 ^ S17 ^ S22
bc3 = S3 ^ S8 ^ S13 ^ S18 ^ S23
bc4 = S4 ^ S9 ^ S14 ^ S19 ^ S24
tmp = bc4 ^ (bc1<<1 | bc1>>(64-1))
S0 ^= tmp
S5 ^= tmp
S10 ^= tmp
S15 ^= tmp
S20 ^= tmp
tmp = bc0 ^ (bc2<<1 | bc2>>(64-1))
S1 ^= tmp
S6 ^= tmp
S11 ^= tmp
S16 ^= tmp
S21 ^= tmp
tmp = bc1 ^ (bc3<<1 | bc3>>(64-1))
S2 ^= tmp
S7 ^= tmp
S12 ^= tmp
S17 ^= tmp
S22 ^= tmp
tmp = bc2 ^ (bc4<<1 | bc4>>(64-1))
S3 ^= tmp
S8 ^= tmp
S13 ^= tmp
S18 ^= tmp
S23 ^= tmp
tmp = bc3 ^ (bc0<<1 | bc0>>(64-1))
S4 ^= tmp
S9 ^= tmp
S14 ^= tmp
S19 ^= tmp
S24 ^= tmp
// rho phi
tmp = S1
tmp, S10 = S10, tmp<<1|tmp>>(64-1)
tmp, S7 = S7, tmp<<3|tmp>>(64-3)
tmp, S11 = S11, tmp<<6|tmp>>(64-6)
tmp, S17 = S17, tmp<<10|tmp>>(64-10)
tmp, S18 = S18, tmp<<15|tmp>>(64-15)
tmp, S3 = S3, tmp<<21|tmp>>(64-21)
tmp, S5 = S5, tmp<<28|tmp>>(64-28)
tmp, S16 = S16, tmp<<36|tmp>>(64-36)
tmp, S8 = S8, tmp<<45|tmp>>(64-45)
tmp, S21 = S21, tmp<<55|tmp>>(64-55)
tmp, S24 = S24, tmp<<2|tmp>>(64-2)
tmp, S4 = S4, tmp<<14|tmp>>(64-14)
tmp, S15 = S15, tmp<<27|tmp>>(64-27)
tmp, S23 = S23, tmp<<41|tmp>>(64-41)
tmp, S19 = S19, tmp<<56|tmp>>(64-56)
tmp, S13 = S13, tmp<<8|tmp>>(64-8)
tmp, S12 = S12, tmp<<25|tmp>>(64-25)
tmp, S2 = S2, tmp<<43|tmp>>(64-43)
tmp, S20 = S20, tmp<<62|tmp>>(64-62)
tmp, S14 = S14, tmp<<18|tmp>>(64-18)
tmp, S22 = S22, tmp<<39|tmp>>(64-39)
tmp, S9 = S9, tmp<<61|tmp>>(64-61)
tmp, S6 = S6, tmp<<20|tmp>>(64-20)
S1 = tmp<<44 | tmp>>(64-44)
// chi
bc0 = S0
bc1 = S1
bc2 = S2
bc3 = S3
bc4 = S4
S0 ^= (^bc1) & bc2
S1 ^= (^bc2) & bc3
S2 ^= (^bc3) & bc4
S3 ^= (^bc4) & bc0
S4 ^= (^bc0) & bc1
bc0 = S5
bc1 = S6
bc2 = S7
bc3 = S8
bc4 = S9
S5 ^= (^bc1) & bc2
S6 ^= (^bc2) & bc3
S7 ^= (^bc3) & bc4
S8 ^= (^bc4) & bc0
S9 ^= (^bc0) & bc1
bc0 = S10
bc1 = S11
bc2 = S12
bc3 = S13
bc4 = S14
S10 ^= (^bc1) & bc2
S11 ^= (^bc2) & bc3
S12 ^= (^bc3) & bc4
S13 ^= (^bc4) & bc0
S14 ^= (^bc0) & bc1
bc0 = S15
bc1 = S16
bc2 = S17
bc3 = S18
bc4 = S19
S15 ^= (^bc1) & bc2
S16 ^= (^bc2) & bc3
S17 ^= (^bc3) & bc4
S18 ^= (^bc4) & bc0
S19 ^= (^bc0) & bc1
bc0 = S20
bc1 = S21
bc2 = S22
bc3 = S23
bc4 = S24
S20 ^= (^bc1) & bc2
S21 ^= (^bc2) & bc3
S22 ^= (^bc3) & bc4
S23 ^= (^bc4) & bc0
S24 ^= (^bc0) & bc1
// iota
S0 ^= roundConstants[r]
}
S[0], S[1], S[2], S[3], S[4] = S0, S1, S2, S3, S4
S[5], S[6], S[7], S[8], S[9] = S5, S6, S7, S8, S9
S[10], S[11], S[12], S[13], S[14] = S10, S11, S12, S13, S14
S[15], S[16], S[17], S[18], S[19] = S15, S16, S17, S18, S19
S[20], S[21], S[22], S[23], S[24] = S20, S21, S22, S23, S24
}
func uint64le(v []byte) uint64 {
return uint64(v[0]) |
uint64(v[1])<<8 |
uint64(v[2])<<16 |
uint64(v[3])<<24 |
uint64(v[4])<<32 |
uint64(v[5])<<40 |
uint64(v[6])<<48 |
uint64(v[7])<<56
}
func putUint64le(v []byte, x uint64) {
v[0] = byte(x)
v[1] = byte(x >> 8)
v[2] = byte(x >> 16)
v[3] = byte(x >> 24)
v[4] = byte(x >> 32)
v[5] = byte(x >> 40)
v[6] = byte(x >> 48)
v[7] = byte(x >> 56)
}

+ 117
- 0
difficulty/difficulty.go

@ -0,0 +1,117 @@
package difficulty
import "fmt"
import "math/big"
import "github.com/deroproject/derosuite/config"
import "github.com/deroproject/derosuite/crypto"
var (
// bigZero is 0 represented as a big.Int. It is defined here to avoid
// the overhead of creating it multiple times.
bigZero = big.NewInt(0)
// bigOne is 1 represented as a big.Int. It is defined here to avoid
// the overhead of creating it multiple times.
bigOne = big.NewInt(1)
// oneLsh256 is 1 shifted left 256 bits. It is defined here to avoid
// the overhead of creating it multiple times.
oneLsh256 = new(big.Int).Lsh(bigOne, 256)
)
// HashToBig converts a PoW has into a big.Int that can be used to
// perform math comparisons.
func HashToBig(buf crypto.Hash) *big.Int {
// A Hash is in little-endian, but the big package wants the bytes in
// big-endian, so reverse them.
blen := len(buf) // its hardcoded 32 bytes, so why do len but lets do it
for i := 0; i < blen/2; i++ {
buf[i], buf[blen-1-i] = buf[blen-1-i], buf[i]
}
return new(big.Int).SetBytes(buf[:])
}
// this function calculates the difficulty in big num form
func ConvertDifficultyToBig(difficultyi uint64) *big.Int {
if difficultyi == 0 {
panic("difficulty can never be zero")
}
// (1 << 256) / (difficultyNum )
difficulty := new(big.Int).SetUint64(difficultyi)
denominator := new(big.Int).Add(difficulty, bigZero) // above 2 lines can be merged
return new(big.Int).Div(oneLsh256, denominator)
}
// this function check whether the pow hash meets difficulty criteria
func CheckPowHash(pow_hash crypto.Hash, difficulty uint64) bool {
big_difficulty := ConvertDifficultyToBig(difficulty)
big_pow_hash := HashToBig(pow_hash)
if big_pow_hash.Cmp(big_difficulty) <= 0 { // if work_pow is less than difficulty
return true
}
return false
}
/* this function calculates difficulty on the basis of previous timestamps and cumulative_difficulty */
func Next_Difficulty(timestamps []uint64, cumulative_difficulty []uint64, target_seconds uint64) (difficulty uint64) {
difficulty = 1 // default difficulty is 1 // for genesis block
if len(timestamps) > config.DIFFICULTY_BLOCKS_COUNT_V2 {
panic("More timestamps provided than required")
}
if len(timestamps) != len(cumulative_difficulty) {
panic("Number of timestamps != Number of cumulative_difficulty")
}
if len(timestamps) <= 1 {
return difficulty // return 1
}
length := uint64(len(timestamps))
weighted_timespans := uint64(0)
for i := uint64(1); i < length; i++ {
timespan := uint64(0)
if timestamps[i-1] >= timestamps[i] {
timespan = 1
} else {
timespan = timestamps[i] - timestamps[i-1]
}
if timespan > (10 * target_seconds) {
timespan = 10 * target_seconds
}
weighted_timespans += i * timespan
}
minimum_timespan := (target_seconds * length) / 2
if weighted_timespans < minimum_timespan { // fix startup weirdness
weighted_timespans = minimum_timespan
}
total_work := cumulative_difficulty[length-1] - cumulative_difficulty[0]
// convert input for 128 bit multiply
var big_total_work, big_target, big_result big.Int
big_total_work.SetUint64(total_work)
target := (((length + 1) / 2) * target_seconds * 3) / 2
big_target.SetUint64(target)
big_result.Mul(&big_total_work, &big_target)
if big_result.IsUint64() {
if big_result.Uint64() > 0x000fffffffffffff { // this will give us atleast 1 year to fix the difficulty algorithm
fmt.Printf("Total work per target_time will soon cross 2^64, please fix the difficulty algorithm\n")
}
difficulty = big_result.Uint64() / weighted_timespans
} else {
panic("Total work per target_time crossing 2^64 , please fix the above")
}
return difficulty
}

+ 93
- 0
difficulty/difficulty_test.go

@ -0,0 +1,93 @@
package difficulty
import "testing"
import "github.com/deroproject/derosuite/crypto"
func Test_Next_Difficulty(t *testing.T) {
target_seconds := uint64(120)
var cumulative_difficulties []uint64
var timestamps []uint64
calculated := Next_Difficulty(timestamps, cumulative_difficulties, target_seconds)
expected := uint64(1)
if calculated != expected {
t.Errorf("Difficulty should be %d found %d\n", expected, calculated)
}
timestamps = append(timestamps, 1)
cumulative_difficulties = append(cumulative_difficulties, 1)
calculated = Next_Difficulty(timestamps, cumulative_difficulties, target_seconds)
expected = uint64(1)
if calculated != expected {
t.Errorf("Difficulty should be %d found %d\n", expected, calculated)
}
timestamps = append(timestamps, 1)
cumulative_difficulties = append(cumulative_difficulties, 3)
calculated = Next_Difficulty(timestamps, cumulative_difficulties, target_seconds)
expected = uint64(3)
if calculated != expected {
t.Errorf("Difficulty should be %d found %d\n", expected, calculated)
}
timestamps = append(timestamps, 1)
cumulative_difficulties = append(cumulative_difficulties, 10)
calculated = Next_Difficulty(timestamps, cumulative_difficulties, target_seconds)
expected = uint64(18)
if calculated != expected {
t.Errorf("Difficulty should be %d found %d\n", expected, calculated)
}
timestamps = append(timestamps, 1)
cumulative_difficulties = append(cumulative_difficulties, 20)
calculated = Next_Difficulty(timestamps, cumulative_difficulties, target_seconds)
expected = uint64(28)
if calculated != expected {
t.Errorf("Difficulty should be %d found %d\n", expected, calculated)
}
}
/*
* raw data from daemon
*
* /*
* 2018-01-07 20:18:21.157 [P2P4] INFO global src/cryptonote_core/blockchain.cpp:1436 ----- BLOCK ADDED AS ALTERNATIVE ON HEIGHT 16163
id: <f9a3faa33054a4a1fa349321c546ee5f42cc416f13a991152c64fcbef994518b>
PoW: <28b6fdf6655c45631c28be02bc528342b25fe913696911b788a01e0b0a000000>
difficulty: 77897895
2018-01-07 21:17:40.182 [P2P9] INFO global src/cryptonote_protocol/cryptonote_protocol_handler.inl:1521 SYNCHRONIZED OK
2018-01-07 22:04:14.368 [P2P2] INFO global src/p2p/net_node.inl:258 Host 125.161.128.47 blocked.
status
Height: 16294/16294 (100.0%) on mainnet, not mining, net hash 780.13 kH/s, v6, up to date, 0(out)+15(in) connections, uptime 0d 10h 29m 2s
2018-01-08 03:14:37.490 [P2P1] INFO global src/cryptonote_core/blockchain.cpp:1436 ----- BLOCK ADDED AS ALTERNATIVE ON HEIGHT 13618
id: <a3918ac81a08e8740f99f79ff788d9e147ceb7e530ed590ac1e0f5d1cbba28c5>
PoW: <b34caa51543b82efee0336677dd825e3236220e69d2f090c58df0b3e05000000>
difficulty: 90940906
*/
func Test_CheckPowHash(t *testing.T) {
hash := crypto.Hash{0x28, 0xb6, 0xfd, 0xf6, 0x65, 0x5c, 0x45, 0x63, 0x1c, 0x28, 0xbe,
0x02, 0xbc, 0x52, 0x83, 0x42, 0xb2, 0x5f, 0xe9, 0x13, 0x69, 0x69,
0x11, 0xb7, 0x88, 0xa0, 0x1e, 0x0b, 0x0a, 0x00, 0x00, 0x00}
difficulty := uint64(77897895)
if !CheckPowHash(hash, difficulty) {
t.Errorf("POW check failedm, severe BUG\n")
}
hash = crypto.Hash{0xb3, 0x4c, 0xaa, 0x51, 0x54, 0x3b, 0x82, 0xef, 0xee, 0x03, 0x36, 0x67,
0x7d, 0xd8, 0x25, 0xe3, 0x23, 0x62, 0x20, 0xe6, 0x9d, 0x2f, 0x09,
0x0c, 0x58, 0xdf, 0x0b, 0x3e, 0x05, 0x00, 0x00, 0x00}
difficulty = uint64(77897895)
if !CheckPowHash(hash, difficulty) {
t.Errorf("POW check 2 failed, severe BUG\n")
}
}

+ 89
- 0
emission/emission.go

@ -0,0 +1,89 @@
package emission
import "fmt"
import "math/big"
import "github.com/deroproject/derosuite/config"
// the logic is same as cryptonote_basic_impl.cpp
// this file controls the logic for emission of coins at each height
// calculates block reward
func GetBlockReward(bl_median_size uint64,
bl_current_size uint64,
already_generated_coins uint64,
hard_fork_version uint64,
fee uint64) (reward uint64) {
target := config.COIN_DIFFICULTY_TARGET
target_minutes := target / 60
emission_speed_factor := config.COIN_EMISSION_SPEED_FACTOR - (target_minutes - 1)
// handle special cases
switch already_generated_coins {
case 0:
reward = 1000000000000 // give 1 DERO to genesis, but we gave 35 due to a silly typo, so continue as is
return reward
case 1000000000000:
reward = 2000000 * 1000000000000 // give the developers initial premine, while keeping the, into account and respecting transparancy
return reward
}
base_reward := (config.COIN_MONEY_SUPPLY - already_generated_coins) >> emission_speed_factor
if base_reward < (config.COIN_FINAL_SUBSIDY_PER_MINUTE * target_minutes) {
base_reward = config.COIN_FINAL_SUBSIDY_PER_MINUTE * target_minutes
}
//full_reward_zone = get_min_block_size(version);
full_reward_zone := config.CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE
if bl_median_size < full_reward_zone {
bl_median_size = full_reward_zone
}
if bl_current_size <= bl_median_size {
reward = base_reward
fmt.Printf("Retunring base reward\n")
return reward
}
// block is bigger than median size , we must calculate it
if bl_current_size > 2*bl_median_size {
//MERROR("Block cumulative size is too big: " << current_block_size << ", expected less than " << 2 * median_size);
panic("Block size is too big\n")
return
}
//panic("This mode of base reward calculation is not yet implemented\n")
multiplicand := (2 * bl_median_size) - bl_current_size
multiplicand = multiplicand * bl_current_size
var big_base_reward, big_multiplicand, big_product, big_reward, big_median_size big.Int
big_median_size.SetUint64(bl_median_size)
big_base_reward.SetUint64(base_reward)
big_multiplicand.SetUint64(multiplicand)
big_product.Mul(&big_base_reward, &big_multiplicand)
big_reward.Div(&big_product, &big_median_size)
big_product.Set(&big_reward)
big_reward.Div(&big_product, &big_median_size)
// lower 64 bits contains the reward
if !big_reward.IsUint64() {
panic("GetBlockReward has issues\n")
}
reward_lo := big_reward.Uint64()
if reward_lo > base_reward {
panic("Reward must be less than base reward\n")
}
return reward_lo
}

+ 42
- 0
emission/emission_test.go

@ -0,0 +1,42 @@
package emission
//import "encoding/hex"
//import "bytes"
import "testing"
func Test_Emission_Rewards(t *testing.T) {
bl_median_size := uint64(0)
bl_current_size := uint64(0)
already_generated_coins := uint64(0)
hard_fork_version := uint64(6)
fee := uint64(0)
if GetBlockReward(bl_median_size, bl_current_size, already_generated_coins, hard_fork_version, fee) != uint64(1000000000000) {
t.Error("Block reward failed for genesis block\n")
}
already_generated_coins = uint64(1000000000000)
if GetBlockReward(bl_median_size, bl_current_size, already_generated_coins, hard_fork_version, fee) != uint64(2000000000000000000) {
t.Error("Block reward failed for pre-mine\n")
}
already_generated_coins += uint64(2000000000000000000)
calculated := GetBlockReward(bl_median_size, bl_current_size, already_generated_coins, hard_fork_version, fee)
expected := uint64(31369672915858)
if calculated != expected {
t.Errorf("Block reward failed for 2nd block estm %d calculated %d\n", expected, calculated)
}
already_generated_coins += uint64(31369672915858)
// calculated block reward for 3rd block
calculated = GetBlockReward(bl_median_size, bl_current_size, already_generated_coins, hard_fork_version, fee)
expected = uint64(31369613082955)
if calculated != expected {
t.Errorf("Block reward failed for 3rd block estm %d calculated %d\n", expected, calculated)
}
}

+ 8
- 0
globals/dummy_test.go

@ -0,0 +1,8 @@
package globals
import "testing"
func Test_Part1(t *testing.T){
}

+ 98
- 0
globals/globals.go

@ -0,0 +1,98 @@
package globals
import "net/url"
import "golang.org/x/net/proxy"
import "github.com/sirupsen/logrus"
import log "github.com/sirupsen/logrus"
import "github.com/deroproject/derosuite/config"
type ChainState int // block chain can only be in 2 state, either SYNCRONISED or syncing
const (
SYNCRONISED ChainState = iota // 0
SYNCING // 1
)
// all the the global variables used by the program are stored here
// since the entire logic is designed around a state machine driven by external events
// once the core starts nothing changes until there is a network state change
var Incoming_Block = make([]byte, 100) // P2P feeds it, blockchain consumes it
var Outgoing_Block = make([]byte, 100) // blockchain feeds it, P2P consumes it only if a block has been mined
var Incoming_Tx = make([]byte, 100) // P2P feeds it, blockchain consumes it
var Outgoing_Tx = make([]byte, 100) // blockchain feeds it, P2P consumes it only if a user has created a Tx mined
var Subsystem_Active uint32 // atomic counter to show how many subsystems are active
var Exit_In_Progress bool
// on init this variable is updated to setup global config in 1 go
var Config config.CHAIN_CONFIG
// global logger all components will use it with context
var Logger *logrus.Logger
var Log_Level = logrus.InfoLevel // default is info level
var ilog_formatter *logrus.TextFormatter // used while tracing code
var Dialer proxy.Dialer = proxy.Direct // for proxy and direct connections
// all outgoing connections , including DNS requests must be made using this
// all program arguments are available here
var Arguments map[string]interface{}
func Initialize() {
var err error
_ = err
Config = config.Mainnet // default is mainnnet
if Arguments["--testnet"].(bool) == true { // setup testnet if requested
Config = config.Testnet
}
// formatter := &logrus.TextFormatter{DisableColors : true}
//Logger= &logrus.Logger{Formatter:formatter}
Logger = logrus.New()
//Logger.Formatter = &logrus.TextFormatter{DisableColors : true}
Logger.SetLevel(logrus.InfoLevel)
if Arguments["--debug"].(bool) == true { // setup debug mode if requested
Log_Level = logrus.DebugLevel
Logger.SetLevel(logrus.DebugLevel)
}
// choose socks based proxy if user requested so
if Arguments["--socks-proxy"] != nil {
log.Debugf("Setting up proxy using %s", Arguments["--socks-proxy"].(string))
//uri, err := url.Parse("socks5://127.0.0.1:9000") // "socks5://demo:demo@192.168.99.100:1080"
uri, err := url.Parse("socks5://" + Arguments["--socks-proxy"].(string)) // "socks5://demo:demo@192.168.99.100:1080"
if err != nil {
log.Fatalf("Error parsing socks proxy: err %s", err)
}
Dialer, err = proxy.FromURL(uri, proxy.Direct)
if err != nil {
log.Fatalf("Error creating socks proxy: err \"%s\" from data %s ", err, Arguments["--socks-proxy"].(string))
}
}
// windows and logrus have issues while printing colored messages, so disable them right now
ilog_formatter = &logrus.TextFormatter{} // this needs to be created after after top logger has been intialised
ilog_formatter.DisableColors = true
ilog_formatter.DisableTimestamp = true
}
/* this function converts a logrus entry into a txt formater based entry with no colors for tracing*/
func CTXString(entry *logrus.Entry) string {
entry.Level = logrus.DebugLevel
data, err := ilog_formatter.Format(entry)
_ = err
return string(data)
}

+ 21
- 0
licenses/aead.skein.license.txt

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Andreas Auernhammer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

+ 20
- 0
licenses/boltdb.license.txt

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 Ben Johnson
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 20
- 0
licenses/docopt.license.txt

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 Keith Batten
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 23
- 0
licenses/efbe.keccak.license.txt

@ -0,0 +1,23 @@
Copyright (c) 2014, Michael Gehring <mg@ebfe.org>
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 27
- 0
licenses/golang.license.txt

@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 21
- 0
licenses/logrus.license.txt

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Simon Eskildsen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

+ 8
- 0
licenses/msgp.license.txt

@ -0,0 +1,8 @@
Copyright (c) 2014 Philip Hofer
Portions Copyright (c) 2009 The Go Authors (license at http://golang.org) where indicated
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 22
- 0
licenses/readline.license.txt

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Chzyer
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

+ 2
- 0
licenses/readme.txt

@ -0,0 +1,2 @@
This folder contains licenses of other libraries used within the project

+ 201
- 0
licenses/rlog.license.txt

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

+ 21
- 0
licenses/stack.license.txt

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Chris Hines
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

+ 20
- 0
licenses/uuid.license

@ -0,0 +1,20 @@
Copyright (C) 2013-2016 by Maxim Bublis <b@codemonkey.ru>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 15
- 0
licenses/x11-license.txt

@ -0,0 +1,15 @@
## ISC License
* Copyright (c) 2016 The Dash Developers
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

+ 121
- 0
p2p/bc_notify_new_block.go

@ -0,0 +1,121 @@
package p2p
import "bytes"
import "encoding/binary"
import "github.com/romana/rlog"
import "github.com/deroproject/derosuite/blockchain"
// FIXME this code can also be shared by NOTIFY_NEW_BLOCK, NOTIFY_NEW_TRANSACTIONS, Handle_BC_Notify_Response_GetObjects
// this code handles a new block floating in the network
func Handle_BC_Notify_New_Block(connection *Connection,
i_command_header *Levin_Header, buf []byte) {
var bl blockchain.Block
connection.logger.Debugf("Incoming NOTIFY_NEW_BLOCK")
// deserialize data header
var i_data_header Levin_Data_Header // incoming data header
err := i_data_header.DeSerialize(buf)
if err != nil {
connection.logger.Debugf("We should destroy connection here, data header cnot deserialized")
return
}
buf = i_data_header.Data
pos := bytes.Index(buf, []byte("block"))
// find inner position of block
pos = pos + 6 // jump to varint length position and decode
buf = buf[pos:]
block_length, done := Decode_Boost_Varint(buf)
rlog.Tracef(2, "Block length %d %x\n", block_length, buf[:8])
buf = buf[done:]
block_buf := buf[:block_length]
err = bl.Deserialize(block_buf)
if err != nil {
connection.logger.Warnf("Block could not be deserialized successfully err %s\n", err)
connection.logger.Debugf("We should destroy connection here, block not deserialized")
return
}
hash := bl.GetHash()
rlog.Tracef(1, "Block deserialized successfully %x\n", hash[:32])
rlog.Tracef(1, "Tx hash length %d\n", len(bl.Tx_hashes))
for i := range bl.Tx_hashes {
rlog.Tracef(2, "%d tx %x\n", i, bl.Tx_hashes[i][:32])
}
// point buffer to check whether any more tx exist
buf = buf[block_length:]
pos = bytes.Index(buf, []byte("\x03txs\x8a")) // at this point to
if pos > -1 {
rlog.Tracef(3, "txt pos %d\n", pos)
buf = buf[pos+5:]
// decode remain data length ( though we know it from buffer size, but still verify it )
tx_count, done := Decode_Boost_Varint(buf)
buf = buf[done:]
for i := uint64(0); i < tx_count; i++ {
var tx blockchain.Transaction
tx_len, done := Decode_Boost_Varint(buf)
buf = buf[done:]
rlog.Tracef(3, "tx count %d i %d tx_len %d\n", tx_count, i, tx_len)
tx_bytes := buf[:tx_len]
// deserialize and verrify transaction
err = tx.DeserializeHeader(tx_bytes)
if err != nil {
connection.logger.Warnf("Transaction could not be deserialized\n")
} else {
hash := tx.GetHash()
rlog.Tracef(2, "Transaction deserialised successfully hash %x\n", hash[:32])
// add tx to block chain, we must verify that the tx has been mined
// add all transaction to TX pool , if not added
chain.Add_TX(&tx)
}
buf = buf[tx_len:] // setup for next tx
}
}
height_string := []byte("\x19current_blockchain_height\x05")
pos = bytes.Index(buf, height_string) // at this point to
if pos < 0 {
connection.logger.Debugf("We should destroy connection here, block not deserialized")
return
}
pos = pos + len(height_string)
buf = buf[pos:]
current_peer_height := binary.LittleEndian.Uint64(buf)
connection.Last_Height = current_peer_height
//connection.logger.Infof("buffer height %x current height %d complete %d\n", buf, connection.Last_Height, complete_block)
// at this point, if it's a block we should try to add it to block chain
// try to add block to chain
connection.logger.Debugf("Found new block adding it to chain %x", bl.GetHash())
chain.Chain_Add(&bl)
}

+ 91
- 0
p2p/bc_notify_new_transactions.go

@ -0,0 +1,91 @@
package p2p
import "bytes"
import "github.com/romana/rlog"
import "github.com/deroproject/derosuite/blockchain"
// if the incoming blob contains block with included transactions
//00009F94 01 11 01 01 01 01 02 01 01 08 06 62 6c 6f 63 6b ........ ...block
//00009FA4 73 8c 04 08 05 62 6c 6f 63 6b 0a fd 03 06 06 cd s....blo ck......
// if the incoming blob contains block without any tx
//00009EB4 01 11 01 01 01 01 02 01 01 08 06 62 6c 6f 63 6b ........ ...block
//00009EC4 73 8c 08 04 05 62 6c 6f 63 6b 0a e5 01 01 00 00 s....blo ck......
// if the incoming blob only contains a TX
// FIXME this code can also be shared by NOTIFY_NEW_BLOCK, NOTIFY_NEW_TRANSACTIONS
// we trigger this if we want to request any TX or block from the peer
func Handle_BC_Notify_New_Transactions(connection *Connection,
i_command_header *Levin_Header, buf []byte) {
// deserialize data header
var i_data_header Levin_Data_Header // incoming data header
err := i_data_header.DeSerialize(buf)
if err != nil {
connection.logger.Debugf("We should destroy connection here, data header cnot deserialized")
connection.Exit = true
return
}
connection.logger.Debugf("Incoming NOTIFY_NEW_TRANSACTIONS")
// check whether the response contains block
pos := bytes.Index(i_data_header.Data, []byte("blocks")) // at this point to
buf = i_data_header.Data
pos = bytes.Index(buf, []byte("\x03txs\x8a")) // at this point to
if pos > -1 {
rlog.Tracef(3, "txt pos %d", pos)
buf = buf[pos+5:]
// decode remain data length ( though we know it from buffer size, but still verify it )
tx_count, done := Decode_Boost_Varint(buf)
buf = buf[done:]
for i := uint64(0); i < tx_count; i++ {
var tx blockchain.Transaction
tx_len, done := Decode_Boost_Varint(buf)
buf = buf[done:]
rlog.Tracef(3, "tx count %d i %d tx_len %d\n", tx_count, i, tx_len)
tx_bytes := buf[:tx_len]
// deserialize and verrify transaction
err = tx.DeserializeHeader(tx_bytes)
if err != nil {
connection.logger.Warnf("Transaction could not be deserialized\n") // we should disconnect peer
} else {
hash := tx.GetHash()
rlog.Tracef(2, "Transaction deserialised successfully hash %x\n", hash[:32])
// add tx to mem pool, we must verify that the tx is valid at this point in time
// we should check , whether we shoul add the tx to pool
//chain.Add_TX(&tx)
// we should add TX to pool
}
buf = buf[tx_len:] // setup for next tx
}
}
}

+ 178
- 0
p2p/bc_notify_request_chain_handler.go

@ -0,0 +1,178 @@
package p2p
import "github.com/romana/rlog"
import "github.com/deroproject/derosuite/globals"
import "github.com/deroproject/derosuite/crypto"
// when 2 peers communiate either both are in sync or async
// if async, reply to the request below, with your state and other will supply you list of block ids
// handle BC_NOTIFY_REQUEST_CHAIN
func Handle_BC_Notify_Chain(connection *Connection,
i_command_header *Levin_Header, buf []byte) {
// deserialize data header
var i_data_header Levin_Data_Header // incoming data header
err := i_data_header.DeSerialize(buf)
if err != nil {
connection.logger.Debugf("We should destroy connection here, data header cnot deserialized")
connection.Exit = true
return
}
buf = i_data_header.Data[11:] // 11 bytes boost header, ignore it
// decode remain data length ( though we know it from buffer size, but still verify it )
data_length, done := Decode_Boost_Varint(buf)
buf = buf[done:]
if data_length == 0 {
rlog.Tracef(4, "Peer says it does not have even genesis block, so disconnect")
connection.Exit = true
return
}
if (data_length % 32) != 0 { // sanity check
rlog.Tracef(4, "We should destroy connection here, packet mismatch")
connection.Exit = true
return
}
rlog.Tracef(4, "Number of hashes %d \n", data_length/32)
var block_list []crypto.Hash
for i := uint64(0); i < data_length/32; i++ {
var bhash crypto.Hash
copy(bhash[:], buf[i*32:(i+1)*32])
block_list = append(block_list, bhash)
rlog.Tracef(5,"%2d hash %x\n", i, bhash[:])
}
// the data is like this, first 10 blocks, then block are in 2^n power and the last block is genesis
// make sure the genesis block is same
if block_list[len(block_list)-1] != globals.Config.Genesis_Block_Hash {
connection.logger.Debugf("Peer's genesis block is different from our, so disconnect")
connection.Exit = true
return
}
// we must give user our version of the chain
start_height := uint64(0)
for i := 0; i < len(block_list); i++ { // find the common point in our chain
if chain.Block_Exists(block_list[i]) {
start_height = chain.Load_Height_for_BL_ID(block_list[i])
rlog.Tracef(4,"Found common point in chain at hash %x\n", block_list[i])
break
}
}
// send atleast 16001 block or till the top
stop_height := chain.Get_Height()
if (stop_height - start_height) > 1001 { // send MAX 512 KB block hashes
stop_height = start_height + 1002
}
block_list = block_list[:0]
for i := start_height; i < stop_height; i++ {
hash, _ := chain.Load_BL_ID_at_Height(i)
block_list = append(block_list, hash)
}
rlog.Tracef(2,"Prepared list of %d block header to send \n", len(block_list))
Send_BC_Notify_Response_Chain_Entry(connection, block_list, start_height, chain.Get_Height(), 1)
}
// header from boost packet
//0060 00 00 00 01 11 01 01 01 01 02 01 [] 01 04 09 62 6c ..............bl
//0070 6f 63 6b 5f 69 64 73 0a [] 81 0a 9b a2 3e fe 50 5f ock_ids.....>.P_
// send the Peer our chain status, so he can give us the latest chain or updated block_ids
// this is only sent when we are in different from peers
func Send_BC_Notify_Chain_Command(connection *Connection) {
connection.Lock()
header_bytes := []byte{ /*0x01, 0x04,*/ 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x73, 0x0a}
_ = header_bytes
// now append boost variant length, and then append all the hashes
var block_list []crypto.Hash
// add your blocks here
our_height := chain.Get_Height()
if our_height < 20 { // if height is less than 20, fetch from genesis block
} else { // send blocks in reverse
for i := uint64(1); our_height < 11; i++ {
hash, err := chain.Load_BL_ID_at_Height(our_height - i)
_ = err
block_list = append(block_list, hash)
}
our_height = our_height - 11
// now seend all our block id in log, 2nd, 4th, 8th, etc
for ; our_height > 0; our_height = our_height >> 1 {
hash, err := chain.Load_BL_ID_at_Height(our_height)
_ = err
block_list = append(block_list, hash)
}
}
// final block is always genesis block
block_list = append(block_list, globals.Config.Genesis_Block_Hash)
buf := make([]byte, 8, 8)
done := Encode_Boost_Varint(buf, uint64(len(block_list)*32)) // encode length of buffer
buf = buf[:done]
var o_command_header Levin_Header
var o_data_header Levin_Data_Header
o_data_header.Data = append(header_bytes, buf...)
// convert and append all hashes to bytes
for _, hash := range block_list {
o_data_header.Data = append(o_data_header.Data, hash[:32]...)
}
o_data_bytes, _ := o_data_header.Serialize()
o_data_bytes[9] = 0x4
o_command_header.CB = uint64(len(o_data_bytes))
o_command_header.Command = BC_NOTIFY_REQUEST_CHAIN
o_command_header.ReturnData = false
o_command_header.Flags = LEVIN_PACKET_REQUEST
o_command_header_bytes, _ := o_command_header.Serialize()
connection.Conn.Write(o_command_header_bytes)
connection.Conn.Write(o_data_bytes)
//fmt.Printf("len of command header %d\n", len(o_command_header_bytes))
//fmt.Printf("len of data header %d\n", len(o_data_bytes))
connection.Unlock()
}

+ 337
- 0
p2p/bc_notify_request_getobjects.go

@ -0,0 +1,337 @@
package p2p
import "fmt"
import "bytes"
import "encoding/binary"
import "github.com/romana/rlog"
import "github.com/deroproject/derosuite/crypto"
import "github.com/deroproject/derosuite/blockchain"
// The peer triggers this it wants some blocks or txs
func Handle_BC_Notify_Request_GetObjects(connection *Connection,
i_command_header *Levin_Header, buf []byte) {
// deserialize data header
var i_data_header Levin_Data_Header // incoming data header
err := i_data_header.DeSerialize(buf)
if err != nil {
connection.logger.Debugf("We should destroy connection here, data header cnot deserialized")
connection.Exit = true
return
}
pos := bytes.Index(buf, []byte("\x06blocks\x0a")) // at this point to
if pos < 0 {
rlog.Tracef(4, "NOTIFY_REQUEST_GET_OBJECTS doesnot contains blocks. Disconnect peer \n")
} else { // we have some block ids, extract and serve them
tmp_slice := buf[pos+8:]
rlog.Tracef(4, " varint %x", tmp_slice[:8])
data_length, done := Decode_Boost_Varint(tmp_slice)
tmp_slice = tmp_slice[done:]
if data_length == 0 {
rlog.Tracef(4, "Peer says it does not have even genesis block, so disconnect")
connection.Exit = true
return
}
rlog.Tracef(4, "Data size %d", data_length)
if (data_length % 32) != 0 { // sanity check
rlog.Tracef(4, "We should destroy connection here, packet mismatch")
connection.Exit = true
return
}
rlog.Tracef(4, "Number of hashes %d tmp_slice %x \n", data_length/32, tmp_slice[:32])
var block_list []crypto.Hash
for i := uint64(0); i < data_length/32; i++ {
var bhash crypto.Hash
copy(bhash[:], tmp_slice[i*32:(i+1)*32])
block_list = append(block_list, bhash)
rlog.Tracef(4,"%2d hash %x\n", i, bhash[:])
}
// send each block independently
/*if len(block_list) == 1 {
Send_Single_Block_to_Peer(connection, block_list[0])
} else*/ { // we need to send al blocks in 1 go
Send_Blocks_to_Peer(connection, block_list)
}
}
// we must give user data
}
func boost_serialisation_block(hash crypto.Hash) []byte {
block_header := []byte{0x04, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x0a}
txs_header := []byte{0x03, 0x74, 0x78, 0x73, 0x8a}
bl, err := chain.Load_BL_FROM_ID(hash)
_ = err
if len(bl.Tx_hashes) >= 1 {
block_header[0] = 0x8
}
block_serialized := bl.Serialize()
// add a varint len
buf := make([]byte, 8, 8)
done := Encode_Boost_Varint(buf, uint64(len(block_serialized))) // encode length of buffer
buf = buf[:done]
block_header = append(block_header, buf...)
block_header = append(block_header, block_serialized...)
if len(bl.Tx_hashes) >= 1 {
block_header = append(block_header, txs_header...)
// add txs length
buf := make([]byte, 8, 8)
done := Encode_Boost_Varint(buf, uint64(len(bl.Tx_hashes))) // encode length of buffer
buf = buf[:done]
block_header = append(block_header, buf...)
for i := range bl.Tx_hashes {
tx, err := chain.Load_TX_FROM_ID(bl.Tx_hashes[i])
if err != nil {
rlog.Tracef(1,"ERR Cannot load tx from DB\n")
return block_header
}
tx_serialized := tx.Serialize()
buf := make([]byte, 8, 8)
done := Encode_Boost_Varint(buf, uint64(len(tx_serialized))) // encode length of buffer
buf = buf[:done]
block_header = append(block_header, buf...)
block_header = append(block_header, tx_serialized...)
}
}
return block_header
}
func Send_Blocks_to_Peer(connection *Connection, block_list []crypto.Hash) {
blocks_header := []byte{0x01, 0x11, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x08, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
0x73, 0x8c} // this is followed by a varint count of blocks
trailer := []byte{0x19, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62,
0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x05}
buf := make([]byte, 8, 8)
binary.LittleEndian.PutUint64(buf, chain.Get_Height())
trailer = append(trailer, buf...)
done := Encode_Boost_Varint(buf, uint64(len(block_list))) // encode length of buffer
buf = buf[:done]
result := append(blocks_header, buf...)
for i := range block_list {
block := boost_serialisation_block(block_list[i])
result = append(result, block...)
}
result = append(result, trailer...)
var o_command_header Levin_Header
o_command_header.CB = uint64(len(result))
o_command_header.Command = BC_NOTIFY_RESPONSE_GET_OBJECTS
o_command_header.ReturnData = false
o_command_header.Flags = LEVIN_PACKET_REQUEST
o_command_header_bytes, _ := o_command_header.Serialize()
connection.Conn.Write(o_command_header_bytes)
connection.Conn.Write(result)
}
func Send_Single_Block_to_Peer(connection *Connection, hash crypto.Hash) {
bl, err := chain.Load_BL_FROM_ID(hash)
if err == nil {
if len(bl.Tx_hashes) == 0 {
Send_Block_with_ZERO_TX(connection, bl)
} else {
Send_Block_with_TX(connection, bl)
}
}
}
/*
header
00009F94 01 11 01 01 01 01 02 01 01 08 06 62 6c 6f 63 6b ........ ...block
00009FA4 73 8c 04 08 05 62 6c 6f 63 6b 0a s....blo ck......
trailer
000173C4 19 63 75 72 72 65 6e 74 5f 62 nm.'...c urrent_b
000173D4 6c 6f 63 6b 63 68 61 69 6e 5f 68 65 69 67 68 74 lockchai n_height
000173E4 05 ec 04 00 00 00 00 00 00
*/
// if a block is with out TX, send it in this format
func Send_Block_with_ZERO_TX(connection *Connection, bl *blockchain.Block) {
fmt.Printf("sending block with zero tx")
header := []byte{0x01, 0x11, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x08, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b,
0x73, 0x8c, 0x04, 0x04, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x0a}
trailer := []byte{0x19, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x62,
0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x05}
buf := make([]byte, 8, 8)
binary.LittleEndian.PutUint64(buf, chain.Get_Height())
trailer = append(trailer, buf...)
block_serialized := bl.Serialize()
done := Encode_Boost_Varint(buf, uint64(len(block_serialized))) // encode length of buffer
buf = buf[:done]
header = append(header, buf...)
header = append(header, block_serialized...)
header = append(header, trailer...)
var o_command_header Levin_Header
o_command_header.CB = uint64(len(header))
o_command_header.Command = BC_NOTIFY_RESPONSE_GET_OBJECTS
o_command_header.ReturnData = false
o_command_header.Flags = LEVIN_PACKET_REQUEST
o_command_header_bytes, _ := o_command_header.Serialize()
connection.Conn.Write(o_command_header_bytes)
connection.Conn.Write(header)
}
// if a block is with TX, send it in this format
func Send_Block_with_TX(connection *Connection, bl *blockchain.Block) {
panic("Sending block with TX not implmented\n")
}
// header from boost packet
//0060 00 00 00 01 11 01 01 01 01 02 01 [] 01 04 09 62 6c ..............bl
//0070 6f 63 6b 5f 69 64 73 0a [] 81 0a 9b a2 3e fe 50 5f ock_ids.....>.P_
// send the peer the blocks hash and transactions hash, that we need
// this function, splits the request and serves
// we split the request into 2 requests
func Send_BC_Notify_Request_GetObjects(connection *Connection, block_list []crypto.Hash, tx_list []crypto.Hash) {
connection.Lock()
txs_header_bytes := []byte{0x03, 't', 'x', 's', 0x0a}
blocks_header_bytes := []byte{0x06, 'b', 'l', 'o', 'c', 'k', 's', 0x0a}
// now append boost variant length, and then append all the hashes
// add your blocks here
// adding genesis block
//block_list = append(block_list,globals.Config.Genesis_Block_Hash)
/*for i := 0; i < 100;i++{
block_list = append(block_list,globals.Config.Genesis_Block_Hash)
}*/
// split block request in independent request , so the response comes independent
if len(block_list) > 0 {
rlog.Tracef(4, "Sending block request for %d blocks \n", len(block_list))
for i := range block_list {
buf := make([]byte, 8, 8)
done := Encode_Boost_Varint(buf, uint64(32)) // encode length of buffer
buf = buf[:done]
var o_command_header Levin_Header
var o_data_header Levin_Data_Header
o_data_header.Data = append(blocks_header_bytes, buf...)
// convert and append all hashes to bytes
o_data_header.Data = append(o_data_header.Data, block_list[i][:32]...)
o_data_bytes, _ := o_data_header.Serialize()
o_data_bytes[9] = 0x4
o_command_header.CB = uint64(len(o_data_bytes))
o_command_header.Command = BC_NOTIFY_REQUEST_GET_OBJECTS
o_command_header.ReturnData = false
o_command_header.Flags = LEVIN_PACKET_REQUEST
o_command_header_bytes, _ := o_command_header.Serialize()
connection.Conn.Write(o_command_header_bytes)
connection.Conn.Write(o_data_bytes)
}
}
if len(tx_list) > 0 {
rlog.Tracef(4, "Sending tx request for %d tx \n", len(tx_list))
buf := make([]byte, 8, 8)
done := Encode_Boost_Varint(buf, uint64(len(tx_list)*32)) // encode length of buffer
buf = buf[:done]
var o_command_header Levin_Header
var o_data_header Levin_Data_Header
o_data_header.Data = append(txs_header_bytes, buf...)
// convert and append all hashes to bytes
for _, hash := range tx_list {
o_data_header.Data = append(o_data_header.Data, hash[:32]...)
}
o_data_bytes, _ := o_data_header.Serialize()
o_data_bytes[9] = 0x4
o_command_header.CB = uint64(len(o_data_bytes))
o_command_header.Command = BC_NOTIFY_REQUEST_GET_OBJECTS
o_command_header.ReturnData = false
o_command_header.Flags = LEVIN_PACKET_REQUEST
o_command_header_bytes, _ := o_command_header.Serialize()
connection.Conn.Write(o_command_header_bytes)
connection.Conn.Write(o_data_bytes)
}
//fmt.Printf("len of command header %d\n", len(o_command_header_bytes))
//fmt.Printf("len of data header %d\n", len(o_data_bytes))
connection.Unlock()
}

+ 227
- 0
p2p/bc_notify_response_chain_handler.go

@ -0,0 +1,227 @@
package p2p
import "bytes"
import "encoding/binary"
import "github.com/romana/rlog"
import "github.com/deroproject/derosuite/globals"
import "github.com/deroproject/derosuite/crypto"
/* the data structure which needs to be serialised is defined in cryptonote_protocol_defs.h
* struct request
{
uint64_t start_height;
uint64_t total_height;
uint64_t cumulative_difficulty;
std::list<crypto::hash> m_block_ids;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(cumulative_difficulty)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids)
KV_SERIALIZE(start_height)
KV_SERIALIZE(total_height)
END_KV_SERIALIZE_MAP()
};
*/
// This request only comes when we have sent the BC_NOTIFY_REQUEST_CHAIN command
// handle BC_NOTIFY_RESPONSE_CHAIN_ENTRY
func Handle_BC_Notify_Response_Chain_Entry(connection *Connection,
i_command_header *Levin_Header, buf []byte) {
// deserialize data header
var i_data_header Levin_Data_Header // incoming data header
err := i_data_header.DeSerialize(buf)
if err != nil {
//fmt.Printf("We should destroy connection here, data header cnot deserialized")
rlog.Tracef(4, "Data header deserialisation failed. Disconnect peer \n")
connection.Exit = true
return
}
pos := bytes.Index(i_data_header.Data, []byte("cumulative_difficulty")) // at this point to
if pos == -1 {
rlog.Tracef(4, "Cumulative difficulty deserialisation failed. Disconnect peer \n")
connection.Exit = true
return
}
cumulative_difficulty := binary.LittleEndian.Uint64(i_data_header.Data[pos+22:])
rlog.Tracef(4, "Cumalative difficulty %d %x\n", cumulative_difficulty, cumulative_difficulty)
pos = bytes.Index(i_data_header.Data, []byte("start_height")) // at this point to
if pos == -1 {
panic("start_height not found, its mandatory\n")
}
start_height := binary.LittleEndian.Uint64(i_data_header.Data[pos+13:])
rlog.Tracef(4, "start_height %d %x\n", start_height, start_height)
pos = bytes.Index(i_data_header.Data, []byte("m_block_ids")) // at this point to
if pos == -1 {
panic("m_block_ids not found, its mandatory\n")
}
// decode data length ( though we know it from buffer size, but still verify it )
buf = i_data_header.Data[pos+11+1:]
data_length, done := Decode_Boost_Varint(buf)
//fmt.Printf("data length %d , hex %x\n", data_length, buf[:8])
buf = buf[done:]
if data_length == 0 {
rlog.Tracef(4, "Peer says it does not have even genesis block, so disconnect")
connection.Exit = true
return
}
if (data_length % 32) != 0 { // sanity check
rlog.Tracef(2, "We should destroy connection here, packet mismatch")
connection.Exit = true
return
}
rlog.Tracef(4, "Number of Blocks id in chain BC_NOTIFY_RESPONSE_CHAIN_ENTRY %d \n", data_length/32)
var block_list []crypto.Hash
for i := uint64(0); i < data_length/32; i++ {
var bhash crypto.Hash
copy(bhash[:], buf[i*32:(i+1)*32])
// only request block that we donot have
if chain.Get_Height() < 20 {
block_list = append(block_list, bhash)
rlog.Tracef(5, "%2d hash %x\n", i, bhash[:])
} else {
if !chain.Block_Exists(bhash) {
block_list = append(block_list, bhash)
rlog.Tracef(5, "%2d hash %x\n", i, bhash[:])
}
}
}
// server will kill us, if we queue more than 1000 blocks
if len(block_list) > 400 {
block_list = block_list[:399]
}
// make sure the genesis block is same
// if peer provided us a genesis block make sure, its ours
if start_height == 0 && block_list[0] != globals.Config.Genesis_Block_Hash {
rlog.Tracef(4, "Peer's genesis block is different from our, so disconnect")
connection.Exit = true
return
}
// we must queue the hashes so as to fetch them
//block_list = block_list[:0]
var hash crypto.Hash
/*
//big_block ,_ := hex.DecodeString("9ba23efe505f9674dc24c150edbdbe57abc3ec6636aa4c1659e811b389c0b30b") // zero tx
//big_block ,_ := hex.DecodeString("14371eeddca0f3ce9b992b3e2a0e482920497d87dd20002456f3a844b04a3318") // single tx
big_block ,_ := hex.DecodeString("a31a17bb26b2ec37479ee3a02f53dd94860611457811d0c23ce892f4e87e1697") // 4 tx
copy(hash[:],big_block[:32])
block_list = append(block_list, hash)
big_tx, _ := hex.DecodeString("af6f12d56f32f58623a834c4f12c5443976346a2f877ef78798c39496bd00559") // single tx
var tx_list []ringct.Hash
copy(hash[:],big_tx[:32])
tx_list = append(tx_list, hash)
*/
_ = hash
var tx_list []crypto.Hash
/*if len(block_list) > 5 {
block_list= block_list[:5]
}*/
Send_BC_Notify_Request_GetObjects(connection, block_list, tx_list[:0])
}
// header from boost packet
/* header bytes
0000 01 11 01 01 01 01 02 01 01 10 15 63 75 6d 75 6c ...........cumul
0010 61 74 69 76 65 5f 64 69 66 66 69 63 75 6c 74 79 ative_difficulty
0020 05 ab 61 00 00 00 00 00 00 0b 6d 5f 62 6c 6f 63 ..a.......m_bloc
0030 6b 5f 69 64 73 0a 82 72 02 00 63 34 12 de 21 ea k_ids..r..c4..!.
// suffix_bytes
44d0 4c 16 59 e8 11 b3 89 c0 b3 0b 0c 73 74 61 72 74 L.Y........start
44e0 5f 68 65 69 67 68 74 05 00 00 00 00 00 00 00 00 _height.........
44f0 0c 74 6f 74 61 6c 5f 68 65 69 67 68 74 05 e5 04 .total_height...
4500 00 00 00 00 00 00 ......
*/
// send the PEER the blocks he needs to download our version of chain
// this is send every 5 seconds
func Send_BC_Notify_Response_Chain_Entry(connection *Connection, block_list []crypto.Hash, start_height, current_height, diff uint64) {
connection.Lock()
header_bytes := []byte{0x01, 0x11, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x10, 0x15, 0x63, 0x75, 0x6d, 0x75, 0x6c,
0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79,
0x05, 0xab, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x6d, 0x5f, 0x62, 0x6c, 0x6f, 0x63,
0x6b, 0x5f, 0x69, 0x64, 0x73, 0x0a}
suffix_bytes := []byte{0x0c, 0x73, 0x74, 0x61, 0x72, 0x74,
0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x05, 0xe5, 0x04,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
binary.LittleEndian.PutUint64(header_bytes[33:], diff)
binary.LittleEndian.PutUint64(suffix_bytes[14:], start_height)
binary.LittleEndian.PutUint64(suffix_bytes[36:], current_height)
// now append boost variant length, and then append all the hashes
buf := make([]byte, 8, 8)
done := Encode_Boost_Varint(buf, uint64(len(block_list)*32)) // encode length of buffer
buf = buf[:done]
var o_command_header Levin_Header
data_bytes := append(header_bytes, buf...)
// convert and append all hashes to bytes
for _, hash := range block_list {
data_bytes = append(data_bytes, hash[:32]...)
}
data_bytes = append(data_bytes, suffix_bytes...)
o_command_header.CB = uint64(len(data_bytes))
o_command_header.Command = BC_NOTIFY_RESPONSE_CHAIN_ENTRY
o_command_header.ReturnData = false
o_command_header.Flags = LEVIN_PACKET_REQUEST
o_command_header_bytes, _ := o_command_header.Serialize()
connection.Conn.Write(o_command_header_bytes)
connection.Conn.Write(data_bytes)
//fmt.Printf("len of command header %d\n", len(o_command_header_bytes))
//fmt.Printf("len of data header %d\n", len(data_bytes))
connection.Unlock()
}

+ 128
- 0
p2p/bc_notify_response_get_objects.go

@ -0,0 +1,128 @@
package p2p
import "bytes"
import "github.com/romana/rlog"
import log "github.com/sirupsen/logrus"
import "github.com/deroproject/derosuite/blockchain"
// if the incoming blob contains block with included transactions
//00009F94 01 11 01 01 01 01 02 01 01 08 06 62 6c 6f 63 6b ........ ...block
//00009FA4 73 8c 04 08 05 62 6c 6f 63 6b 0a fd 03 06 06 cd s....blo ck......
// if the incoming blob contains block without any tx
//00009EB4 01 11 01 01 01 01 02 01 01 08 06 62 6c 6f 63 6b ........ ...block
//00009EC4 73 8c 08 04 05 62 6c 6f 63 6b 0a e5 01 01 00 00 s....blo ck......
// if the incoming blob only contains a TX
// FIXME this code can also be shared by NOTIFY_NEW_BLOCK, NOTIFY_NEW_TRANSACTIONS
// we trigger this if we want to request any TX or block from the peer
func Handle_BC_Notify_Response_GetObjects(connection *Connection,
i_command_header *Levin_Header, buf []byte) {
var bl blockchain.Block
complete_block := false
// deserialize data header
var i_data_header Levin_Data_Header // incoming data header
err := i_data_header.DeSerialize(buf)
if err != nil {
log.Debugf("We should destroy connection here, data header cnot deserialized")
return
}
// check whether the response contains block
pos := bytes.Index(i_data_header.Data, []byte("blocks")) // at this point to
buf = i_data_header.Data
if pos > 0 { // the data contains atleast 1 block
pos += 6
buf = i_data_header.Data[pos:]
pos := bytes.Index(buf, []byte("block"))
// find inner position of block
pos = pos + 6 // jump to varint length position and decode
buf = buf[pos:]
block_length, done := Decode_Boost_Varint(buf)
rlog.Tracef(9, "Block length %d %x\n", block_length, buf[:8])
buf = buf[done:]
block_buf := buf[:block_length]
err = bl.Deserialize(block_buf)
if err != nil {
log.Debugf("Block could not be deserialized successfully err %s\n", err)
log.Debugf("We should destroy connection here, block not deserialized")
return
}
hash := bl.GetHash()
rlog.Tracef(9, "Block deserialized successfully %x\n", hash[:32])
rlog.Tracef(9, "Tx hash length %d\n", len(bl.Tx_hashes))
for i := range bl.Tx_hashes {
rlog.Tracef(9, "%d tx %x\n", i, bl.Tx_hashes[i][:32])
}
// point buffer to check whether any more tx exist
buf = buf[block_length:]
complete_block = true
}
pos = bytes.Index(buf, []byte("\x03txs\x8a")) // at this point to
if pos > -1 {
rlog.Tracef(9, "txt pos %d\n", pos)
buf = buf[pos+5:]
// decode remain data length ( though we know it from buffer size, but still verify it )
tx_count, done := Decode_Boost_Varint(buf)
buf = buf[done:]
for i := uint64(0); i < tx_count; i++ {
var tx blockchain.Transaction
tx_len, done := Decode_Boost_Varint(buf)
buf = buf[done:]
rlog.Tracef(9, "tx count %d i %d tx_len %d\n", tx_count, i, tx_len)
tx_bytes := buf[:tx_len]
// deserialize and verrify transaction
err = tx.DeserializeHeader(tx_bytes)
if err != nil {
log.Debugf("Transaction could not be deserialized\n")
} else {
hash := tx.GetHash()
rlog.Tracef(9, "Transaction deserialised successfully hash %x\n", hash[:32])
// add tx to block chain, we must verify that the tx has been mined
chain.Add_TX(&tx)
}
buf = buf[tx_len:] // setup for next tx
}
}
// at this point, if it's a block we should try to add it to block chain
if complete_block {
// add block to chain
log.Debugf("Found a block we should add it to our chain\n")
chain.Chain_Add(&bl)
}
// add all transaction to chains
}

+ 72
- 0
p2p/boost.go

@ -0,0 +1,72 @@
package p2p
import "encoding/binary"
// boost varint can encode upto 1 gb size
func Decode_Boost_Varint(buf []byte) (uint64, int) {
first_byte := buf[0]
num_bytes := first_byte & 3 // lower 2 bits contain bytes count, which follow current byte
// grab full 4 bytes
value := binary.LittleEndian.Uint32(buf)
switch num_bytes {
case 0:
value &= 0xff
case 1:
value &= 0xffff
case 2:
value &= 0xffffff
case 3:
value &= 0xffffffff
}
value = value >> 2 // discard lower 2 bits
if num_bytes == 2 { // holy hell, let god have mercy on this boost parser
num_bytes = 3
}
return uint64(value), int(num_bytes + 1)
}
// this function encodes Value to bu in boost varint style
func Encode_Boost_Varint(buf []byte, Value uint64) int {
bytes_required := byte(0)
switch {
case Value > 1073741823:
panic("Exceeded boost varint capacity while encoding\n") // (2^30) -1
case Value > 4194303:
bytes_required = 4
case Value > 16383:
bytes_required = 3
case Value > 63:
bytes_required = 2
default:
bytes_required = 1
}
first_byte := (Value % 64) << 2
Value = Value >> 6
second_byte := Value % 256
Value = Value >> 8
third_byte := Value % 256
Value = Value >> 8
fourth_byte := Value % 256
Value = Value >> 8
// encode bytes length in lower 2 bits of first byte
first_byte |= uint64(byte(bytes_required - 1))
buf[0] = byte(first_byte)
buf[1] = byte(second_byte)
buf[2] = byte(third_byte)
buf[3] = byte(fourth_byte)
if bytes_required == 3 { // thank god we are soon going to migrate from boost hell, once and for all
bytes_required = 4
}
return int(bytes_required)
}

+ 74
- 0
p2p/boost_test.go

@ -0,0 +1,74 @@
package p2p
import "testing"
/* this func decode boost variant
*
* 0 0a 60 == (60 >> 2 ) = 24 bytes
* 1 0a c0 == (c0 >> 2 ) = 48 bytes
* 2 0a 21 01 == (121 >> 2 ) = 72
* 3 0a 81 01 == (181 >> 2 ) = 96 bytes
* 4 0a e1 01
* 5 0a 41 02
* 7 0a 01 03
* 8 0a 61 03
* 9 0a c1 03
*
*
*
* */
func Test_Boost_Varint_Serdes(t *testing.T) {
buf := make([]byte, 8, 8)
bytes_required := Encode_Boost_Varint(buf, 24)
if bytes_required != 1 || buf[0] != 0x60 {
t.Errorf("Single bytes boost varint encode test failed %d", buf[0])
}
// decode and test
value, bytes_required := Decode_Boost_Varint(buf)
if value != 24 || bytes_required != 1 {
t.Errorf("Single bytes boost varint decode test failed value %d bytes %d", value, bytes_required)
}
// 2 bytes test
bytes_required = Encode_Boost_Varint(buf, 72)
if bytes_required != 2 || buf[0] != 0x21 || buf[1] != 1 {
t.Errorf("2 bytes boost varint encode test failed")
}
// decode and test
value, bytes_required = Decode_Boost_Varint(buf)
if value != 72 || bytes_required != 2 {
t.Errorf("2 bytes boost varint decode test failed")
}
bytes_required = Encode_Boost_Varint(buf, 6000)
if bytes_required != 2 || buf[0] != 0xc1 || buf[1] != 0x5d {
t.Errorf("2 bytes boost varint encode test failed")
}
// decode and test
value, bytes_required = Decode_Boost_Varint(buf)
if value != 6000 || bytes_required != 2 {
t.Errorf("2 bytes boost varint decode test failed")
}
// 3 bytes test
bytes_required = Encode_Boost_Varint(buf, 40096)
if bytes_required != 4 || buf[0] != 0x82 || buf[1] == 0x72 && buf[2] != 0x2 && buf[3] == 0 {
t.Errorf("3 bytes boost varint encode test failed")
}
// decode and test
value, bytes_required = Decode_Boost_Varint(buf)
if value != 40096 || bytes_required != 4 {
t.Errorf("3 bytes boost varint decode test failed")
}
}

+ 194
- 0
p2p/command_handler_p2p_handshake.go

@ -0,0 +1,194 @@
package p2p
import "fmt"
import "net"
import "time"
import "github.com/romana/rlog"
import log "github.com/sirupsen/logrus"
import "github.com/deroproject/derosuite/globals"
// the connection starts with P2P handshake
// send the hand shake
func Send_Handshake(connection *Connection) {
// first request support flags
if connection.Exit {
return
}
Send_SupportFlags_Command(connection)
connection.Lock()
// lets do handshake
var d Node_Data
var c CORE_DATA
var data_header Levin_Data_Header
var levin_header Levin_Header
d.Network_UUID = globals.Config.Network_ID
d.Peer_ID = (uint64)(time.Now().Unix())
c.Current_Height = chain.Get_Height()
c.Cumulative_Difficulty = chain.Get_Difficulty()
c.Top_Version = 6
// top block id from the genesis or other block
c.Top_ID = chain.Get_Top_ID()
ds, _ := d.Serialize()
cs, _ := c.Serialize()
data_header.Data = ds
data_header.Data = append(data_header.Data, cs...)
levin_data, _ := data_header.Serialize()
levin_header.CB = uint64(len(levin_data))
levin_header.Command = P2P_COMMAND_HANDSHAKE
levin_header.ReturnData = true
levin_header.Flags = LEVIN_PACKET_REQUEST
header_bytes, _ := levin_header.Serialize()
connection.Conn.Write(header_bytes)
connection.Conn.Write(levin_data)
connection.Command_queue.PushBack(uint32(P2P_COMMAND_HANDSHAKE))
connection.Unlock()
}
// handle P2P_COMMAND_HANDSHAKE,
// we must send a response
// our response is a boost compatible response which is parseable by old cryptonote daemons
func Handle_P2P_Handshake_Command(connection *Connection,
i_command_header *Levin_Header, buf []byte) {
connection.logger.Infof("Handshake request arrived, we must parse it")
// extract peers from our list, and insert them into the response
// max 250 peers can be send ( since we are aiming compatibility with old daemons)
var reply Node_Data_Response
//panic("Handle_P2P_Handshake needs to fixed and tested")
reply.NodeData.Network_UUID = globals.Config.Network_ID
reply.NodeData.Peer_ID = (uint64)(OUR_PEER_ID)
reply.NodeData.Local_time = uint64(time.Now().Unix())
reply.CoreData.Current_Height = chain.Get_Height()
reply.CoreData.Cumulative_Difficulty = chain.Get_Difficulty()
reply.CoreData.Top_Version = 6
// top block id from the genesis or other block
// this data is from the block chain
// this data must be the top block that we see till now
reply.CoreData.Top_ID = chain.Get_Top_ID()
for i := 0; i < 250; i++ {
reply.PeerArray = append(reply.PeerArray,
Peer_Info{IP: net.IPv4(byte(i), byte(i), byte(i), byte(i)), Port: 0, ID: 0, LastSeen: 0})
}
var o_command_header Levin_Header
var o_data_header Levin_Data_Header
o_data_header.Data, _ = reply.Serialize()
data, _ := o_data_header.Serialize()
// mark as containing 4 elements
data[9] = 0x10
o_command_header.CB = uint64(len(data))
o_command_header.Command = P2P_COMMAND_HANDSHAKE
o_command_header.ReturnData = false
o_command_header.ReturnCode = 1 // send as good response
o_command_header.Flags = LEVIN_PACKET_RESPONSE
o_command_header_bytes, _ := o_command_header.Serialize()
connection.Conn.Write(o_command_header_bytes)
connection.Conn.Write(data)
rlog.Tracef(4, "Sending handshake response\n")
Handle_P2P_Handshake_Command_Response(connection, i_command_header,buf) // parse incoming response
}
/* handles response of our p2p command, parses data etc*/
func Handle_P2P_Handshake_Command_Response(connection *Connection,
i_command_header *Levin_Header, buf []byte) {
var reply Node_Data_Response
// deserialize data header
var i_data_header Levin_Data_Header // incoming data header
err := i_data_header.DeSerialize(buf)
if err != nil {
connection.logger.WithFields(log.Fields{
"ip": connection.Addr.IP,
}).Debugf("Disconnecting client, handshake could not be deserialized")
return
}
if reply.DeSerialize(i_data_header.Data) != nil {
logger.WithFields(log.Fields{
"ip": connection.Addr.IP,
}).Debugf("Disconnecting client, handshake could not be deserialized")
return
}
if reply.NodeData.Network_UUID != globals.Config.Network_ID {
logger.WithFields(log.Fields{
"ip": connection.Addr.IP,
"id": reply.NodeData.Network_UUID,
}).Debugf("Disconnecting client, Wrong network ID")
return
}
// we need to kick the peer if the height is something specific and peer id is less than ours
// TODO right we are not doing it
connection.Peer_ID = reply.NodeData.Peer_ID
connection.Port = reply.NodeData.Local_Port
connection.Last_Height = reply.CoreData.Current_Height
connection.Top_Version = uint64(reply.CoreData.Top_Version)
connection.Top_ID = reply.CoreData.Top_ID
connection.Cumulative_Difficulty = reply.CoreData.Cumulative_Difficulty
connection.State = ACTIVE
connection.logger.WithFields(log.Fields{
"PeerHeight": reply.CoreData.Current_Height,
"Top Version": reply.CoreData.Top_Version,
"Top_ID": fmt.Sprintf("%x", reply.CoreData.Top_ID),
}).Debugf("Successful Handshake with Peer")
// lets check whether we need to resync with this peer
if chain.IsLagging(reply.CoreData.Cumulative_Difficulty, reply.CoreData.Current_Height, reply.CoreData.Top_ID) {
logger.Debugf("We need to resync with the peer")
// set mode to syncronising
Send_BC_Notify_Chain_Command(connection)
}
}

+ 68
- 0
p2p/command_handler_p2p_supportflags.go

@ -0,0 +1,68 @@
package p2p
import "encoding/binary"
var FLAGS_VALUE uint32 = 0 // we donot support fluffly blocks at this point in time
var support_response_bytes = [29]byte{0x01, 0x11, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x04, 0x0d, 0x73, 0x75, 0x70, 0x70,
0x6f, 0x72, 0x74, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x06, 0x00, 0x00, 0x00, 0x00}
// handle P2P_COMMAND_REQUEST_SUPPORT_FLAGS
func Handle_P2P_Support_Flags(connection *Connection,
i_command_header *Levin_Header, buf []byte) {
// deserialize data header
var i_data_header Levin_Data_Header // incoming data header
err := i_data_header.DeSerialize(buf)
if err != nil {
connection.logger.Debugf("We should destroy connection here, data header cnot deserialized")
connection.Exit = true
return
}
// make sure data is length 10
// create a new response header
var o_command_header Levin_Header
//var o_data_header Levin_Data_Header
binary.LittleEndian.PutUint32(support_response_bytes[25:], FLAGS_VALUE) // packed 8+8+1+4 bytes
o_command_header.CB = uint64(len(support_response_bytes))
o_command_header.Command = P2P_COMMAND_REQUEST_SUPPORT_FLAGS
o_command_header.ReturnData = false
o_command_header.Flags = LEVIN_PACKET_RESPONSE
o_command_header_bytes, _ := o_command_header.Serialize()
connection.Conn.Write(o_command_header_bytes)
connection.Conn.Write(support_response_bytes[:])
}
// send the hand shake
func Send_SupportFlags_Command(connection *Connection) {
connection.Lock()
var o_command_header Levin_Header
var o_data_header Levin_Data_Header
o_data_bytes, _ := o_data_header.Serialize()
o_command_header.CB = uint64(len(o_data_bytes))
o_command_header.Command = P2P_COMMAND_REQUEST_SUPPORT_FLAGS
o_command_header.ReturnData = true
o_command_header.Flags = LEVIN_PACKET_REQUEST
o_command_header_bytes, _ := o_command_header.Serialize()
connection.Conn.Write(o_command_header_bytes)
connection.Conn.Write(o_data_bytes)
connection.Command_queue.PushBack(uint32(P2P_COMMAND_REQUEST_SUPPORT_FLAGS))
connection.Unlock()
}

+ 139
- 0
p2p/command_handler_p2p_timed_sync.go

@ -0,0 +1,139 @@
package p2p
//import "fmt"
import "bytes"
import "time"
import "encoding/binary"
import "github.com/romana/rlog"
import log "github.com/sirupsen/logrus"
// outgoing response needs to be as follows
/*
0020 01 11 01 01 01 01 02 01 01 08 0a 6c 6f 63 61 ............loca
0030 6c 5f 74 69 6d 65 05 c9 ea 45 5a 00 00 00 00 0c l_time...EZ.....
0040 70 61 79 6c 6f 61 64 5f 64 61 74 61 0c 10 15 63 payload_data...c
0050 75 6d 75 6c 61 74 69 76 65 5f 64 69 66 66 69 63 umulative_diffic
0060 75 6c 74 79 05 37 62 00 00 00 00 00 00 0e 63 75 ulty.7b.......cu
0070 72 72 65 6e 74 5f 68 65 69 67 68 74 05 ec 04 00 rrent_height....
0080 00 00 00 00 00 06 74 6f 70 5f 69 64 0a 80 85 d9 ......top_id....
0090 d2 f6 cd ee 1b 87 dd d1 ac 3d 15 db 4d 72 63 ca .........=..Mrc.
00a0 1c 43 37 db 53 78 7f 03 3b 74 f6 fc 45 0e 0b 74 .C7.Sx..;t..E..t
00b0 6f 70 5f 76 65 72 73 69 6f 6e 08 06 op_version..
*/
// handle P2P_COMMAND_TIMED_SYNC_T
func Handle_P2P_Timed_Sync(connection *Connection,
i_command_header *Levin_Header, buf []byte) {
// deserialize data header
var i_data_header Levin_Data_Header // incoming data header
err := i_data_header.DeSerialize(buf)
if err != nil {
log.Debugf("Invalid P2P_COMMAND_TIMED_SYNC_T, disconnecting peer")
connection.Exit = true
return
}
// parse incoming core data
var peer_core_data CORE_DATA
pos := bytes.Index(i_data_header.Data, []byte("payload_data")) // at this point to node data and should be parsed as such
if pos < 0 {
log.Debugf("Invalid P2P_COMMAND_TIMED_SYNC_T, disconnecting peer")
connection.Exit = true
return
}
err = peer_core_data.DeSerialize(i_data_header.Data[pos-1:])
if err != nil {
log.Debugf("Invalid P2P_COMMAND_TIMED_SYNC_T, disconnecting peer")
connection.Exit = true
return
}
rlog.Trace(5, "Incoming core data %+v \n", peer_core_data)
// TODO if cumulative difficulty at this top mismatches ours start resync
// if height is more than ours, start resync
var our_core_data CORE_DATA
// fill the structure with our chain data
our_core_data.Top_ID = chain.Get_Top_ID()
our_core_data.Cumulative_Difficulty = chain.Load_Block_Cumulative_Difficulty(our_core_data.Top_ID) // get cumulative difficulty for top block
our_core_data.Current_Height = chain.Load_Height_for_BL_ID(our_core_data.Top_ID)
our_core_data.Top_Version = 6
serialised_bytes, _ := our_core_data.Serialize()
header_bytes := []byte{0x01, 0x11, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x08, 0x0a, 0x6c, 0x6f, 0x63, 0x61,
0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x05,
/* time bytes start here */ 0xc9, 0xea, 0x45, 0x5a, 0x00, 0x00, 0x00, 0x00}
binary.LittleEndian.PutUint64(header_bytes[22:], uint64(time.Now().Unix()))
//fmt.Printf("header %x serialised_bytes %x\n", header_bytes, serialised_bytes)
response_bytes := append(serialised_bytes, header_bytes...)
// create a new response header
var o_command_header Levin_Header
//var o_data_header Levin_Data_Header
o_command_header.CB = uint64(len(response_bytes))
o_command_header.Command = P2P_COMMAND_TIMED_SYNC
o_command_header.ReturnData = false
o_command_header.Flags = LEVIN_PACKET_RESPONSE
o_command_header_bytes, _ := o_command_header.Serialize()
connection.Conn.Write(o_command_header_bytes)
connection.Conn.Write(response_bytes[:])
connection.Last_Height = peer_core_data.Current_Height
connection.Top_Version = uint64(peer_core_data.Top_Version)
connection.Top_ID = peer_core_data.Top_ID
connection.Cumulative_Difficulty = peer_core_data.Cumulative_Difficulty
connection.State = ACTIVE
// lets check whether we need to resync with this peer
if chain.IsLagging(peer_core_data.Cumulative_Difficulty, peer_core_data.Current_Height, peer_core_data.Top_ID) {
log.Debugf("We need to resync with the peer")
// set mode to syncronising
Send_BC_Notify_Chain_Command(connection)
}
}
/* we will never send this request, so we donot need to parse response
func Send_P2P_Timed_Sync(connection *Connection){
connection.Lock()
var o_command_header Levin_Header
var o_data_header Levin_Data_Header
o_data_bytes,_ := o_data_header.Serialize()
o_command_header.CB = uint64(len(o_data_bytes))
o_command_header.Command = P2P_COMMAND_REQUEST_SUPPORT_FLAGS
o_command_header.ReturnData = true
o_command_header.Flags = LEVIN_PACKET_REQUEST
o_command_header_bytes,_ := o_command_header.Serialize()
connection.Conn.Write(o_command_header_bytes)
connection.Conn.Write(o_data_bytes)
connection.Command_queue.PushBack(uint32(P2P_COMMAND_REQUEST_SUPPORT_FLAGS))
connection.Unlock()
}
*/

+ 130
- 0
p2p/connection_pool.go

@ -0,0 +1,130 @@
package p2p
/* this file implements the connection pool manager, keeping a list of active connections etc
* this will also ensure that a single IP is connected only once
*
*/
import "fmt"
import "net"
import "sync"
import "container/list"
import log "github.com/sirupsen/logrus"
import "github.com/deroproject/derosuite/crypto"
// any connection incoming/outgoing can only be in this state
type Conn_State string
const (
HANDSHAKE_PENDING Conn_State = "Pending"
IDLE = "Idle"
ACTIVE = "Active"
)
// This structure is used to do book keeping for the connection and keeps other DATA related to peer
type Connection struct {
Incoming bool // is connection incoming or outgoing
Addr *net.TCPAddr // endpoint on the other end
Port uint32 // port advertised by other end as its server,if it's 0 server cannot accept connections
Peer_ID uint64 // Remote peer id
Last_Height uint64 // last height sent by peer
Top_Version uint64 // current hard fork version supported by peer
Exit bool // Exit marker that connection needs to be killed
State Conn_State // state of the connection
Top_ID crypto.Hash // top block id of the connection
Cumulative_Difficulty uint64 // cumulative difficulty of top block of peer
logger *log.Entry // connection specific logger
Requested_Objects [][32]byte // currently unused as we sync up with a single peer at a time
Conn net.Conn // actual object to talk
Command_queue *list.List // LEVIN protocol is syncronous
sync.Mutex
}
var connection_map = map[string]*Connection{}
var connection_mutex sync.Mutex
func Key(ip net.IP) string {
return string(ip.To16()) // Simple []byte => string conversion
}
// check whether an IP is in the map already
func IsConnected(ip net.IP) bool {
connection_mutex.Lock()
defer connection_mutex.Unlock()
if _, ok := connection_map[Key(ip)]; ok {
return true
}
return false
}
// add connection to map
func Connection_Add(c *Connection) {
connection_mutex.Lock()
defer connection_mutex.Unlock()
connection_map[Key(c.Addr.IP)] = c
}
// add connection to map
func Connection_Delete(c *Connection) {
connection_mutex.Lock()
defer connection_mutex.Unlock()
delete(connection_map, Key(c.Addr.IP))
}
// prints all the connection info to screen
func Connection_Print() {
connection_mutex.Lock()
defer connection_mutex.Unlock()
fmt.Printf("Connection info for peers\n")
fmt.Printf("%-20s %-16s %-5s %-7s %9s %3s\n", "Remote Addr", "PEER ID", "PORT", " State", "Height","DIR")
for _, v := range connection_map {
dir := "OUT"
if v.Incoming {
dir = "INC"
}
fmt.Printf("%-20s %16x %5d %7s %9d %s\n", v.Addr.IP, v.Peer_ID, v.Port, v.State, v.Last_Height, dir)
}
}
// for continuos update on command line, get the maximum height of all peers
func Best_Peer_Height() (best_height uint64) {
connection_mutex.Lock()
for _, v := range connection_map {
if v.Last_Height > best_height {
best_height = v.Last_Height
}
}
connection_mutex.Unlock()
return
}
// this function return peer count which have successful handshake
func Peer_Count() (Count uint64) {
connection_mutex.Lock()
for _, v := range connection_map {
if v.State != HANDSHAKE_PENDING {
Count++
}
}
connection_mutex.Unlock()
return
}
// this returns count of peers in both directions
func Peer_Direction_Count() (Incoming uint64, Outgoing uint64) {
connection_mutex.Lock()
for _, v := range connection_map {
if v.State != HANDSHAKE_PENDING {
if v.Incoming{
Incoming++
}else{
Outgoing++
}
}
}
connection_mutex.Unlock()
return
}

+ 85
- 0
p2p/controller.go

@ -0,0 +1,85 @@
package p2p
import "net"
import "time"
import "sync/atomic"
import log "github.com/sirupsen/logrus"
import "github.com/deroproject/derosuite/globals"
import "github.com/deroproject/derosuite/blockchain"
var chain *blockchain.Blockchain // external reference to chain
var Exit_Event = make(chan bool) // causes all threads to exit
var Exit_In_Progress bool // marks we are doing exit
var logger *log.Entry // global logger, every logger in this package is a child of this
// Initialize P2P subsystem
func P2P_Init(params map[string]interface{}) error {
logger = globals.Logger.WithFields(log.Fields{"com": "P2P"}) // all components must use this logger
chain = params["chain"].(*blockchain.Blockchain)
go P2P_engine() // start outgoing engine
//go P2P_Server_v1() // start accepting connections
logger.Infof("P2P started")
atomic.AddUint32(&globals.Subsystem_Active, 1) // increment subsystem
return nil
}
func P2P_engine() {
for {
if Exit_In_Progress {
return
}
//remote_addr := "localhost:18090"
//remote_addr := "192.168.56.1:18090"
//remote_addr := "76.74.170.128:18090"
remote_addr := "89.38.97.110:18090"
remote_ip, err := net.ResolveTCPAddr("tcp", remote_addr)
if err != nil {
if Exit_In_Progress {
return
}
logger.Debugf("Resolve address failed:", err.Error())
time.Sleep(2 * time.Second)
continue
}
// since we may be connecting through socks, grab the remote ip for our purpose rightnow
conn, err := globals.Dialer.Dial("tcp", remote_ip.String())
if err != nil {
if Exit_In_Progress {
return
}
logger.Debugf("Dial failed err %s", err.Error())
time.Sleep(2 * time.Second)
continue
}
logger.Debugf("Connection established to %s", remote_ip)
Handle_Connection(conn, remote_ip, false) // handle connection
time.Sleep(4 * time.Second)
}
}
// shutdown the p2p component
func P2P_Shutdown() {
Exit_In_Progress = true
close(Exit_Event) // send signal to all connections to exit
// TODO we must wait for connections to kill themselves
time.Sleep(1 * time.Second)
logger.Infof("P2P Shutdown")
atomic.AddUint32(&globals.Subsystem_Active, ^uint32(0)) // this decrement 1 fom subsystem
}
func Connection_ShutDown(connection *Connection) {
connection.Conn.Close()
}

+ 27
- 0
p2p/cryptonote_core_blockchain_command_handler.go

@ -0,0 +1,27 @@
package p2p
// these are defined in cryptonote_protocol_defs.h
const BC_COMMANDS_POOL_BASE = 2000
const BC_NOTIFY_NEW_BLOCK = BC_COMMANDS_POOL_BASE + 1 // arrival of new block
const BC_NOTIFY_NEW_TRANSACTIONS = BC_COMMANDS_POOL_BASE + 2 // arrival of new transactions
const BC_NOTIFY_REQUEST_GET_OBJECTS = BC_COMMANDS_POOL_BASE + 3 // get objects
const BC_NOTIFY_RESPONSE_GET_OBJECTS = BC_COMMANDS_POOL_BASE + 4 // carries payload
// where is the 5th one ??
const BC_NOTIFY_REQUEST_CHAIN = BC_COMMANDS_POOL_BASE + 6 // provides chain state
const BC_NOTIFY_RESPONSE_CHAIN_ENTRY = BC_COMMANDS_POOL_BASE + 7 // request and reponse, for block height
// used to print names for command
var BC_COMMAND_NAME = map[uint32]string{
// below are p2p commands
P2P_COMMAND_HANDSHAKE: "P2P_COMMAND_HANDSHAKE",
P2P_COMMAND_TIMED_SYNC: "P2P_COMMAND_TIMED_SYNC",
P2P_COMMAND_PING: "P2P_COMMAND_PING",
P2P_COMMAND_REQUEST_SUPPORT_FLAGS: "P2P_COMMAND_REQUEST_SUPPORT_FLAGS",
// below are cryptonote based commands
}

+ 7
- 0
p2p/external.go

@ -0,0 +1,7 @@
/* this file contains the external data inputs as as block height */
package p2p
var OUR_PEER_ID uint64
//var our_IP
var our_Port uint32

+ 383
- 0
p2p/levin.go

@ -0,0 +1,383 @@
package p2p
import "io"
import "net"
import "fmt"
import "time"
import "testing"
import "container/list"
import "encoding/binary"
import "github.com/romana/rlog"
import log "github.com/sirupsen/logrus"
import "github.com/deroproject/derosuite/globals"
// all communications flow in little endian
const LEVIN_SIGNATURE = 0x0101010101012101 //Bender's nightmare
const LEVIN_SIGNATURE_DATA = 0x0102010101011101
const LEVIN_PROTOCOL_VER_0 = 0
const LEVIN_PROTOCOL_VER_1 = 1
const LEVIN_PACKET_REQUEST = 0x00000001
const LEVIN_PACKET_RESPONSE = 0x00000002
// the whole structure should be packed into 33 bytes
type Levin_Header struct {
Signature uint64
CB uint64 // this contains data size appended to buffer
ReturnData bool
Command uint32
ReturnCode int32
Flags uint32
Protocol_Version uint32
}
// all response will have the signature in big endian form
type Levin_Data_Header struct {
Signature uint64 // LEVIN_SIGNATURE_DATA
//Boost_Header byte
Data []byte
}
// sets timeout based on connection state, so as stale connections are cleared quickly
func set_timeout(connection *Connection) {
if connection.State == HANDSHAKE_PENDING {
connection.Conn.SetReadDeadline(time.Now().Add(20 * time.Second)) // new connections have 20 seconds to handshake
} else {
connection.Conn.SetReadDeadline(time.Now().Add(300 * time.Second)) // good connections have 5 mins to communicate
}
}
/* this is the entire connection handler, all incoming/outgoing connections end up here */
func Handle_Connection(conn net.Conn, remote_addr *net.TCPAddr, incoming bool) {
var connection Connection
var levin_header Levin_Header
connection.Incoming = incoming
connection.Conn = conn
var idle int
connection.Addr = remote_addr // since we may be connecting via socks, get target IP
connection.Command_queue = list.New() // init command queue
connection.State = HANDSHAKE_PENDING
if incoming {
connection.logger = logger.WithFields(log.Fields{"RIP": remote_addr.String(), "DIR": "INC"})
} else {
connection.logger = logger.WithFields(log.Fields{"RIP": remote_addr.String(), "DIR": "OUT"})
}
defer func() {
if r := recover(); r != nil {
connection.logger.Fatalf("Recovered while handling connection", r)
}
}()
Connection_Add(&connection) // add connection to pool
if !incoming {
Send_Handshake(&connection) // send handshake
}
// goroutine to exit the connection if signalled
go func() {
ticker := time.NewTicker(1 * time.Second) // 1 second ticker
for {
select {
case <-ticker.C:
idle++
// if idle more than 13 secs, we should send a timed sync
if idle > 13 {
if connection.State != HANDSHAKE_PENDING {
connection.State = IDLE
}
Send_Timed_Sync(&connection)
//connection.logger.Debugf("We should send a timed sync")
idle = 0
}
case <-Exit_Event: // p2p is shutting down, close the connection
connection.Exit = true
ticker.Stop() // release resources of timer
Connection_Delete(&connection)
conn.Close()
return // close the connection and close the routine
}
if connection.Exit { // release resources of timer
ticker.Stop()
Connection_Delete(&connection)
return
}
}
}()
for {
if connection.Exit {
connection.logger.Debugf("Connection exited")
conn.Close()
return
}
// wait and read header
header_data := make([]byte, 33, 33) // size of levin header
idle = 0
rlog.Tracef(10, "waiting to read header bytes from network %s\n", globals.CTXString(connection.logger))
set_timeout(&connection)
read_bytes, err := io.ReadFull(conn, header_data)
if err != nil {
rlog.Tracef(4, "Error while reading levin header exiting err:%s\n", err)
connection.Exit = true
continue
}
rlog.Tracef(10, "Read %d bytes from network\n", read_bytes)
if connection.State != HANDSHAKE_PENDING {
connection.State = ACTIVE
}
err = levin_header.DeSerialize(header_data)
if err != nil {
rlog.Tracef(4, "Error while DeSerializing levin header exiting err:%s\n", err)
connection.Exit = true
continue
}
// read data as per requirement
data := make([]byte, levin_header.CB, levin_header.CB)
set_timeout(&connection)
read_bytes, err = io.ReadFull(conn, data)
rlog.Tracef(10, "Read %d bytes from network for data \n", read_bytes)
if err != nil {
rlog.Tracef(4, "Error while reading levin data exiting err:%s\n", err)
connection.Exit = true
continue
}
name := COMMAND_NAME[levin_header.Command]
if name == "" {
connection.logger.Warnf("No Such command %d exiting\n", levin_header.Command)
connection.Exit = true
continue
}
//connection.logger.WithFields(log.Fields{
// "command": name,
// "flags": levin_header.Flags}).Debugf("Incoming Command")
if levin_header.Flags == LEVIN_PACKET_RESPONSE {
if connection.Command_queue.Len() < 1 {
connection.logger.Warnf("Invalid Response ( we have not queued anything\n")
connection.Exit = true
continue
}
front_command := connection.Command_queue.Front()
if levin_header.Command != front_command.Value.(uint32) {
connection.logger.Warnf("Invalid Response ( we queued some other command\n")
connection.Exit = true
continue
}
connection.Lock()
connection.Command_queue.Remove(front_command)
connection.Unlock()
switch levin_header.Command {
case P2P_COMMAND_HANDSHAKE: // Parse incoming handshake response
Handle_P2P_Handshake_Command_Response(&connection, &levin_header, data)
// if response is OK, mark conncection as good and add it to list
case P2P_COMMAND_TIMED_SYNC: // we never send timed response
// connection.logger.Infof("Response for timed sync arrived")
Handle_P2P_Timed_Sync_Response(&connection, &levin_header, data)
case P2P_COMMAND_PING: // we never send ping packets
case P2P_COMMAND_REQUEST_SUPPORT_FLAGS: // we never send flags packet
}
}
if levin_header.Flags == LEVIN_PACKET_REQUEST {
switch levin_header.Command {
case P2P_COMMAND_HANDSHAKE: // send response
connection.logger.Debugf("Incoming handshake command")
Handle_P2P_Handshake_Command(&connection, &levin_header, data)
case P2P_COMMAND_REQUEST_SUPPORT_FLAGS: // send reponse
Handle_P2P_Support_Flags(&connection, &levin_header, data)
case P2P_COMMAND_TIMED_SYNC:
Handle_P2P_Timed_Sync(&connection, &levin_header, data)
// crypto note core protocols commands related to blockchain
// peer wants to syncronise his chain to ours
case BC_NOTIFY_REQUEST_CHAIN:
Handle_BC_Notify_Chain(&connection, &levin_header, data)
// we want to syncronise our chain to peers
case BC_NOTIFY_RESPONSE_CHAIN_ENTRY:
Handle_BC_Notify_Response_Chain_Entry(&connection, &levin_header, data)
case BC_NOTIFY_REQUEST_GET_OBJECTS: // peer requested some object
Handle_BC_Notify_Request_GetObjects(&connection, &levin_header, data)
case BC_NOTIFY_RESPONSE_GET_OBJECTS: // peer responded to our object requests
Handle_BC_Notify_Response_GetObjects(&connection, &levin_header, data)
case BC_NOTIFY_NEW_TRANSACTIONS:
Handle_BC_Notify_New_Transactions(&connection, &levin_header, data)
case BC_NOTIFY_NEW_BLOCK:
Handle_BC_Notify_New_Block(&connection, &levin_header, data)
}
}
}
}
/* this operation can never fail */
func SerializeLevinHeader(header Levin_Header) []byte {
packed_buffer := make([]byte, 33, 33)
binary.LittleEndian.PutUint64(packed_buffer[0:8], LEVIN_SIGNATURE) // packed 8 bytes
binary.LittleEndian.PutUint64(packed_buffer[8:16], header.CB) // packed 8 + 8 bytes
if header.ReturnData {
packed_buffer[16] = 1 // packed 8 + 8 + 1
}
binary.LittleEndian.PutUint32(packed_buffer[17:17+4], header.Command) // packed 8+8+1+4 bytes
binary.LittleEndian.PutUint32(packed_buffer[21:21+4], uint32(header.ReturnCode)) // packed 8+8+1+4 bytes
binary.LittleEndian.PutUint32(packed_buffer[25:25+4], header.Flags) // packed 8+8+1+4 bytes
binary.LittleEndian.PutUint32(packed_buffer[29:29+4], LEVIN_PROTOCOL_VER_1) // packed 8+8+1+4 bytes
return packed_buffer
}
func (header Levin_Header) Serialize() ([]byte, int) {
packed_buffer := make([]byte, 33, 33)
binary.LittleEndian.PutUint64(packed_buffer[0:8], LEVIN_SIGNATURE) // packed 8 bytes
binary.LittleEndian.PutUint64(packed_buffer[8:16], header.CB) // packed 8 + 8 bytes
if header.ReturnData {
packed_buffer[16] = 1 // packed 8 + 8 + 1
}
binary.LittleEndian.PutUint32(packed_buffer[17:17+4], header.Command) // packed 8+8+1+4 bytes
binary.LittleEndian.PutUint32(packed_buffer[21:21+4], uint32(header.ReturnCode)) // packed 8+8+1+4 bytes
binary.LittleEndian.PutUint32(packed_buffer[25:25+4], header.Flags) // packed 8+8+1+4 bytes
binary.LittleEndian.PutUint32(packed_buffer[29:29+4], LEVIN_PROTOCOL_VER_1) // packed 8+8+1+4 bytes
return packed_buffer, len(packed_buffer)
}
// extract structure info from hardcoded node
func (header *Levin_Header) DeSerialize(packed_buffer []byte) (err error) {
if len(packed_buffer) != 33 {
return fmt.Errorf("Insufficient header bytes")
}
header.Signature = binary.LittleEndian.Uint64(packed_buffer[0:8]) // packed 8 bytes
if header.Signature != LEVIN_SIGNATURE {
return fmt.Errorf("Incorrect Levin Signature")
}
header.CB = binary.LittleEndian.Uint64(packed_buffer[8:16]) // packed 8 + 8 bytes
if packed_buffer[16] == 0 {
header.ReturnData = false // packed 8 + 8 + 1
} else {
header.ReturnData = true // packed 8 + 8 + 1
}
header.Command = binary.LittleEndian.Uint32(packed_buffer[17 : 17+4]) // packed 8+8+1+4 bytes
header.ReturnCode = (int32)(binary.LittleEndian.Uint32(packed_buffer[21 : 21+4])) // packed 8+8+1+4 bytes
header.Flags = binary.LittleEndian.Uint32(packed_buffer[25 : 25+4]) // packed 8+8+1+4 bytes
header.Protocol_Version = binary.LittleEndian.Uint32(packed_buffer[29 : 29+4]) // packed 8+8+1+4 bytes
return nil
}
func (header Levin_Data_Header) Serialize() ([]byte, int) {
var packed_buffer []byte
// if nothing is to be placed
if len(header.Data) == 0 {
packed_buffer = make([]byte, 8+2, 8+2) // 10 bytes minimum heade
binary.LittleEndian.PutUint64(packed_buffer[0:8], LEVIN_SIGNATURE_DATA) // packed 8 bytes
packed_buffer[8] = 1
packed_buffer[9] = 0
return packed_buffer, len(packed_buffer)
}
packed_buffer = make([]byte, 8+2+len(header.Data), 8+2+len(header.Data))
binary.LittleEndian.PutUint64(packed_buffer[0:8], LEVIN_SIGNATURE_DATA) // packed 8 bytes
packed_buffer[8] = 1
packed_buffer[9] = 8
copy(packed_buffer[10:], header.Data)
return packed_buffer, len(packed_buffer)
}
// extract structure info from hardcoded node
func (header *Levin_Data_Header) DeSerialize(packed_buffer []byte) (err error) {
if len(packed_buffer) < 10 {
return fmt.Errorf("Insufficient header bytes")
}
header.Signature = binary.LittleEndian.Uint64(packed_buffer[0:8]) // packed 8 bytes
if header.Signature != LEVIN_SIGNATURE_DATA {
return fmt.Errorf("WRONG LEVIN_SIGNATURE_DATA")
}
if len(packed_buffer)-8 == 2 {
return nil
}
header.Data = make([]byte, len(packed_buffer)-8+2, len(packed_buffer)-8+2)
// ignore 2 bytes
// packed_buffer[8]=1 // version
// packed_buffer[9]=8 // boost 8 , this can be anything as per boost level
copy(header.Data, packed_buffer[10:])
return nil
}
func DeSerializeLevinHeader(packed_buffer []byte, header *Levin_Header) error {
if len(packed_buffer) != 33 {
return fmt.Errorf("Insufficient header bytes")
}
header.Signature = binary.LittleEndian.Uint64(packed_buffer[0:8]) // packed 8 bytes
header.CB = binary.LittleEndian.Uint64(packed_buffer[8:16]) // packed 8 + 8 bytes
if packed_buffer[16] == 0 {
header.ReturnData = false // packed 8 + 8 + 1
} else {
header.ReturnData = true // packed 8 + 8 + 1
}
header.Command = binary.LittleEndian.Uint32(packed_buffer[17 : 17+4]) // packed 8+8+1+4 bytes
header.ReturnCode = (int32)(binary.LittleEndian.Uint32(packed_buffer[21 : 21+4])) // packed 8+8+1+4 bytes
header.Flags = binary.LittleEndian.Uint32(packed_buffer[25 : 25+4]) // packed 8+8+1+4 bytes
header.Protocol_Version = binary.LittleEndian.Uint32(packed_buffer[29 : 29+4]) // packed 8+8+1+4 bytes
return nil
}
func TestSerializeDeserialize(t *testing.T) {
}

+ 56
- 0
p2p/levin_test.go

@ -0,0 +1,56 @@
package p2p
import "testing"
import "encoding/hex"
func Test_Levin_Header_Deserialisation(t *testing.T) {
// this structure has been manually pulled from wireshark stream
raw_data := "0121010101010101e20000000000000001e9030000000000000100000001000000"
raw_data_blob, _ := hex.DecodeString(raw_data)
_ = raw_data_blob
var lheader Levin_Header
err := lheader.DeSerialize(raw_data_blob)
if err != nil {
t.Error("DeSerialize Levin header Failed\n")
return
}
// now serialize once again
serialised, _ := lheader.Serialize()
if raw_data != hex.EncodeToString(serialised) {
t.Error("Serialize Levin_Header Failed")
}
}
func Test_Levin_Data_Header(t *testing.T) {
// this structure has been manually pulled from wireshark stream
raw_data := "01110101010102010100"
raw_data_blob, _ := hex.DecodeString(raw_data)
_ = raw_data_blob
var lheader Levin_Data_Header
err := lheader.DeSerialize(raw_data_blob)
if err != nil {
t.Error("DeSerialize Levin Data header Failed\n")
return
}
// now serialize once again
serialised, _ := lheader.Serialize()
if raw_data != hex.EncodeToString(serialised) {
t.Errorf("Serialize Levin_Data_Header Failed \n%s correct value \n%s Our value", raw_data, hex.EncodeToString(serialised))
}
}

+ 27
- 0
p2p/notes.txt

@ -0,0 +1,27 @@
// cryptonote protocol based commands are described here
https://github.com/monero-project/monero/blob/ed8fc8ac1c694344e30d07f1cb1a5cb904b13f52/src/cryptonote_protocol/cryptonote_protocol_defs.h
#define BC_COMMANDS_POOL_BASE 2000
https://github.com/monero-project/monero/blob/master/src/p2p/p2p_protocol_defs.h
#define P2P_COMMANDS_POOL_BASE 1000
0x3e9 1001
0x3ef 1007
The protocol state machine works as follows
if you are client
connect,
send support flags
send handshake
receive handshake, if height or top is different send, send notify request chain
// fall into processing loop

+ 559
- 0
p2p/p2p_command_handshake.go

@ -0,0 +1,559 @@
package p2p
import "net"
import "fmt"
import "bytes"
import "encoding/hex"
import "encoding/binary"
import "github.com/satori/go.uuid"
//import "github.com/romana/rlog"
//import log "github.com/sirupsen/logrus"
const P2P_COMMANDS_POOL_BASE = 1000
const P2P_COMMAND_HANDSHAKE = P2P_COMMANDS_POOL_BASE + 1 // carries payload
const P2P_COMMAND_TIMED_SYNC = P2P_COMMANDS_POOL_BASE + 2 // carries payload
const P2P_COMMAND_PING = P2P_COMMANDS_POOL_BASE + 3
// debug commands are not even implemented ( therefore not supported/developed at all)
const P2P_COMMAND_REQUEST_SUPPORT_FLAGS = P2P_COMMANDS_POOL_BASE + 7
// used to print names for command
var COMMAND_NAME = map[uint32]string{
// below are p2p commands
P2P_COMMAND_HANDSHAKE: "P2P_COMMAND_HANDSHAKE",
P2P_COMMAND_TIMED_SYNC: "P2P_COMMAND_TIMED_SYNC",
P2P_COMMAND_PING: "P2P_COMMAND_PING",
P2P_COMMAND_REQUEST_SUPPORT_FLAGS: "P2P_COMMAND_REQUEST_SUPPORT_FLAGS",
// below are cryptonote based commands
BC_NOTIFY_NEW_BLOCK: "BC_NOTIFY_NEW_BLOCK",
BC_NOTIFY_NEW_TRANSACTIONS: "BC_NOTIFY_NEW_TRANSACTIONS",
BC_NOTIFY_REQUEST_GET_OBJECTS: "BC_NOTIFY_REQUEST_GET_OBJECTS",
BC_NOTIFY_RESPONSE_GET_OBJECTS: "BC_NOTIFY_RESPONSE_GET_OBJECTS",
// where is the 5th one ??
BC_NOTIFY_REQUEST_CHAIN: "BC_NOTIFY_REQUEST_CHAIN", // when this comes, reply with reponse chain entry
BC_NOTIFY_RESPONSE_CHAIN_ENTRY: "BC_NOTIFY_RESPONSE_CHAIN_ENTRY",
}
type Network_Interface interface {
Serialize() ([]byte, int)
Deserialize([]byte) (int, error)
}
type Peer_Info struct {
IP net.IP
Port uint32
ID uint64
LastSeen uint64
}
// from here https://github.com/monero-project/monero/blob/master/src/p2p/p2p_protocol_defs.h
type Node_Data struct {
Network_UUID uuid.UUID
Local_time uint64
Local_Port uint32
Peer_ID uint64
}
type Node_Data_Response struct {
PeerArray []Peer_Info
NodeData Node_Data
CoreData CORE_DATA
}
//
// hardcoded buffer for boost compatibility
/* pipe marker placed for initial and ending points
0000 01 21 01 01 01 01 01 01 e2 00 00 00 00 00 00 00 .!..............
0010 00 e9 03 00 00 01 00 00 00 02 00 00 00 01 00 00 ................
0020 00 01 11 01 01 01 01 02 01|01 08 09 6e 6f 64 65 ............node
0030 5f 64 61 74 61 0c 10 0a 6c 6f 63 61 6c 5f 74 69 _data...local_ti
0040 6d 65 05 d5 6b 2a 5a 00 00 00 00 07 6d 79 5f 70 me..k*Z.....my_p
0050 6f 72 74 06 00 00 00 00 0a 6e 65 74 77 6f 72 6b ort......network
0060 5f 69 64 0a 40 12 30 f1 71 61 04 41 61 17 31 00 _id.@.0.qa.Aa.1.
0070 82 16 a1 a1 11 07 70 65 65 72 5f 69 64 05 ce d5 ......peer_id...
0080 2f 43 e6 e7 a0 4e|0c 70 61 79 6c 6f 61 64 5f 64 /C...N.payload_d
0090 61 74 61 0c 10 15 63 75 6d 75 6c 61 74 69 76 65 ata...cumulative
00a0 5f 64 69 66 66 69 63 75 6c 74 79 05 01 00 00 00 _difficulty.....
00b0 00 00 00 00 0e 63 75 72 72 65 6e 74 5f 68 65 69 .....current_hei
00c0 67 68 74 05 01 00 00 00 00 00 00 00 06 74 6f 70 ght..........top
00d0 5f 69 64 0a 80 48 ca 7c d3 c8 de 5b 6a 4d 53 d2 _id..H.|...[jMS.
00e0 86 1f bd ae dc a1 41 55 35 59 f9 be 95 20 06 80 ......AU5Y... ..
00f0 53 cd a8 43 0b 0b 74 6f 70 5f 76 65 72 73 69 6f S..C..top_versio
0100 6e 08 01 n..
the serialised structure is 0x4c bytes
*/
func (data Node_Data) Serialize() ([]byte, int) {
var buf []byte = []byte{
//0x01,0x08, // boost header
0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, // lenth + node data
0x0c, 0x10, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, // length + local time
0x05, // boost header for uint64
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // localtime should be filled here
0x07, 0x6d, 0x79, 0x5f, 0x70, 0x6f, 0x72, 0x74, // length + my_port
0x06, // boost header for uint32
0x0, 0x0, 0x0, 0x0, // myport should be filled here
0x0a, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x69, 0x64, // length +network id
0x0a, 0x40, // boost for 16 byte uint 8 array
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // network_id should be filled here
0x07, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, // length + peer id
0x05, // boost header for uint64
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // peerid should be filled here
}
binary.LittleEndian.PutUint64(buf[24:], data.Local_time)
binary.LittleEndian.PutUint32(buf[41:], data.Local_Port)
network_id, _ := data.Network_UUID.MarshalBinary()
copy(buf[58:], network_id[:])
binary.LittleEndian.PutUint64(buf[83:], data.Peer_ID)
return buf, 91
}
// extract structure info from hardcoded node
func (data *Node_Data) DeSerialize(buf []byte) (err error) {
if len(buf) < 91 {
return fmt.Errorf("Insufficient Buffer")
}
data.Local_time = binary.LittleEndian.Uint64(buf[24:])
data.Local_Port = binary.LittleEndian.Uint32(buf[41:])
_ = data.Network_UUID.UnmarshalBinary(buf[58 : 58+16])
data.Peer_ID = binary.LittleEndian.Uint64(buf[83:])
return nil
}
type CORE_DATA struct {
Current_Height uint64
Cumulative_Difficulty uint64
Top_ID [32]byte // top block hash
Top_Version byte // top version
}
// hardcoded buffer for boost compatibility
/* pipe marker placed for initial and ending points
0000 01 21 01 01 01 01 01 01 e2 00 00 00 00 00 00 00 .!..............
0010 00 e9 03 00 00 01 00 00 00 02 00 00 00 01 00 00 ................
0020 00 01 11 01 01 01 01 02 01 01 08 09 6e 6f 64 65 ............node
0030 5f 64 61 74 61 0c 10 0a 6c 6f 63 61 6c 5f 74 69 _data...local_ti
0040 6d 65 05 d5 6b 2a 5a 00 00 00 00 07 6d 79 5f 70 me..k*Z.....my_p
0050 6f 72 74 06 00 00 00 00 0a 6e 65 74 77 6f 72 6b ort......network
0060 5f 69 64 0a 40 12 30 f1 71 61 04 41 61 17 31 00 _id.@.0.qa.Aa.1.
0070 82 16 a1 a1 11 07 70 65 65 72 5f 69 64 05 ce d5 ......peer_id...
0080 2f 43 e6 e7 a0 4e|0c 70 61 79 6c 6f 61 64 5f 64 /C...N.payload_d
0090 61 74 61 0c 10 15 63 75 6d 75 6c 61 74 69 76 65 ata...cumulative
00a0 5f 64 69 66 66 69 63 75 6c 74 79 05 01 00 00 00 _difficulty.....
00b0 00 00 00 00 0e 63 75 72 72 65 6e 74 5f 68 65 69 .....current_hei
00c0 67 68 74 05 01 00 00 00 00 00 00 00 06 74 6f 70 ght..........top
00d0 5f 69 64 0a 80 48 ca 7c d3 c8 de 5b 6a 4d 53 d2 _id..H.|...[jMS.
00e0 86 1f bd ae dc a1 41 55 35 59 f9 be 95 20 06 80 ......AU5Y... ..
00f0 53 cd a8 43 0b 0b 74 6f 70 5f 76 65 72 73 69 6f S..C..top_versio
0100 6e 08 01 | n..
*/
func (data CORE_DATA) Serialize() ([]byte, int) {
var buf []byte = []byte{
0x0c, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, // lenth + payload_data
0x0c, 0x10,
0x15, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, // length + cumulative difficulty
0x05, // boost header for uint64
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // cumulative difficulty should be filled here
0x0e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, // length + my_port
0x05, // boost header for uint64
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // current height should be filled here
0x06, 0x74, 0x6f, 0x70, 0x5f, 0x69, 0x64, // length + top_id block hash
0x0a, 0x80, // boost for 32 byte uint 8 array
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // first 16 bytes
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // second 16 bytes
0x0b, 0x74, 0x6f, 0x70, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, // length + top_version
0x08, // boost header for uint8
0x0, // top_version should be filled here
}
binary.LittleEndian.PutUint64(buf[38:], data.Cumulative_Difficulty)
binary.LittleEndian.PutUint64(buf[62:], data.Current_Height)
copy(buf[79:], data.Top_ID[:])
buf[124] = data.Top_Version
return buf, len(buf)
}
// extract structure info from hardcoded node
func (data *CORE_DATA) DeSerialize(buf []byte) (err error) {
if len(buf) < 125 {
return fmt.Errorf("Insufficient Buffer")
}
data.Cumulative_Difficulty = binary.LittleEndian.Uint64(buf[38:])
data.Current_Height = binary.LittleEndian.Uint64(buf[62:])
copy(data.Top_ID[0:32], buf[79:79+32])
data.Top_Version = buf[124]
return nil
}
func (data *Node_Data_Response) Serialize() ([]byte, int) {
pkt1 := "0e6c6f63616c5f706565726c6973740ac15dd1cdd38aa04600001e145a24a1521225362a2d5a000000005fd71310a0460000fa49324a8c0c40a0312a2d5a00000000c6ccf7aaa046000043eace428b25b1d3312a2d5a0000000032079a14a04600008740365aeee32a5a262a2d5a000000004db2817ca046000064786ac517d7504cfd292d5a00000000b93b65fba04600008293f1c40ffdfb3ecd292d5a0000000051414e6da04600000abaf4050f9ed985bc292d5a0000000072620312a0460000656c3b6dd65dbb459f292d5a00000000a3ac1411a2460000a0acc8ba334262699a292d5a00000000c06ea092a0460000cf1acf3a47f41ddf96292d5a0000000049b40d05a0460000a62d61498f1d98b948292d5a0000000052278f64a0460000deb319bafed959dc41292d5a00000000569b0905a046000028bcfbe770b0a1d614292d5a00000000d942110ba04600005a40b1725aeae591d0282d5a000000002d7dc222a0460000bb6ce64086eaa3f9be282d5a000000005d5b7609a046000098584e4e026fdd1889282d5a0000000058b74b0fa04600004dbeb701f2b0b7a986282d5a000000005ec74a45a046000049aa3dbc5aa3fe9983282d5a00000000aa4808afa04600007949cb645d40e4b5c3272d5a0000000042d7e049a04600003891254f5195f2bdab272d5a0000000044b8da2aa04600009fe839e92e0f23cd70272d5a00000000d445233ba0460000fbf8266d9033c0970a272d5a0000000064105973a0460000019ecb192bdaabdfb2262d5a000000005c348c6ca0460000c755269aab6a8fcc93262d5a000000008ac93cc6f41f000044543d568956ff4e7e262d5a00000000be105e84a04600001e6ba47844b0711751262d5a00000000c06ea072a046000036212e76b101e03a21262d5a000000007659ad2aa0460000d2e66223ba274334ee252d5a00000000c6931ca2a0460000711be06c699c3e345e252d5a00000000c6ff26f2a0460000fa5bcee48d28311048252d5a0000000050f1d8d5a04600006ab44bea6af086a024252d5a00000000054f5481a046000085aaf3d3ceb9195e1e252d5a000000006d78e636a0460000f8cd24472dca88e915252d5a00000000a2fd4145a04600006a6180174ebc1ab3fa242d5a00000000b01f7552b06d0000f7eac19a4d3128ddf8242d5a0000000005ba491da04600008ccec43406a7e4eaf2242d5a000000004a402b86a046000027d07f355b65b2f1f0242d5a0000000047cf3fc0a046000036023345e4759030b9232d5a0000000068ed9fd7a04600008b773b5448d437fcaa232d5a00000000bad56a01a04600003874d4d754dea82134"
pkt2 := "232d5a0000000079df096fa0460000838ed6473815e3952a232d5a00000000056b331ea0460000bb60159a185d57ef18232d5a00000000ba5d3e08a0460000d8cda4f8b8a6fccc0e232d5a00000000d4a4af80a0460000dbf29eb6976bee17ff222d5a00000000bca70c31a0460000d1ed41a93c82a5e9cd222d5a0000000079ad62dca0460000a43e986cea7e962a9a222d5a000000003a6eaceda0460000869f779d519a29ea69222d5a00000000b996bdb5a04600003cbb78ca57fe825e47222d5a0000000069e3627aa0460000605d15da34571d6442222d5a00000000465f4dcaa04600009ae6231712883c4642222d5a000000004055e3e7a0460000d7ead28dfb7cf1da34222d5a00000000bcc25752a04600006f78b8dbd4e04adc2d222d5a00000000bd0d4cbea0460000da021f6b3fe046c11d222d5a00000000af9f43d6a04600002d7bf3d9389ffb9917222d5a00000000188a4a8ba0460000eeb803ba1ef91d79fd212d5a000000003ed2681fa1460000353beec5dd1e2a9e7c212d5a0000000055f48d1ca0460000a3400009e9844c0b49212d5a0000000049bd66efa0460000f18d66ed9022ad0948212d5a0000000051ca9bb2a0460000809988312336b22f39212d5a00000000d5f0b63ca04600001e6afbd4fd51e07534212d5a0000000057d8715ea04600009cddc760e42ffbf5f6202d5a0000000036e50655a0460000f5227c57d494ac9ae9202d5a00000000567e0c4da0460000587db2d322e353b5d2202d5a00000000442e0e3ba04600005ab660e3de256563cb202d5a000000004e54fb50a046000075523816cd9cf2f189202d5a00000000516a4f90a0460000d61f18f76820c9f181202d5a000000006c2d24cca0460000d9c81eac54baf7f67f202d5a00000000560e8f79a0460000ec547410ae3fd4035a202d5a00000000340f51cea0460000dc34bf69c38ae5d652202d5a00000000aa4e6146a0460000db1fa70f77e73dd62d202d5a00000000b0d7fa3fa04600004bb07dfcf4e6890ccc1f2d5a0000000044ca6ee3a0460000d5865c1dd372a1baa21f2d5a000000000e205b86a04600008dd26b584ab55719a11f2d5a0000000095ca324aa04600009a3a9ef27b77f282981f2d5a000000005b798ca7a0460000b3c9d8ae8975cffa841f2d5a000000005e2a8c4da046000078b5cedbd290b8425a1f2d5a00000000442dc4f4a04600006b4679bfcef716b2571f2d5a0000000052291660a04600005e9457952bce50cdad1e2d5a0000000018710dc7a0460000778306285e7ab66ba81e2d5a00000000daea3f6ea0460000a0c6e7d127af91778e1e2d5a00000000524541caa0460000c14d04995da839c77a1e2d5a00000000d8f689a6a0460000fdd4138e79e8982a771e2d5a000000005602b855a046000091"
pkt3 := "3cdaeab53c63716d1e2d5a00000000506b49a1a0460000b1ddf05dc6be790b1d1e2d5a00000000a3ac123da2460000cc6bfbb7fa2bbe42e81d2d5a000000004bac6c1fa04600002eb6e73d4d67f81ce21d2d5a000000008ac98860a0460000116f2c033e504f15d41d2d5a000000002e0b4119a0460000626b6c58922342d0c31d2d5a0000000005bdc09fa0460000aba76ed16edcbc60b71d2d5a000000007ca871aba0460000119a899dda114998991d2d5a000000002ead2989a0460000d9acc674e377271d931d2d5a00000000c2be0b27a04600003a05693c409ba2bd5e1d2d5a0000000043bc4386a046000054c5cf9bcd0ec1083c1d2d5a00000000c64a35aba046000083bab533d150fadb101d2d5a000000002df70d06a0460000b4c9fcb561bce1d4d51c2d5a00000000a8eb679ea046000064ed073fd245007d401c2d5a0000000043958154a04600002997c4e037048ac6391c2d5a00000000b025a95ba0460000797a2cb5020dc535f71b2d5a000000008ac5b670a0460000e2e230417319c2a2971b2d5a000000005b4f07aea0460000269637bc02ef1abe8c1b2d5a000000008d69330ba0460000395719f6dd36d8125c1b2d5a0000000054f98048a046000094f474d261a11f9a571b2d5a000000003b7e944ba0460000c928eed89fa41de6011b2d5a0000000048c93c9ba04600008db1ac5f9797a0abfc1a2d5a00000000d80abee5a0460000b496f4eb5680dce5da1a2d5a000000006c333deba04600002d5ed7a7ac343731cd1a2d5a000000008ba244b9a046000076f40a10d781b61cbc1a2d5a000000005475582ba0460000159120bee1a7525ca01a2d5a0000000068881108a046000001dbba89e194eb139f1a2d5a0000000043085ee2a04600003b3085cbe30951939d1a2d5a000000009e8ccaaca04600007bcc12ac2fd4ec412f1a2d5a00000000b21b88cda04600002b595b3733f57c5686192d5a000000005bd7e351a0460000b638cec7dfb7be8c78192d5a000000008f597145a0460000d6a407050a82289263192d5a00000000253b61bda04600006bc053b4cdfd0be5ea182d5a000000004a50ed5ea0460000bef140ccc7dcf481df182d5a0000000092c75155a04600005a38b1e55e5318d1cd182d5a00000000af24d788a0460000d8f1b5eeacb97274c4182d5a00000000585b1c7ca046000019fb32baa195f121b7182d5a0000000042113069a0460000ca868ba31fd0ed9f93182d5a0000000053e44365a04600003389aa202a53dbea50182d5a000000004a8bf1dda046000038559bb7d3f52fee43182d5a0000000056a937f8a046000083632110dbb20d6430182d5a00000000b0d4e860a0460000767853706dc141e0c0172d5a000000002a76bd6aa046000072c4a28309b6dc0eaa172d5a0000000005"
pkt4 := "c781eaa046000079634df91a79eed1a5172d5a000000004d460151a04600001e6c75c66f5429f136172d5a00000000524b5de1a0460000dc22c083d1053d570d172d5a000000001f23ad26a0460000cc24fe35f2a84c23d0162d5a00000000a772d28da046000053be187ce01848ffbe162d5a00000000a3ac0d54a0460000dbc278d5c71a3fb8b8162d5a0000000018bac39aa04600005513dad620e59e5584162d5a000000007bf3138ca04600002feb5e81676ecf5982162d5a00000000deea0a0ca04600008abb018ee61980b16d162d5a00000000d47866d5a0460000038f82ecf8103e7864162d5a000000005a41157ba04600002b223aed5fc784b761162d5a000000002585c11aa0460000caf332866c4ab43a4f162d5a00000000bad653f6a0460000f2dc8c348848333ff8152d5a00000000546c6ceaa046000062263b041206d216aa152d5a000000004e2e5b86b06d000074e3e5edb5b87c0198152d5a000000005c3f2db9a0460000580b582fb04fbaf893152d5a000000005161fe40a0460000140ce0bb7d6753c390152d5a00000000d809b134a046000022c99d60c5609e7e90152d5a00000000819e4279a04600004caee8075f93e98331142d5a00000000565b2e3da0460000bcbeaaa342e50bdcee132d5a0000000044eb39faa04600009731416c8258768aec132d5a000000006c0419b1a046000042681128e219389ac0132d5a000000004ea94775a04600008a68504d7542f985b5132d5a00000000558fb88da04600005feacafbb881ca729d132d5a00000000a3ac223aa04600001b5aad98b945b0a634132d5a0000000050f1d3bca04600007f00b39fbac2a2d227132d5a00000000a3acff39a0460000b1d3267909bae7e570122d5a00000000be62021ba0460000cc5acc2be5f2b10f47122d5a0000000025b6986ca0460000a4b15b169158a015c6112d5a00000000b028c01ba04600008efbb33df658f4afba112d5a00000000591b0587a0460000afc949f19ae28c6395112d5a000000001806b371a04600004660018f27ac16cb7f112d5a0000000002566daaa0460000e253da8371fba38c71112d5a0000000079dd4525a0460000bc86deeaa78d8d5164112d5a000000002bfc6f6aa0460000075c9f398eba224e3c112d5a0000000054b912ada0460000e39d3c5230cb953d38112d5a0000000052fd6f1ba0460000596cb6bf116f38e3a2102d5a00000000488c5602a0460000fc7c10885b0de0d58d102d5a00000000a3acff34a0460000b0c19a820f57e3518d102d5a00000000d4b69b2da046000010b4ef63f0456a8783102d5a0000000044292257a04600004800b3f181b891a31c102d5a00000000b16204afa0460000de954b6acb9fe67a0d102d5a00000000cbdb2566a046000041c285cd8d83ad03e7"
pkt5 := "0f2d5a0000000048cc2527a0460000aaf77cb662a45c44760f2d5a000000003ed2bd6a6846000081e6000b164f1b8c220f2d5a0000000048da8b39a04600008ffd383095c6a5000f0f2d5a00000000c39afabca0460000badb0eddf1401bccd10e2d5a000000009e45f285a0460000a4d5b6cb15d775bcaf0e2d5a00000000b3db6915a0460000c03e41b41c52218ca00e2d5a0000000025eb46cfa0460000fcda3bedbac144b8810e2d5a0000000064203122a0460000d2f4fd49e43aee8a630e2d5a00000000590e6880a04600002d06b312ae4da3aa470e2d5a0000000076d1fca8a0460000e350dc70e91faaae3a0e2d5a00000000567ea70fa0460000593ad0d815bf46a3390e2d5a00000000c5f53004a0460000706c5da464ac04ff700d2d5a00000000c654df93a04600009233e6310c64bc745c0d2d5a0000000051eb2b34a0460000dbd7e2452e452a970b0d2d5a00000000cbdd045ea04600008ed3d5f429f1f50c090d2d5a0000000055cc6110a0460000bce41a041bba6ac1c30c2d5a000000004ddd4c42a046000069958c02c54bf2af7d0c2d5a000000004f4be504a0460000d198c12f826920a7790c2d5a00000000d5c50f7ea0460000f6a0b5467f05b205730c2d5a00000000cbdad4bda0460000c25bc4cfafd51667590c2d5a00000000052343f1a046000043687e9093c4a8e5560c2d5a000000005c26ed81a04600001a33225cb494e9f1fc0b2d5a000000002e303e99a04600005633b701fd83b07bf90b2d5a0000000064022677a0460000334fd27483c03c53f40b2d5a000000003ed25ee1a746000069e745cde93841c5e20b2d5a000000007d294aefa0460000647585d27f9d01a1aa0b2d5a000000004246dde0a0460000e8202e86eab81cf7900b2d5a000000003bb2a1a3a046000087f958a58a5edf58600b2d5a00000000cc2c60aea04600001ac6163a952e1e14510b2d5a00000000a3acff36a0460000fd6552d454a535a8440b2d5a000000003ed25ee1a646000067b6521faa80a58f410b2d5a0000000077ed6acfa04600001381c53afe90abaa020b2d5a000000003def0cd8a04600005506e9319df085d1af0a2d5a00000000de895d55a0460000fc57428c8df06894ab0a2d5a0000000018ba77efa046000069986492a2f830bca80a2d5a0000000056bc14d3a046000091943c25ed4c5080740a2d5a00000000af88623ca04600005eea824cee690714720a2d5a00000000d5153025a046000077cabc3d0cb226a83f0a2d5a00000000423944e4a04600008f06566ca4144c223f0a2d5a0000000049e50006a046000024443fb9ab86d621e9092d5a000000004d66d2a0a04600000f157a7fe9e2bd8ee8092d5a000000005ac7f5e4a0460000e89a776bf4daf58dca092d5a000000005aca6a36a0460000f0"
pkt6 := "1e6654c8272f4fbc092d5a00000000adf7e926a0460000c476478ce765c60e1c092d5a00000000d0750beba0460000ef12fda0c8980dcfd7082d5a0000000060087525a0460000b87b36c8f4d6642baa082d5a00000000276d9971a0460000ef2863ae135dd14638082d5a000000004ad3433da0460000287ca4e84380440f14082d5a000000005b796e86a0460000516bd12b232ad6428b072d5a000000005d8f68f2a0460000704ad266f503c5cf7c072d5a00000000a3ac1325a0460000bd02703a979f621c5a072d5a00000000ae8a208eb06d00001e9cf30ad73aad4804072d5a00000000b9a5449aa04600006f664b9b290351a303072d5a0000000073db2303a0460000dc4f0b624ba2ea30db062d5a000000004b8cf4c0a046000025cf9eab20ef6c2a8a062d5a00000000b0c37985a04600003731fb0ab15f74e387062d5a0000000049c0edf8a04600009c06dc3761c9ca407a062d5a00000000c7e7557aa0460000ed5439c2fff10bd502062d5a00000000b0c19c9ca046000052c2ccf08106c064c6052d5a000000005848bc44a0460000f0d739a58554ee9fc6052d5a000000004cba7a11a046000014ad822192813a085e052d5a00000000485e572ca04600004dacb99d2769abee19052d5a0000000065a53810a0460000d1478b2a7562baf50d052d5a00000000bce8e1fea0460000da65626afe89649bc2042d5a00000000675a9b02a0460000ee5a4b2021ddbaa083042d5a0000000058b00c64a04600003aebc6cbbef9f21341042d5a000000002d4f630fa04600008560f49b748e3822d5032d5a000000003e8d2676a0460000e4012c5db1fa5248ca032d5a000000003d0eee5ca0460000474611d52689cc2184032d5a000000003143acdea046000004ccb9a3a7ff4eba32032d5a00000000adffc2d7a04600002f"
pkt7 := "87a7d0bb905dd9cd022d5a00000000ad116f7aa0460000b49cf00e56057462a4022d5a0000000056b27f74a046000001a499da9ce04d2c8c022d5a000000000256e19aa04600004e7bac4253c903d689022d5a00000000b1130bf4a04600004af5ff085e7ea9cb51022d5a00000000ae6975a2a04600001d0338681085399150022d5a00000000568b8f28a046000071b69cd0a237ad2c46022d5a00000000616559dca0460000a2b6cc14cbdfa1ed25022d5a000000006c3074aca0460000aa2de6d754dd4432bd012d5a000000006030fa41a04600002aca1553f8b11c34b0012d5a00000000558c2804a0460000bb5bf92c0843e0ac99012d5a0000000056a6911ba046000081862a87a5adf2db7e012d5a00000000126c6f63616c5f706565726c6973745f6e65778ce9030c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006d1cdd38a066d5f706f727407a04604747970650801026964051e145a24a1521225096c6173745f7365656e01362a2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970065fd71310066d5f706f727407a0460474797065080102696405fa49324a8c0c40a0096c6173745f7365656e01312a2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006c6ccf7aa066d5f706f727407a046047479706508010269640543eace428b25b1d3096c6173745f7365656e01312a2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700632079a14066d5f706f727407a04604747970650801026964058740365aeee32a5a096c6173745f7365656e01262a2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970064db2817c066d5f706f727407a046047479706508010269640564786ac517d7504c096c6173745f7365656e01fd292d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006b93b65fb066d5f706f727407a04604747970650801026964058293f1c40ffdfb3e096c6173745f7365656e01cd292d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700651414e6d066d5f706f727407a04604747970650801026964050abaf4050f9ed985096c6173745f7365656e01bc292d5a000000000c03"
pkt8 := "6164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700672620312066d5f706f727407a0460474797065080102696405656c3b6dd65dbb45096c6173745f7365656e019f292d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006a3ac1411066d5f706f727407a2460474797065080102696405a0acc8ba33426269096c6173745f7365656e019a292d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006c06ea092066d5f706f727407a0460474797065080102696405cf1acf3a47f41ddf096c6173745f7365656e0196292d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700649b40d05066d5f706f727407a0460474797065080102696405a62d61498f1d98b9096c6173745f7365656e0148292d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700652278f64066d5f706f727407a0460474797065080102696405deb319bafed959dc096c6173745f7365656e0141292d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006569b0905066d5f706f727407a046047479706508010269640528bcfbe770b0a1d6096c6173745f7365656e0114292d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006d942110b066d5f706f727407a04604747970650801026964055a40b1725aeae591096c6173745f7365656e01d0282d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970062d7dc222066d5f706f727407a0460474797065080102696405bb6ce64086eaa3f9096c6173745f7365656e01be282d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970065d5b7609066d5f706f727407a046047479706508010269640598584e4e026fdd18096c6173745f7365656e0189282d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700658b74b0f066d5f706f727407a04604747970650801026964054dbeb701f2b0b7a9096c6173745f7365656e0186282d5a"
pkt9 := "000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970065ec74a45066d5f706f727407a046047479706508010269640549aa3dbc5aa3fe99096c6173745f7365656e0183282d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006aa4808af066d5f706f727407a04604747970650801026964057949cb645d40e4b5096c6173745f7365656e01c3272d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700642d7e049066d5f706f727407a04604747970650801026964053891254f5195f2bd096c6173745f7365656e01ab272d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700644b8da2a066d5f706f727407a04604747970650801026964059fe839e92e0f23cd096c6173745f7365656e0170272d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006d445233b066d5f706f727407a0460474797065080102696405fbf8266d9033c097096c6173745f7365656e010a272d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700664105973066d5f706f727407a0460474797065080102696405019ecb192bdaabdf096c6173745f7365656e01b2262d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970065c348c6c066d5f706f727407a0460474797065080102696405c755269aab6a8fcc096c6173745f7365656e0193262d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970068ac93cc6066d5f706f727407f41f047479706508010269640544543d568956ff4e096c6173745f7365656e017e262d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006be105e84066d5f706f727407a04604747970650801026964051e6ba47844b07117096c6173745f7365656e0151262d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006c06ea072066d5f706f727407a046047479706508010269640536212e76b101e03a096c6173745f736565"
pkt10 := "6e0121262d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970067659ad2a066d5f706f727407a0460474797065080102696405d2e66223ba274334096c6173745f7365656e01ee252d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006c6931ca2066d5f706f727407a0460474797065080102696405711be06c699c3e34096c6173745f7365656e015e252d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006c6ff26f2066d5f706f727407a0460474797065080102696405fa5bcee48d283110096c6173745f7365656e0148252d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700650f1d8d5066d5f706f727407a04604747970650801026964056ab44bea6af086a0096c6173745f7365656e0124252d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006054f5481066d5f706f727407a046047479706508010269640585aaf3d3ceb9195e096c6173745f7365656e011e252d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970066d78e636066d5f706f727407a0460474797065080102696405f8cd24472dca88e9096c6173745f7365656e0115252d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006a2fd4145066d5f706f727407a04604747970650801026964056a6180174ebc1ab3096c6173745f7365656e01fa242d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006b01f7552066d5f706f727407b06d0474797065080102696405f7eac19a4d3128dd096c6173745f7365656e01f8242d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700605ba491d066d5f706f727407a04604747970650801026964058ccec43406a7e4ea096c6173745f7365656e01f2242d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970064a402b86066d5f706f727407a046047479706508010269640527d07f355b65b2f1096c61"
pkt11 := "73745f7365656e01f0242d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700647cf3fc0066d5f706f727407a046047479706508010269640536023345e4759030096c6173745f7365656e01b9232d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700668ed9fd7066d5f706f727407a04604747970650801026964058b773b5448d437fc096c6173745f7365656e01aa232d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006bad56a01066d5f706f727407a04604747970650801026964053874d4d754dea821096c6173745f7365656e0134232d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700679df096f066d5f706f727407a0460474797065080102696405838ed6473815e395096c6173745f7365656e012a232d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006056b331e066d5f706f727407a0460474797065080102696405bb60159a185d57ef096c6173745f7365656e0118232d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006ba5d3e08066d5f706f727407a0460474797065080102696405d8cda4f8b8a6fccc096c6173745f7365656e010e232d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006d4a4af80066d5f706f727407a0460474797065080102696405dbf29eb6976bee17096c6173745f7365656e01ff222d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006bca70c31066d5f706f727407a0460474797065080102696405d1ed41a93c82a5e9096c6173745f7365656e01cd222d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700679ad62dc066d5f706f727407a0460474797065080102696405a43e986cea7e962a096c6173745f7365656e019a222d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970063a6eaced066d5f706f727407a0460474797065080102696405869f779d51"
pkt12 := "9a29ea096c6173745f7365656e0169222d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006b996bdb5066d5f706f727407a04604747970650801026964053cbb78ca57fe825e096c6173745f7365656e0147222d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700669e3627a066d5f706f727407a0460474797065080102696405605d15da34571d64096c6173745f7365656e0142222d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006465f4dca066d5f706f727407a04604747970650801026964059ae6231712883c46096c6173745f7365656e0142222d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970064055e3e7066d5f706f727407a0460474797065080102696405d7ead28dfb7cf1da096c6173745f7365656e0134222d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006bcc25752066d5f706f727407a04604747970650801026964056f78b8dbd4e04adc096c6173745f7365656e012d222d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006bd0d4cbe066d5f706f727407a0460474797065080102696405da021f6b3fe046c1096c6173745f7365656e011d222d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006af9f43d6066d5f706f727407a04604747970650801026964052d7bf3d9389ffb99096c6173745f7365656e0117222d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006188a4a8b066d5f706f727407a0460474797065080102696405eeb803ba1ef91d79096c6173745f7365656e01fd212d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970063ed2681f066d5f706f727407a1460474797065080102696405353beec5dd1e2a9e096c6173745f7365656e017c212d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700655f48d1c066d5f706f727407a04604747970650801026964"
pkt13 := "05a3400009e9844c0b096c6173745f7365656e0149212d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700649bd66ef066d5f706f727407a0460474797065080102696405f18d66ed9022ad09096c6173745f7365656e0148212d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700651ca9bb2066d5f706f727407a0460474797065080102696405809988312336b22f096c6173745f7365656e0139212d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006d5f0b63c066d5f706f727407a04604747970650801026964051e6afbd4fd51e075096c6173745f7365656e0134212d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700657d8715e066d5f706f727407a04604747970650801026964059cddc760e42ffbf5096c6173745f7365656e01f6202d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700636e50655066d5f706f727407a0460474797065080102696405f5227c57d494ac9a096c6173745f7365656e01e9202d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006567e0c4d066d5f706f727407a0460474797065080102696405587db2d322e353b5096c6173745f7365656e01d2202d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006442e0e3b066d5f706f727407a04604747970650801026964055ab660e3de256563096c6173745f7365656e01cb202d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970064e54fb50066d5f706f727407a046047479706508010269640575523816cd9cf2f1096c6173745f7365656e0189202d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006516a4f90066d5f706f727407a0460474797065080102696405d61f18f76820c9f1096c6173745f7365656e0181202d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970066c2d24cc066d5f706f727407a04604747970"
pkt14 := "65080102696405d9c81eac54baf7f6096c6173745f7365656e017f202d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f6164647265"
pkt15 := "73733e28290c08046d5f697006560e8f79066d5f706f727407a0460474797065080102696405ec547410ae3fd403096c6173745f7365656e015a202d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006340f51ce066d5f706f727407a0460474797065080102696405dc34bf69c38ae5d6096c6173745f7365656e0152202d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006aa4e6146066d5f706f727407a0460474797065080102696405db1fa70f77e73dd6096c6173745f7365656e012d202d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006b0d7fa3f066d5f706f727407a04604747970650801026964054bb07dfcf4e6890c096c6173745f7365656e01cc1f2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700644ca6ee3066d5f706f727407a0460474797065080102696405d5865c1dd372a1ba096c6173745f7365656e01a21f2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970060e205b86066d5f706f727407a04604747970650801026964058dd26b584ab55719096c6173745f7365656e01a11f2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700695ca324a066d5f706f727407a04604747970650801026964059a3a9ef27b77f282096c6173745f7365656e01981f2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970065b798ca7066d5f706f727407a0460474797065080102696405b3c9d8ae8975cffa096c6173745f7365656e01841f2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970065e2a8c4d066d5f706f727407a046047479706508010269640578b5cedbd290b842096c6173745f7365656e015a1f2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006442dc4f4066d5f706f727407a04604747970650801026964056b4679bfcef716b2096c6173745f7365656e01571f2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b"
pkt16 := "5f616464726573733e28290c08046d5f69700652291660066d5f706f727407a04604747970650801026964055e9457952bce50cd096c6173745f7365656e01ad1e2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700618710dc7066d5f706f727407a0460474797065080102696405778306285e7ab66b096c6173745f7365656e01a81e2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006daea3f6e066d5f706f727407a0460474797065080102696405a0c6e7d127af9177096c6173745f7365656e018e1e2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006524541ca066d5f706f727407a0460474797065080102696405c14d04995da839c7096c6173745f7365656e017a1e2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006d8f689a6066d5f706f727407a0460474797065080102696405fdd4138e79e8982a096c6173745f7365656e01771e2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970065602b855066d5f706f727407a0460474797065080102696405913cdaeab53c6371096c6173745f7365656e016d1e2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006506b49a1066d5f706f727407a0460474797065080102696405b1ddf05dc6be790b096c6173745f7365656e011d1e2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006a3ac123d066d5f706f727407a2460474797065080102696405cc6bfbb7fa2bbe42096c6173745f7365656e01e81d2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970064bac6c1f066d5f706f727407a04604747970650801026964052eb6e73d4d67f81c096c6173745f7365656e01e21d2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970068ac98860066d5f706f727407a0460474797065080102696405116f2c033e504f15096c6173745f7365656e01d41d2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e"
pkt17 := "6574776f726b5f616464726573733e28290c08046d5f6970062e0b4119066d5f706f727407a0460474797065080102696405626b6c58922342d0096c6173745f7365656e01c31d2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700605bdc09f066d5f706f727407a0460474797065080102696405aba76ed16edcbc60096c6173745f7365656e01b71d2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970067ca871ab066d5f706f727407a0460474797065080102696405119a899dda114998096c6173745f7365656e01991d2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970062ead2989066d5f706f727407a0460474797065080102696405d9acc674e377271d096c6173745f7365656e01931d2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006c2be0b27066d5f706f727407a04604747970650801026964053a05693c409ba2bd096c6173745f7365656e015e1d2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700643bc4386066d5f706f727407a046047479706508010269640554c5cf9bcd0ec108096c6173745f7365656e013c1d2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006c64a35ab066d5f706f727407a046047479706508010269640583bab533d150fadb096c6173745f7365656e01101d2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970062df70d06066d5f706f727407a0460474797065080102696405b4c9fcb561bce1d4096c6173745f7365656e01d51c2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970"
pkt18 := "06a8eb679e066d5f706f727407a046047479706508010269640564ed073fd245007d096c6173745f7365656e01401c2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700643958154066d5f706f727407a04604747970650801026964052997c4e037048ac6096c6173745f7365656e01391c2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006b025a95b066d5f706f727407a0460474797065080102696405797a2cb5020dc535096c6173745f7365656e01f71b2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970068ac5b670066d5f706f727407a0460474797065080102696405e2e230417319c2a2096c6173745f7365656e01971b2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970065b4f07ae066d5f706f727407a0460474797065080102696405269637bc02ef1abe096c6173745f7365656e018c1b2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970068d69330b066d5f706f727407a0460474797065080102696405395719f6dd36d812096c6173745f7365656e015c1b2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700654f98048066d5f706f727407a046047479706508010269640594f474d261a11f9a096c6173745f7365656e01571b2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970063b7e944b066d5f706f727407a0460474797065080102696405c928eed89fa41de6096c6173745f7365656e01011b2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700648c93c9b066d5f706f727407a04604747970650801026964058db1ac5f9797a0ab096c6173745f7365656e01fc1a2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006d80abee5066d5f706f727407a0460474797065080102696405b496f4eb5680dce5096c6173745f7365656e01da1a2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c"
pkt19 := "08046d5f6970066c333deb066d5f706f727407a04604747970650801026964052d5ed7a7ac343731096c6173745f7365656e01cd1a2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970068ba244b9066d5f706f727407a046047479706508010269640576f40a10d781b61c096c6173745f7365656e01bc1a2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970065475582b066d5f706f727407a0460474797065080102696405159120bee1a7525c096c6173745f7365656e01a01a2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700668881108066d5f706f727407a046047479706508010269640501dbba89e194eb13096c6173745f7365656e019f1a2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700643085ee2066d5f706f727407a04604747970650801026964053b3085cbe3095193096c6173745f7365656e019d1a2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970069e8ccaac066d5f706f727407a04604747970650801026964057bcc12ac2fd4ec41096c6173745f7365656e012f1a2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006b21b88cd066d5f706f727407a04604747970650801026964052b595b3733f57c56096c6173745f7365656e0186192d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970065bd7e351066d5f706f727407a0460474797065080102696405b638cec7dfb7be8c096c6173745f7365656e0178192d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970068f597145066d5f706f727407a0460474797065080102696405d6a407050a822892096c6173745f7365656e0163192d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006253b61bd066d5f706f727407a04604747970650801026964056bc053b4cdfd0be5096c6173745f7365656e01ea182d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f6164647265"
pkt20 := "73733e28290c08046d5f6970064a50ed5e066d5f706f727407a0460474797065080102696405bef140ccc7dcf481096c6173745f7365656e01df182d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700692c75155066d5f706f727407a04604747970650801026964055a38b1e55e5318d1096c6173745f7365656e01cd182d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006af24d788066d5f706f727407a0460474797065080102696405d8f1b5eeacb97274096c6173745f7365656e01c4182d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006585b1c7c066d5f706f727407a046047479706508010269640519fb32baa195f121096c6173745f7365656e01b7182d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700642113069066d5f706f727407a0460474797065080102696405ca868ba31fd0ed9f096c6173745f7365656e0193182d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700653e44365066d5f706f727407a04604747970650801026964053389aa202a53dbea096c6173745f7365656e0150182d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970064a8bf1dd066d5f706f727407a046047479706508010269640538559bb7d3f52fee096c6173745f7365656e0143182d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700656a937f8066d5f706f727407a046047479706508010269640583632110dbb20d64096c6173745f7365656e0130182d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006b0d4e860066d5f706f727407a0460474797065080102696405767853706dc141e0096c6173745f7365656e01c0172d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970062a76bd6a066d5f706f727407a046047479706508010269640572c4a28309b6dc0e096c6173745f7365656e01aa172d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b"
pkt21 := "5f616464726573733e28290c08046d5f69700605c781ea066d5f706f727407a046047479706508010269640579634df91a79eed1096c6173745f7365656e01a5172d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970064d460151066d5f706f727407a04604747970650801026964051e6c75c66f5429f1096c6173745f7365656e0136172d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006524b5de1066d5f706f727407a0460474797065080102696405dc22c083d1053d57096c6173745f7365656e010d172d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970061f23ad26066d5f706f727407a0460474797065080102696405cc24fe35f2a84c23096c6173745f7365656e01d0162d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006a772d28d066d5f706f727407a046047479706508010269640553be187ce01848ff096c6173745f7365656e01be162d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006a3ac0d54066d5f706f727407a0460474797065080102696405dbc278d5c71a3fb8096c6173745f7365656e01b8162d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700618bac39a066d5f706f727407a04604747970650801026964055513dad620e59e55096c6173745f7365656e0184162d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970067bf3138c066d5f706f727407a04604747970650801026964052feb5e81676ecf59096c6173745f7365656e0182162d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006deea0a0c066d5f706f727407a04604747970650801026964058abb018ee61980b1096c6173745f7365656e016d162d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006d47866d5066d5f706f727407a0460474797065080102696405038f82ecf8103e78096c6173745f7365656e0164162d5a000000000c036164720c082374656d706c6174652061733c697076345f6e"
pkt22 := "6574776f726b5f616464726573733e28290c08046d5f6970065a41157b066d5f706f727407a04604747970650801026964052b223aed5fc784b7096c6173745f7365656e0161162d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970062585c11a066d5f706f727407a0460474797065080102696405caf332866c4ab43a096c6173745f7365656e014f162d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006bad653f6066d5f706f727407a0460474797065080102696405f2dc8c348848333f096c6173745f7365656e01f8152d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006546c6cea066d5f706f727407a046047479706508010269640562263b041206d216096c6173745f7365656e01aa152d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970064e2e5b86066d5f706f727407b06d047479706508010269640574e3e5edb5b87c01096c6173745f7365656e0198152d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970065c3f2db9066d5f706f727407a0460474797065080102696405580b582fb04fbaf8096c6173745f7365656e0193152d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970065161fe40066d5f706f727407a0460474797065080102696405140ce0bb7d6753c3096c6173745f7365656e0190152d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006d809b134066d5f706f727407a046047479706508010269640522c99d60c5609e7e096c6173745f7365656e0190152d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006819e4279066d5f706f727407a04604747970650801026964054caee8075f93e983096c6173745f7365656e0131142d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006565b2e3d066d5f706f727407a0460474797065080102696405bcbeaaa342e50bdc096c6173745f7365656e01ee132d5a000000000c036164720c082374656d706c6174652061733c"
pkt23 := "697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700644eb39fa066d5f706f727407a04604747970650801026964059731416c8258768a096c6173745f7365656e01ec132d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970066c0419b1066d5f706f727407a046047479706508010269640542681128e219389a096c6173745f7365656e01c0132d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970064ea94775066d5f706f727407a04604747970650801026964058a68504d7542f985096c6173745f7365656e01b5132d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006558fb88d066d5f706f727407a04604747970650801026964055feacafbb881ca72096c6173745f7365656e019d132d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006a3ac223a066d5f706f727407a04604747970650801026964051b5aad98b945b0a6096c6173745f7365656e0134132d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700650f1d3bc066d5f706f727407a04604747970650801026964057f00b39fbac2a2d2096c6173745f7365656e0127132d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006a3acff39066d5f706f727407a0460474797065080102696405b1d3267909bae7e5096c6173745f7365656e0170122d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006be62021b066d5f706f727407a0460474797065080102696405cc5acc2be5f2b10f096c6173745f7365656e0147122d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700625b6986c066d5f706f727407a0460474797065080102696405a4b15b169158a015096c6173745f7365656e01c6112d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006b028c01b066d5f706f727407a04604747970650801026964058efbb33df658f4af096c6173745f7365656e01ba112d5a000000000c036164720c082374656d706c61"
pkt24 := "74652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006591b0587066d5f706f727407a0460474797065080102696405afc949f19ae28c63096c6173745f7365656e0195112d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970061806b371066d5f706f727407a04604747970650801026964054660018f27ac16cb096c6173745f7365656e017f112d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700602566daa066d5f706f727407a0460474797065080102696405e253da8371fba38c096c6173745f7365656e0171112d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700679dd4525066d5f706f727407a0460474797065080102696405bc86deeaa78d8d51096c6173745f7365656e0164112d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970062bfc6f6a066d5f706f727407a0460474797065080102696405075c9f398eba224e096c6173745f7365656e013c112d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700654b912ad066d5f706f727407a0460474797065080102696405e39d3c5230cb953d096c6173745f7365656e0138112d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700652fd6f1b066d5f706f727407a0460474797065080102696405596cb6bf116f38e3096c6173745f7365656e01a2102d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006488c5602066d5f706f727407a0460474797065080102696405fc7c10885b0de0d5096c6173745f7365656e018d102d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006a3acff34066d5f706f727407a0460474797065080102696405b0c19a820f57e351096c6173745f7365656e018d102d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006d4b69b2d066d5f706f727407a046047479706508010269640510b4ef63f0456a87096c6173745f7365656e0183102d5a000000000c036164720c0823"
pkt25 := "74656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700644292257066d5f706f727407a04604747970650801026964054800b3f181b891a3096c6173745f7365656e011c102d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006b16204af066d5f706f727407a0460474797065080102696405de954b6acb9fe67a096c6173745f7365656e010d102d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006cbdb2566066d5f706f727407a046047479706508010269640541c285cd8d83ad03096c6173745f7365656e01e70f2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700648cc2527066d5f706f727407a0460474797065080102696405aaf77cb662a45c44096c6173745f7365656e01760f2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970063ed2bd6a066d5f706f7274076846047479706508010269640581e6000b164f1b8c096c6173745f7365656e01220f2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700648da8b39066d5f706f727407a04604747970650801026964058ffd383095c6a500096c6173745f7365656e010f0f2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006c39afabc066d5f706f727407a0460474797065080102696405badb0eddf1401bcc096c6173745f7365656e01d10e2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970069e45f285066d5f706f727407a0460474797065080102696405a4d5b6cb15d775bc096c6173745f7365656e01af0e2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006b3db6915066d5f706f727407a0460474797065080102696405c03e41b41c52218c096c6173745f7365656e01a00e2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700625eb46cf066d5f706f727407a0460474797065080102696405fcda3bedbac144b8096c6173745f7365656e01810e2d5a000000000c03"
pkt26 := "6164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700664203122066d5f706f727407a0460474797065080102696405d2f4fd49e43aee8a096c6173745f7365656e01630e2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006590e6880066d5f706f727407a04604747970650801026964052d06b312ae4da3aa096c6173745f7365656e01470e2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700676d1fca8066d5f706f727407a0460474797065080102696405e350dc70e91faaae096c6173745f7365656e013a0e2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006567ea70f066d5f706f727407a0460474797065080102696405593ad0d815bf46a3096c6173745f7365656e01390e2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006c5f53004066d5f706f727407a0460474797065080102696405706c5da464ac04ff096c6173745f7365656e01700d2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006c654df93066d5f706f727407a04604747970650801026964059233e6310c64bc74096c6173745f7365656e015c0d2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700651eb2b34066d5f706f727407a0460474797065080102696405dbd7e2452e452a97096c6173745f7365656e010b0d2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006cbdd045e066d5f706f727407a04604747970650801026964058ed3d5f429f1f50c096c6173745f7365656e01090d2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700655cc6110066d5f706f727407a0460474797065080102696405bce41a041bba6ac1096c6173745f7365656e01c30c2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970064ddd4c42066d5f706f727407a046047479706508010269640569958c02c54bf2af096c6173745f7365656e017d0c2d5a"
pkt27 := "000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970064f4be504066d5f706f727407a0460474797065080102696405d198c12f826920a7096c6173745f7365656e01790c2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006d5c50f7e066d5f706f727407a0460474797065080102696405f6a0b5467f05b205096c6173745f7365656e01730c2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006cbdad4bd066d5f706f727407a0460474797065080102696405c25bc4cfafd51667096c6173745f7365656e01590c2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006052343f1066d5f706f727407a046047479706508010269640543687e9093c4a8e5096c6173745f7365656e01560c2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970065c26ed81066d5f706f727407a04604747970650801026964051a33225cb494e9f1096c6173745f7365656e01fc0b2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970062e303e99066d5f706f727407a04604747970650801026964055633b701fd83b07b096c6173745f7365656e01f90b2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700664022677066d5f706f727407a0460474797065080102696405334fd27483c03c53096c6173745f7365656e01f40b2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970063ed25ee1066d5f706f727407a746047479706508010269640569e745cde93841c5096c6173745f7365656e01e20b2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970067d294aef066d5f706f727407a0460474797065080102696405647585d27f9d01a1096c6173745f7365656e01aa0b2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970064246dde0066d5f706f727407a0460474797065080102696405e8202e86eab81cf7096c6173745f736565"
pkt28 := "6e01900b2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970063bb2a1a3066d5f706f727407a046047479706508010269640587f958a58a5edf58096c6173745f7365656e01600b2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006cc2c60ae066d5f706f727407a04604747970650801026964051ac6163a952e1e14096c6173745f7365656e01510b2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006a3acff36066d5f706f727407a0460474797065080102696405fd6552d454a535a8096c6173745f7365656e01440b2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970063ed25ee1066d5f706f727407a646047479706508010269640567b6521faa80a58f096c6173745f7365656e01410b2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700677ed6acf066d5f706f727407a04604747970650801026964051381c53afe90abaa096c6173745f7365656e01020b2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970063def0cd8066d5f706f727407a04604747970650801026964055506e9319df085d1096c6173745f7365656e01af0a2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006de895d55066d5f706f727407a0460474797065080102696405fc57428c8df06894096c6173745f7365656e01ab0a2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700618ba77ef066d5f706f727407a046047479706508010269640569986492a2f830bc096c6173745f7365656e01a80a2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700656bc14d3066d5f706f727407a046047479706508010269640591943c25ed4c5080096c6173745f7365656e01740a2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006af88623c066d5f706f727407a04604747970650801026964055eea824cee690714096c61"
pkt29 := "73745f7365656e01720a2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006d5153025066d5f706f727407a046047479706508010269640577cabc3d0cb226a8096c6173745f7365656e013f0a2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006423944e4066d5f706f727407a04604747970650801026964058f06566ca4144c22096c6173745f7365656e013f0a2d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700649e50006066d5f706f727407a046047479706508010269640524443fb9ab86d621096c6173745f7365656e01e9092d"
pkt30 := "5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970064d66d2a0066d5f706f727407a04604747970650801026964050f157a7fe9e2bd8e096c6173745f7365656e01e8092d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970065ac7f5e4066d5f706f727407a0460474797065080102696405e89a776bf4daf58d096c6173745f7365656e01ca092d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970065aca6a36066d5f706f727407a0460474797065080102696405f01e6654c8272f4f096c6173745f7365656e01bc092d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006adf7e926066d5f706f727407a0460474797065080102696405c476478ce765c60e096c6173745f7365656e011c092d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006d0750beb066d5f706f727407a0460474797065080102696405ef12fda0c8980dcf096c6173745f7365656e01d7082d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700660087525066d5f706f727407a0460474797065080102696405b87b36c8f4d6642b096c6173745f7365656e01aa082d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006276d9971066d5f706f727407a0460474797065080102696405ef2863ae135dd146096c6173745f7365656e0138082d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970064ad3433d066d5f706f727407a0460474797065080102696405287ca4e84380440f096c6173745f7365656e0114082d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970065b796e86066d5f706f727407a0460474797065080102696405516bd12b232ad642096c6173745f7365656e018b072d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970065d8f68f2066d5f706f727407a0460474797065080102696405704ad266f503c5cf096c6173745f7365"
pkt31 := "656e017c072d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006a3ac1325066d5f706f727407a0460474797065080102696405bd02703a979f621c096c6173745f7365656e015a072d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006ae8a208e066d5f706f727407b06d04747970650801026964051e9cf30ad73aad48096c6173745f7365656e0104072d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006b9a5449a066d5f706f727407a04604747970650801026964056f664b9b290351a3096c6173745f7365656e0103072d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700673db2303066d5f706f727407a0460474797065080102696405dc4f0b624ba2ea30096c6173745f7365656e01db062d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970064b8cf4c0066d5f706f727407a046047479706508010269640525cf9eab20ef6c2a096c6173745f7365656e018a062d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006b0c37985066d5f706f727407a04604747970650801026964053731fb0ab15f74e3096c6173745f7365656e0187062d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700649c0edf8066d5f706f727407a04604747970650801026964059c06dc3761c9ca40096c6173745f7365656e017a062d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006c7e7557a066d5f706f727407a0460474797065080102696405ed5439c2fff10bd5096c6173745f7365656e0102062d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006b0c19c9c066d5f706f727407a046047479706508010269640552c2ccf08106c064096c6173745f7365656e01c6052d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970065848bc44066d5f706f727407a0460474797065080102696405f0d739a58554ee9f096c"
pkt32 := "6173745f7365656e01c6052d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970064cba7a11066d5f706f727407a046047479706508010269640514ad822192813a08096c6173745f7365656e015e052d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006485e572c066d5f706f727407a04604747970650801026964054dacb99d2769abee096c6173745f7365656e0119052d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700665a53810066d5f706f727407a0460474797065080102696405d1478b2a7562baf5096c6173745f7365656e010d052d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006bce8e1fe066d5f706f727407a0460474797065080102696405da65626afe89649b096c6173745f7365656e01c2042d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006675a9b02066d5f706f727407a0460474797065080102696405ee5a4b2021ddbaa0096c6173745f7365656e0183042d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700658b00c64066d5f706f727407a04604747970650801026964053aebc6cbbef9f213096c6173745f7365656e0141042d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970062d4f630f066d5f706f727407a04604747970650801026964058560f49b748e3822096c6173745f7365656e01d5032d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970063e8d2676066d5f706f727407a0460474797065080102696405e4012c5db1fa5248096c6173745f7365656e01ca032d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970063d0eee5c066d5f706f727407a0460474797065080102696405474611d52689cc21096c6173745f7365656e0184032d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970063143acde066d5f706f727407a046047479706508010269640504ccb9a3"
pkt33 := "a7ff4eba096c6173745f7365656e0132032d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006adffc2d7066d5f706f727407a04604747970650801026964052f87a7d0bb905dd9096c6173745f7365656e01cd022d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006ad116f7a066d5f706f727407a0460474797065080102696405b49cf00e56057462096c6173745f7365656e01a4022d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700656b27f74066d5f706f727407a046047479706508010269640501a499da9ce04d2c096c6173745f7365656e018c022d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970060256e19a066d5f706f727407a04604747970650801026964054e7bac4253c903d6096c6173745f7365656e0189022d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006b1130bf4066d5f706f727407a04604747970650801026964054af5ff085e7ea9cb096c6173745f7365656e0151022d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006ae6975a2066d5f706f727407a04604747970650801026964051d03386810853991096c6173745f7365656e0150022d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006568b8f28066d5f706f727407a046047479706508010269640571b69cd0a237ad2c096c6173745f7365656e0146022d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006616559dc066d5f706f727407a0460474797065080102696405a2b6cc14cbdfa1ed096c6173745f7365656e0125022d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970066c3074ac066d5f706f727407a0460474797065080102696405aa2de6d754dd4432096c6173745f7365656e01bd012d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f6970066030fa41066d5f706f727407a046047479706508010269"
pkt34 := "64052aca1553f8b11c34096c6173745f7365656e01b0012d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f697006558c2804066d5f706f727407a0460474797065080102696405bb5bf92c0843e0ac096c6173745f7365656e0199012d5a000000000c036164720c082374656d706c6174652061733c697076345f6e6574776f726b5f616464726573733e28290c08046d5f69700656a6911b066d5f706f727407a046047479706508010269640581862a87a5adf2db096c6173745f7365656e017e012d5a00000000096e6f64655f646174610c100a6c6f63616c5f74696d65055d2a2d5a00000000076d795f706f727406a04600000a6e6574776f726b5f69640a401230f171610441611731008216a1a11007706565725f6964055b0ae5e603fddefa0c7061796c6f61645f646174610c101563756d756c61746976655f646966666963756c74790506c317fa2b2813000e63757272656e745f686569676874055a4d16000000000006746f705f69640a80531ce53560e2f90c598e5a2c98da4560bc0cc5f95cca18dc144cd791830c0a520b746f705f76657273696f6e0806"
full_hex := pkt1 + pkt2 + pkt3 + pkt4 + pkt5 + pkt6 + pkt7 + pkt8 + pkt9
full_hex += pkt10 + pkt11 + pkt12 + pkt13 + pkt14 + pkt15 + pkt16 + pkt17 + pkt18 + pkt19
full_hex += pkt20 + pkt21 + pkt22 + pkt23 + pkt24 + pkt25 + pkt26 + pkt27 + pkt28 + pkt29
full_hex += pkt30 + pkt31 + pkt32 + pkt33 + pkt34
binary_buffer, _ := hex.DecodeString(full_hex)
ip_offset := 50 // various offsets in local_peerlist_new
port_offset := 62
peerid_offset := 75
lastseen_offset := 75 + 19
/*
pos := 18 // offset of old ip table
new_pos := 18 + 24*250 + 23 // offset of new ip table
for i := 0; i < 250;i++{ // each entry is 24 bytes
// each 24 byte contains
//4 byte ip, 4 byte port, 8 byte peer_id , 8 byte last_seen
// new structure has only 2 bytes for peer list
zero_pos := new_pos+(i*103)
if i < len(data.PeerArray) {
if data.PeerArray[i].IP.To4() != nil {
}
}
// add a null entry junkpeer
junkpeer := data.PeerArray[i]
_ = pos
_ = junkpeer
// add entry to traditional entry
binary_buffer[pos+(i*24)+0] = junkpeer.IP[12]
binary_buffer[pos+(i*24)+1] = junkpeer.IP[13]
binary_buffer[pos+(i*24)+2] = junkpeer.IP[14]
binary_buffer[pos+(i*24)+3] = junkpeer.IP[15]
binary.LittleEndian.PutUint32(binary_buffer[pos+(i*24)+4:],junkpeer.Port) // packed 8+8+1+4 bytes
binary.LittleEndian.PutUint64(binary_buffer[pos+(i*24)+8:],junkpeer.ID) // packed 8+8+1+4 bytes
binary.LittleEndian.PutUint64(binary_buffer[pos+(i*24)+16:],junkpeer.LastSeen)
// add entry to new entry
binary_buffer[zero_pos+ip_offset+0] = junkpeer.IP[12]
binary_buffer[zero_pos+ip_offset+1] = junkpeer.IP[13]
binary_buffer[zero_pos+ip_offset+2] = junkpeer.IP[14]
binary_buffer[zero_pos+ip_offset+3] = junkpeer.IP[15]
binary.LittleEndian.PutUint16(binary_buffer[zero_pos+port_offset:],uint16(junkpeer.Port)) // packed 8+8+1+4 bytes
binary.LittleEndian.PutUint64(binary_buffer[zero_pos+peerid_offset:],junkpeer.ID) // packed 8+8+1+4 bytes
binary.LittleEndian.PutUint64(binary_buffer[zero_pos+lastseen_offset:],junkpeer.LastSeen)
}
*/
_ = ip_offset
_ = port_offset
_ = peerid_offset
_ = lastseen_offset
//pos = 0x7c2e; // at this point to node data and should be parsed as such
pos := bytes.Index(binary_buffer, []byte("\x09node_data")) // at this point to node data and should be parsed as such
tbuf, _ := data.NodeData.Serialize()
copy(binary_buffer[pos:], tbuf[:])
// pos = 0x7c2e + 91 // at this point to node data and should be parsed as such
pos = bytes.Index(binary_buffer, []byte("\x0cpayload_data")) // at this point to node data and should be parsed as such
tbuf, _ = data.CoreData.Serialize()
copy(binary_buffer[pos:], tbuf[:])
return binary_buffer, len(binary_buffer)
}
func (data *Node_Data_Response) DeSerialize(binary_buffer []byte) (err error) {
/*
if len(binary_buffer) != 32006 {
return fmt.Errorf("Insufficient Buffer")
}
*/
// IP information is not mandatory
logger.Warnf("We are still NOT using Peer information")
/*
// find peer list only if its present
ip_offset := 50 // various offsets in local_peerlist_new
port_offset := 62
peerid_offset := 75
lastseen_offset := 75+19
pos := 18 // offset of old ip table
new_pos := 18 + 24*250 + 23 // offset of new ip table
for i := 0; i < 250;i++{ // each entry is 24 bytes
// each 24 byte contains
//4 byte ip, 4 byte port, 8 byte peer_id , 8 byte last_seen
// new structure has only 2 bytes for peer list
zero_pos := new_pos+(i*103)
var p Peer_Info
p.IP = net.IPv4( binary_buffer[zero_pos+ip_offset],
binary_buffer[zero_pos+ip_offset+1],
binary_buffer[zero_pos+ip_offset+2],
binary_buffer[zero_pos+ip_offset+3])
p.Port = (uint32)(binary.LittleEndian.Uint16(binary_buffer[zero_pos+port_offset:]))
p.ID = binary.LittleEndian.Uint64(binary_buffer[zero_pos+peerid_offset:])
p.LastSeen= binary.LittleEndian.Uint64(binary_buffer[zero_pos+lastseen_offset:])
data.PeerArray = append(data.PeerArray, p)
// fmt.Printf("%+v\n", p)
}
*/
// we need not serialize local_peerlist_new
// however we need to do it when serialize
//pos = 18 + 24*250 + 23 // each new entry is 101 bytes long
//fmt.Printf("new buffer %x\n",binary_buffer[pos:pos+20])
/*
for i := 0; i < 250;i++{ // each entry is 24 bytes
zero_pos := pos+(i*101)
fmt.Printf("%d %x\n",i, binary_buffer[pos+(i*101): pos+(i*101)+101])
}*/
// locate node_data
pos := bytes.Index(binary_buffer, []byte("node_data")) // at this point to node data and should be parsed as such
if pos == -1 {
return fmt.Errorf("Node data not found, its mandatory")
}
//fmt.Printf("node data %x\n",binary_buffer[pos+9: pos+25])
err = data.NodeData.DeSerialize(binary_buffer[pos-1:]) // 9 bytes node_data_len
if err != nil {
return err
}
// fmt.Printf("%+v \n", data.NodeData)
pos = bytes.Index(binary_buffer, []byte("payload_data")) // at this point to node data and should be parsed as such
err = data.CoreData.DeSerialize(binary_buffer[pos-1:])
// fmt.Printf("%+v \n", data.CoreData)
if err != nil {
return err
}
return nil
}
/* deserialize a buffer */
func deserialize() {
hexdata := "01110101010102010108096e6f64655f646174610c100a6c6f63616c5f74696d6505d56b2a5a00000000076d795f706f727406000000000a6e6574776f726b5f69640a401230f171610441611731008216a1a11107706565725f696405a350efa02d3904920c7061796c6f61645f646174610c101563756d756c61746976655f646966666963756c74790501000000000000000e63757272656e745f68656967687405010000000000000006746f705f69640a8048ca7cd3c8de5b6a4d53d2861fbdaedca141553559f9be9520068053cda8430b0b746f705f76657273696f6e0801"
binary_buffer, _ := hex.DecodeString(hexdata)
var n Node_Data
n.Local_time = 1512729557
n.Local_Port = 0
n.Network_UUID.UnmarshalBinary([]byte{18, 48, 241, 113, 97, 4, 65, 97, 23, 49, 0, 130, 22, 161, 161, 17})
n.Peer_ID = 0x4ea0e7e6432fd5ce
result, _ := n.Serialize()
fmt.Printf("SerializeNodeData %s\n", hex.EncodeToString(result))
var c CORE_DATA
c.Current_Height = 1
c.Top_Version = 1
c.Cumulative_Difficulty = 1
copy(c.Top_ID[:], []byte{0x48, 0xca, 0x7c, 0xd3, 0xc8, 0xde, 0x5b, 0x6a, 0x4d, 0x53, 0xd2,
0x86, 0x1f, 0xbd, 0xae, 0xdc, 0xa1, 0x41, 0x55, 0x35, 0x59, 0xf9, 0xbe, 0x95, 0x20, 0x06, 0x80,
0x53, 0xcd, 0xa8, 0x43, 0x0b})
result, _ = c.Serialize()
fmt.Printf("SerializeCoreData %s\n", hex.EncodeToString(result))
boost_deserialize(binary_buffer)
}
// deserialize the buf bytes
func boost_deserialize(buf []byte) map[string]interface{} {
if buf[0] != 1 {
panic("Invalid data")
}
// skip 8 byte signature
pos := 8 + 1
for {
// Decode Container
switch buf[pos] {
case 0x8: // start of structure, decode structure name
length := int(buf[pos+1])
name := string(buf[pos+1+1 : pos+1+1+length])
fmt.Printf("Structure name %s\n", name)
pos += length + 1 + 1
case 0xc:
// everything is key value pair
// jump to specific decoder
key_value_count := int(buf[pos+1])
fmt.Printf("Unknown data type 0xc value %d, 16 values\n", buf[pos+1])
pos += 1 + 1
for i := 0; i < key_value_count/2; i++ {
_, _, done := Decode_Key_Value(buf[pos:])
pos += done
}
default:
fmt.Printf("unknown boost encoding '0x%02x'\n", buf[pos])
goto done
}
}
done:
;
return nil
}
func Decode_Key_Value(buf []byte) (key string, value interface{}, done int) {
pos := 0
key_length := int(buf[0])
key = string(buf[1 : 1+key_length])
fmt.Printf("Key %s ", key)
pos = 1 + key_length
done = 1 + key_length
switch buf[pos] {
case 0x5: // 64 bit little endian
value = binary.LittleEndian.Uint64(buf[pos+1:])
done += 1 + 8
case 0x6: // 32 bit little endian
value = binary.LittleEndian.Uint32(buf[pos+1:])
done += 1 + 4
case 0x7: // 16 bit liitle endia
value = binary.LittleEndian.Uint16(buf[pos+1:])
done += 1 + 2
case 0x8: // single byte
value = (buf[pos+1])
done += 1 + 1
case 0xa: // this is a boost varint decoded elsewhere
value = buf[pos+1+1 : pos+1+1+16]
done += 1 + 1 + 16
default:
fmt.Printf("unknown boost encoding '0x%02x'\n", buf[pos])
}
fmt.Printf("Key %s %+v\n", key, value)
return key, value, done
}

+ 176
- 0
p2p/p2p_send_and_receive_timedsync.go

@ -0,0 +1,176 @@
package p2p
import "bytes"
import "encoding/binary"
import "github.com/romana/rlog"
// this file sends a timed sync packet and parses response
/* below is a timed sync request
000001DE 01 21 01 01 01 01 01 01 87 00 00 00 00 00 00 00 .!...... ........
000001EE 01 ea 03 00 00 00 00 00 00 01 00 00 00 01 00 00 ........ ........
000001FE 00 .
000001FF 01 11 01 01 01 01 02 01 01 04 0c 70 61 79 6c 6f ........ ...paylo
0000020F 61 64 5f 64 61 74 61 0c 10 15 63 75 6d 75 6c 61 ad_data. ..cumula
0000021F 74 69 76 65 5f 64 69 66 66 69 63 75 6c 74 79 05 tive_dif ficulty.
0000022F 8e 4a f1 09 da 00 00 00 0e 63 75 72 72 65 6e 74 .J...... .current
0000023F 5f 68 65 69 67 68 74 05 53 4b 00 00 00 00 00 00 _height. SK......
0000024F 06 74 6f 70 5f 69 64 0a 80 1d ca fd 23 08 50 54 .top_id. ....#.PT
0000025F 16 e0 41 c8 33 e6 db 91 ca 33 6b b1 fb af 9d a1 ..A.3... .3k.....
0000026F 99 f2 77 85 5d a1 e8 2a fa 0b 74 6f 70 5f 76 65 ..w.]..* ..top_ve
0000027F 72 73 69 6f 6e 08 06 rsion..
00000000 01 21 01 01 01 01 01 01 87 00 00 00 00 00 00 00 .!...... ........
00000010 01 ea 03 00 00 00 00 00 00 01 00 00 00 01 00 00 ........ ........
00000020 00 .
00000021 01 11 01 01 01 01 02 01 01 04 0c 70 61 79 6c 6f ........ ...paylo
00000031 61 64 5f 64 61 74 61 0c 10 15 63 75 6d 75 6c 61 ad_data. ..cumula
00000041 74 69 76 65 5f 64 69 66 66 69 63 75 6c 74 79 05 tive_dif ficulty.
00000051 2e 9d 54 af fd 00 00 00 0e 63 75 72 72 65 6e 74 ..T..... .current
00000061 5f 68 65 69 67 68 74 05 b6 4c 00 00 00 00 00 00 _height. .L......
00000071 06 74 6f 70 5f 69 64 0a 80 64 52 25 11 88 5a ca .top_id. .dR%..Z.
00000081 10 c8 f9 28 c5 ad a4 03 e1 4f 6d 68 23 8c c5 ea ...(.... .Omh#...
00000091 73 e6 16 58 37 d1 96 22 07 0b 74 6f 70 5f 76 65 s..X7.." ..top_ve
000000A1 72 73 69 6f 6e 08 06
01 21 01 01 01 01 01 01 87 00 00 00 00 00 00 00
01 ea 03 00 00 00 00 00 00 01 00 00 00 01 00 00
00
01 11 01 01 01 01 02 01 01 04 0c 70 61 79 6c 6f
61 64 5f 64 61 74 61 0c 10 15 63 75 6d 75 6c 61
74 69 76 65 5f 64 69 66 66 69 63 75 6c 74 79 05
2e 9d 54 af fd 00 00 00 0e 63 75 72 72 65 6e 74
5f 68 65 69 67 68 74 b6 4c00000000000000
06 74 6f 70 64 52 25 11 88 5a ca 10 c8 f9 28 c5
ada403e14f6d68238cc5ea73e6165837
d19622075da1e82afa0b746f705f7665
72 73 69 6f 6e 08 06
01210101010101018700000000000000
01ea0300000000000001000000010000
00
011101010101020101040c7061796c6f
61645f646174610c101563756d756c61
746976655f646966666963756c747905
2e9d54affd0000000e63757272656e74
5f 68 65 69 67 68 74 05 b6 4c 00 00 00 00 00 00
06 74 6f 70 5f 69 64 0a 80 1d ca fd 23 08 50 64
522511885aca10c8f928c5ada403e14f
6d68238cc5ea73e6165837d196220765
7273696f6e0806
*/
func Send_Timed_Sync(connection *Connection) {
request_packet:=[]byte { 0x01, 0x21, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0xea, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00,
0x01, 0x11, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01,
0x01, 0x04, 0x0c, 0x70, 0x61, 0x79, 0x6c, 0x6f,
0x61, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x0c,
0x10, 0x15, 0x63, 0x75, 0x6d, 0x75, 0x6c, 0x61,
0x74, 0x69, 0x76, 0x65, 0x5f, 0x64, 0x69, 0x66,
0x66, 0x69, 0x63, 0x75, 0x6c, 0x74, 0x79, 0x05,
0x8e, 0x4a, 0xf1, 0x09, 0xda, 0x00, 0x00, 0x00,
0x0e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74,
0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x05,
0x53, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x06, 0x74, 0x6f, 0x70, 0x5f, 0x69, 0x64, 0x0a,
0x80, 0x1d, 0xca, 0xfd, 0x23, 0x08, 0x50, 0x54,
0x16, 0xe0, 0x41, 0xc8, 0x33, 0xe6, 0xdb, 0x91,
0xca, 0x33, 0x6b, 0xb1, 0xfb, 0xaf, 0x9d, 0xa1,
0x99, 0xf2, 0x77, 0x85, 0x5d, 0xa1, 0xe8, 0x2a,
0xfa, 0x0b, 0x74, 0x6f, 0x70, 0x5f, 0x76, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x08, 0x06, };
// write our data into the packet
top_id := chain.Get_Top_ID()
cumulative_diff := chain.Load_Block_Cumulative_Difficulty(top_id)
height := chain.Get_Height()
top_version := byte(6)
// now lets write that into the buffer
binary.LittleEndian.PutUint64(request_packet[81:],cumulative_diff)
binary.LittleEndian.PutUint64(request_packet[105:],height)
copy(request_packet[122:],top_id[:])
request_packet[len(request_packet)-1]= top_version
//connection.logger.Infof("sync request packet %x top_id %x", request_packet, top_id)
connection.Lock()
defer connection.Unlock()
connection.Conn.Write(request_packet)
connection.Command_queue.PushBack(uint32(P2P_COMMAND_TIMED_SYNC))
}
// handle response of above command
// response comes as below
/*
00000000 01 21 01 01 01 01 01 01 9b 00 00 00 00 00 00 00 .!...... ........
00000010 00 ea 03 00 00 00 00 00 00 02 00 00 00 01 00 00 ........ ........
00000020 00 .
00000021 0c 70 61 79 6c 6f 61 64 5f 64 61 74 61 0c 10 15 .payload _data...
00000031 63 75 6d 75 6c 61 74 69 76 65 5f 64 69 66 66 69 cumulati ve_diffi
00000041 63 75 6c 74 79 05 8e 4a f1 09 da 00 00 00 0e 63 culty..J .......c
00000051 75 72 72 65 6e 74 5f 68 65 69 67 68 74 05 52 4b urrent_h eight.RK
00000061 00 00 00 00 00 00 06 74 6f 70 5f 69 64 0a 80 1d .......t op_id...
00000071 ca fd 23 08 50 54 16 e0 41 c8 33 e6 db 91 ca 33 ..#.PT.. A.3....3
00000081 6b b1 fb af 9d a1 99 f2 77 85 5d a1 e8 2a fa 0b k....... w.]..*..
00000091 74 6f 70 5f 76 65 72 73 69 6f 6e 08 06 01 11 01 top_vers ion.....
000000A1 01 01 01 02 01 01 08 0a 6c 6f 63 61 6c 5f 74 69 ........ local_ti
000000B1 6d 65 05 df 3d 5b 5a 00 00 00 00 me..=[Z. ...
*/
func Handle_P2P_Timed_Sync_Response(connection *Connection,
i_command_header *Levin_Header, buf []byte) {
var i_data_header Levin_Data_Header // incoming data header, deserialize it
var peer_core_data CORE_DATA
err := i_data_header.DeSerialize(buf)
if err != nil {
connection.logger.Debugf("Invalid Levin Data header, disconnecting peer")
connection.Exit = true
return
}
// parse incoming core data
pos := bytes.Index(i_data_header.Data, []byte("payload_data")) // at this point to node data and should be parsed as such
if pos < 0 {
connection.logger.Debugf("Invalid P2P_COMMAND_TIMED_SYNC_T, could not find payload_data, disconnecting peer")
connection.Exit = true
return
}
err = peer_core_data.DeSerialize(i_data_header.Data[pos-1:])
if err != nil {
connection.logger.Debugf("Invalid P2P_COMMAND_TIMED_SYNC_T, could not deserialize core_data, disconnecting peer")
connection.Exit = true
return
}
rlog.Trace(5, "Incoming core data %+v \n", peer_core_data)
// lets check whether we need to resync with this peer
if chain.IsLagging(peer_core_data.Cumulative_Difficulty, peer_core_data.Current_Height, peer_core_data.Top_ID) {
connection.logger.Debugf("We need to resync with the peer")
// set mode to syncronising
Send_BC_Notify_Chain_Command(connection)
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save