@ -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----- |
@ -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. |
@ -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 |
|||
} |
@ -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 |
|||
} |
|||
|
|||
} |
|||
} |
@ -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 |
|||
} |
@ -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" |
|||
} |
|||
} |
@ -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"] |
|||
} |
|||
} |
@ -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"] |
|||
} |
|||
} |
@ -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" |
|||
} |
|||
} |
@ -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" |
|||
} |
|||
} |
@ -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" |
|||
} |
|||
} |
@ -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"] |
|||
} |
|||
} |
@ -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" |
|||
} |
|||
} |
@ -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"] |
|||
} |
|||
} |
@ -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" |
|||
} |
|||
} |
@ -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"] |
|||
} |
|||
} |
@ -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" |
|||
} |
|||
} |
@ -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" |
|||
} |
|||
} |
@ -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) |
|||
} |
@ -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 |
|||
|
|||
} |
|||
|
@ -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") |
|||
} |
|||
|
|||
} |
@ -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 |
|||
} |
@ -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 "" |
|||
} |
@ -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 |
|||
} |
@ -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 |
|||
} |
@ -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 |
|||
}*/ |
|||
|
|||
} |
@ -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 |
|||
} |
@ -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 |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
} |
@ -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 |
@ -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 |
|||
|
|||
*/ |
@ -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 |
|||
} |
@ -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 |
|||
} |
@ -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 |
|||
} |
|||
|
|||
|
|||
}*/ |
@ -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 |
@ -0,0 +1,8 @@ |
|||
package main |
|||
|
|||
import "testing" |
|||
|
|||
|
|||
func Test_Part1(t *testing.T){ |
|||
|
|||
} |
@ -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 |
|||
} |
@ -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
|
|||
} |
@ -0,0 +1,8 @@ |
|||
package config |
|||
|
|||
import "testing" |
|||
|
|||
|
|||
func Test_Part1(t *testing.T){ |
|||
|
|||
} |
@ -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 |
|||
} |
@ -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 |
|||
} |
@ -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) |
|||
} |
|||
} |
|||
} |
@ -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 |
|||
} |
@ -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 |
|||
} |
@ -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 |
|||
} |
@ -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 |
|||
} |
@ -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 |
|||
} |
@ -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 |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
} |
@ -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 |
|||
} |
@ -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 |
|||
} |
@ -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 |
|||
} |
|||
} |
|||
} |
@ -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, |
|||
} |
@ -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 |
|||
|
|||
} |
|||
|
|||
} |
@ -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() { |
|||
} |
@ -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, |
|||
} |
@ -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) |
|||
} |
@ -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 |
|||
} |
@ -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") |
|||
} |
|||
|
|||
} |
@ -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 |
|||
} |
@ -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) |
|||
} |
|||
|
|||
} |
@ -0,0 +1,8 @@ |
|||
package globals |
|||
|
|||
import "testing" |
|||
|
|||
|
|||
func Test_Part1(t *testing.T){ |
|||
|
|||
} |
@ -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) |
|||
} |
@ -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. |
@ -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. |
@ -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. |
@ -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. |
@ -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. |
@ -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. |
@ -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. |
@ -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. |
|||
|
@ -0,0 +1,2 @@ |
|||
This folder contains licenses of other libraries used within the project |
|||
|
@ -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. |
@ -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. |
@ -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. |
@ -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. |
@ -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) |
|||
|
|||
} |
@ -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
|
|||
|
|||
} |
|||
} |
|||
|
|||
} |
@ -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() |
|||
} |
@ -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() |
|||
} |
@ -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() |
|||
} |
@ -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
|
|||
|
|||
} |
@ -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) |
|||
|
|||
} |
@ -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") |
|||
} |
|||
|
|||
} |
@ -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) |
|||
} |
|||
|
|||
} |
@ -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() |
|||
} |
@ -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() |
|||
} |
|||
*/ |
@ -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 |
|||
} |
@ -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() |
|||
|
|||
} |
@ -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
|
|||
} |
@ -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 |
@ -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) { |
|||
|
|||
} |
@ -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)) |
|||
} |
|||
} |
@ -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 |
|||
|
|||
|
|||
|
|||
|
@ -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 |
|||
} |
@ -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) |
|||
} |
|||
|
|||
} |