Compare commits

...

108 Commits

Author SHA1 Message Date
MoneroOcean b3e74bffda Added SAL support 2024-07-20 04:41:43 +03:00
MoneroOcean c696b5b43f Added SAL support 2024-07-20 04:41:33 +03:00
MoneroOcean 084ea7fd77 Added SAL support 2024-07-20 01:24:23 +03:00
MoneroOcean e7a1430242 Added SAL support 2024-07-19 21:44:05 +03:00
MoneroOcean a138d12221 Added SAL support 2024-07-19 21:06:11 +03:00
MoneroOcean dd63cb209d Added SAL support 2024-07-19 21:01:48 +03:00
MoneroOcean 161ec204e6 Added SAL support 2024-07-19 20:50:23 +03:00
MoneroOcean 2104ac35d5 Added SAL support 2024-07-19 20:40:05 +03:00
MoneroOcean 30a1cf7813 Added SAL support 2024-07-19 20:29:42 +03:00
MoneroOcean 32dfbcf8ea Added SAL support 2024-07-19 19:38:52 +03:00
MoneroOcean b8d2ba017e Added SAL support 2024-07-19 19:31:43 +03:00
MoneroOcean b18445f6e7 Added SAL support 2024-07-19 19:31:10 +03:00
MoneroOcean bea129bb73 Added SAL support 2024-07-19 10:14:23 +03:00
MoneroOcean 690e900011 Added SAL support 2024-07-19 07:14:51 +03:00
Some Random Crypto Guy 516511da69 repaired regression, added back in VARIANT_TAG, fixed tree hashing calculation, sorted input and output types 2024-07-19 07:13:07 +03:00
Some Random Crypto Guy 0aadf3db51 changed to use of Salvium input and output types - doh! 2024-07-19 07:12:41 +03:00
Some Random Crypto Guy be8c2e9c8f fixed build issues 2024-07-19 07:12:33 +03:00
Some Random Crypto Guy 66854eb683 initial update to add Salvium support 2024-07-19 07:12:19 +03:00
MoneroOcean a9f2317ffa Added ZEPH update support 2024-07-14 02:11:37 +03:00
ZephyrProtocol eea6d166b2 Update pricing record serialization for Zephyr version v1.2.0 Artemis 2024-07-14 02:10:55 +03:00
MoneroOcean 6bb5e00c17 Updated RTM utils 2024-06-04 23:35:23 +03:00
MoneroOcean 7bbb0cf80e Fixed RTM blob contruction 2024-06-02 23:58:07 +03:00
MoneroOcean 16f9569d0c Fixed KCN blob contruction in all cases 2024-06-02 22:19:33 +03:00
MoneroOcean e6143eb9c0 Fixed KCN blob contruction in all cases 2024-06-02 20:40:12 +03:00
MoneroOcean 1b2f6af8f8 Fixed KCN blob contruction in all cases 2024-06-02 20:23:32 +03:00
MoneroOcean eb61aefe8b Fixed KCN blob contruction in all cases 2024-06-02 02:58:03 +03:00
MoneroOcean 1d0ada1c82 Fixed KCN blob contruction in all cases 2024-06-01 20:31:28 +03:00
MoneroOcean 0bb1785826 Fixed KCN reward 2024-06-01 19:20:26 +03:00
MoneroOcean 1c48ad7e46 Adjusted KCN reward 2024-06-01 16:57:50 +03:00
MoneroOcean f0c26e6d5b Adjusted KCN reward 2024-06-01 16:57:24 +03:00
MoneroOcean 7a1d7271a1 RTM fix 2024-06-01 10:22:55 +03:00
MoneroOcean 30e051fa46 KCN fix 2024-06-01 09:35:38 +03:00
MoneroOcean 4ccd4fdca7 KCN fix 2024-06-01 08:56:41 +03:00
MoneroOcean 71bda2c8bb KCN fix 2024-05-31 23:29:51 +03:00
MoneroOcean 261c518133 KCN fix 2024-05-31 20:19:14 +03:00
MoneroOcean 2a1741ac52 KCN fix 2024-05-31 20:16:33 +03:00
MoneroOcean 1f59698bda Adds kcn dev reward to bt 2024-05-31 19:39:05 +03:00
MoneroOcean 3238964d2a Adds kcn dev reward to bt 2024-05-31 19:29:34 +03:00
MoneroOcean 85260f0281 Adds kcn dev reward to bt 2024-05-31 19:29:21 +03:00
MoneroOcean 8c944e469c Adds kcn dev reward to bt 2024-05-31 18:17:08 +03:00
MoneroOcean dae35d962a Added KCN coin support 2024-05-28 00:24:26 +03:00
MoneroOcean 89fc132363 Updated for Ubuntu 24.04 2024-05-05 18:57:31 +03:00
MoneroOcean af3cc3e902 Removed unused field 2024-05-05 18:53:34 +03:00
MoneroOcean 4d8a30042e Updated for Ubuntu 24.04 2024-05-03 20:13:04 +03:00
MoneroOcean a760b46501 Updated for Ubuntu 24.04 2024-05-03 20:12:39 +03:00
MoneroOcean 9ef2d782c1 Updated for Ubuntu 24.04 2024-05-03 19:39:19 +03:00
MoneroOcean 278654276e Added ZEPH fork support 2023-10-01 14:24:39 +00:00
MoneroOcean 5ca2284583 XLA fork support 2023-09-26 16:42:28 +00:00
MoneroOcean 100f6cb2d5 Added CLORE support 2023-09-06 00:54:46 +00:00
MoneroOcean 59edc8c114 Added next fork Haven support 2023-08-28 03:37:06 +00:00
MoneroOcean 82693cbe57 Better ZEPH support 2023-06-25 03:59:43 +00:00
MoneroOcean 1547e8d121 Better ZEPH support 2023-06-25 03:57:31 +00:00
MoneroOcean 22c123ff32 Better ZEPH support 2023-06-25 03:41:27 +00:00
MoneroOcean 8e026a0684 ZEPH support 2023-06-25 00:27:42 +00:00
MoneroOcean 7fa30e45cc ZEPH support 2023-06-25 00:25:37 +00:00
MoneroOcean 5747436dd3 ZEPH support 2023-06-25 00:23:28 +00:00
MoneroOcean 62b4dc68e7 ZEPH support 2023-06-24 21:56:42 +00:00
MoneroOcean 6731280da4 ZEPH support 2023-06-24 21:52:23 +00:00
MoneroOcean d1281ee79f ZEPH support 2023-06-24 21:49:42 +00:00
MoneroOcean 1182f790c5 ZEPH support 2023-06-24 21:48:29 +00:00
MoneroOcean d3a0336291 ZEPH support 2023-06-24 21:45:44 +00:00
MoneroOcean 0c18e18560 ZEPH support 2023-06-24 21:44:04 +00:00
MoneroOcean 23c3520d13 ZEPH support 2023-06-24 21:42:23 +00:00
MoneroOcean fa62a68afa XHV fix 2023-01-09 15:54:33 +00:00
MoneroOcean 1afe313308 XHV fix 2023-01-09 15:52:03 +00:00
MoneroOcean 516160568f Haven 3.0 support 2022-12-27 05:44:02 +00:00
MoneroOcean 157948193d Haven 3.0 support 2022-12-27 05:43:46 +00:00
MoneroOcean b627d4d36e Haven 3.0 support 2022-12-27 05:41:02 +00:00
MoneroOcean eb4d1a5225 Removed not needed stuff 2022-08-05 17:56:53 +00:00
MoneroOcean bdefbffd43 Monero v15 (view tags) hard-fork support 2022-08-05 15:30:55 +00:00
MoneroOcean 125a1ab637 Draft implementation 2022-08-04 08:00:13 +00:00
MoneroOcean f97a87a97a Bug fix 2022-08-04 06:16:29 +00:00
MoneroOcean 3b5542e94a Removed stuff' 2022-08-04 06:15:11 +00:00
MoneroOcean 9bc777cf7d Fixed RTM tx parsing 2022-03-11 00:52:39 +00:00
MoneroOcean 66fd45fd11 Fixed RTM tx parsing 2022-03-11 00:47:57 +00:00
MoneroOcean 6c09ba09fd Fixed RTM tx parsing 2022-03-11 00:47:54 +00:00
MoneroOcean 90aac49c77 Fixed RTM tx parsing' 2022-03-11 00:23:54 +00:00
MoneroOcean e6ebe4d355 Skip unsupported RTM tx 2022-03-08 23:41:36 +00:00
MoneroOcean 0e72d68d94 Updated to support XHV v6 TXs 2022-02-04 15:33:14 +00:00
MoneroOcean 170d0c0ec9 Added RTM block ID function 2021-12-09 01:12:27 +00:00
MoneroOcean 5548c6de53 Blob fix 2021-12-01 20:41:07 +00:00
MoneroOcean 56ea8f3792 Blob fix 2021-12-01 20:40:54 +00:00
MoneroOcean 4d15a85e70 GR support 2021-11-30 21:45:33 +00:00
MoneroOcean 494e6aa059 GR support 2021-11-30 16:27:45 +00:00
MoneroOcean 5d33cf7e90 GR support 2021-11-30 16:22:57 +00:00
MoneroOcean f4ff9bc8cb GR support 2021-11-30 16:12:59 +00:00
MoneroOcean acc0039b14 GR support 2021-11-30 07:07:19 +00:00
MoneroOcean 57d2f65800 GR support 2021-11-29 23:53:10 +00:00
MoneroOcean be85a60436 GR support 2021-11-28 20:28:29 +00:00
MoneroOcean 1f0941edd8 GR support 2021-11-28 20:22:49 +00:00
MoneroOcean 626fd45757 Haven 2.0 support 2021-11-18 15:41:29 +00:00
MoneroOcean dd7fc1aa05 Better source sync 2021-11-18 15:41:29 +00:00
Neil Coggins f5ccc22d2c updated to support Haven 2.0 (untested) 2021-11-18 15:41:29 +00:00
MoneroOcean 22f9cf0bca Merge pull request #17 from Ghost-ai-cpu/master
Boost compatibility fix
2021-08-25 15:50:24 -07:00
Ghost-ai-cpu 8f3052679a Merge pull request #1 from Ghost-ai-cpu/patch-1
Patch 1
2021-08-23 11:11:27 +05:30
Ghost-ai-cpu 0d0da4af7b Update portable_storage.h 2021-08-23 11:09:54 +05:30
MoneroOcean 3da08f4e74 Fixed tx version 2021-07-21 02:13:19 +00:00
MoneroOcean d9778fd1ef Fixed tx version 2021-07-21 02:13:01 +00:00
MoneroOcean f212be897e XHV fix 2021-07-20 20:27:23 +00:00
Neil Coggins d1a0cf9439 updated pricing record to support timestamps 2021-07-20 20:26:52 +00:00
MoneroOcean b402ceb37f Updated to new nan version 2021-07-20 17:23:14 +00:00
MoneroOcean f31a2751ab Added autolykos2 support 2021-07-20 04:27:04 +00:00
MoneroOcean af5a7c2186 Added new XHV fork support 2021-04-20 19:41:22 +00:00
Neil Coggins 916e440fb6 added support for xAssets 2021-04-09 13:28:04 +01:00
Neil Coggins bd305271cd added support for xAssets 2021-04-09 13:27:50 +01:00
Neil Coggins e86f0a8afd added support for xAssets 2021-04-09 13:27:12 +01:00
MoneroOcean 80b9b2be12 Eth draft support 2021-01-22 21:14:28 +00:00
MoneroOcean d405a871a4 Eth draft support 2021-01-22 21:07:24 +00:00
35 changed files with 3204 additions and 1162 deletions
+2 -1
View File
@@ -4,7 +4,8 @@ Node-CryptoForkNote-Util with Merged Mining support
Installing locally and testing
-----
```
npm install https://github.com/MoneroOcean/node-cryptoforknote-util
JOBS=$(nproc) npm install https://github.com/MoneroOcean/node-cryptoforknote-util
node_modules/cryptoforknote-util/tests/run.sh
```
Dependencies
+3 -1
View File
@@ -6,6 +6,8 @@
"src/main.cc",
"src/cryptonote_core/cryptonote_format_utils.cpp",
"src/offshore/pricing_record.cpp",
"src/zephyr_oracle/pricing_record.cpp",
"src/salvium_oracle/pricing_record.cpp",
"src/crypto/tree-hash.c",
"src/crypto/crypto.cpp",
"src/crypto/crypto-ops.c",
@@ -30,7 +32,7 @@
"-fno-exceptions -std=gnu11 -march=native -fPIC -DNDEBUG -Ofast -funroll-loops -fvariable-expansion-in-unroller -ftree-loop-if-convert-stores -fmerge-all-constants -fbranch-target-load-optimize2"
],
"cflags_cc": [
"-fexceptions -frtti -std=gnu++11 -march=native -fPIC -DNDEBUG -Ofast -s -funroll-loops -fvariable-expansion-in-unroller -ftree-loop-if-convert-stores -fmerge-all-constants -fbranch-target-load-optimize2"
"-fexceptions -frtti -std=c++17 -march=native -fPIC -DNDEBUG -Ofast -s -funroll-loops -fvariable-expansion-in-unroller -ftree-loop-if-convert-stores -fmerge-all-constants -fbranch-target-load-optimize2"
],
"xcode_settings": {
"OTHER_CFLAGS": [ "-fexceptions -frtti" ]
+105 -34
View File
@@ -7,6 +7,8 @@ const varuint = require('varuint-bitcoin');
const crypto = require('crypto');
const fastMerkleRoot = require('merkle-lib/fastRoot');
const rtm = require('cryptoforknote-util/rtm');
function scriptCompile(addrHash) {
return bitcoin.script.compile([
bitcoin.opcodes.OP_DUP,
@@ -18,7 +20,7 @@ function scriptCompile(addrHash) {
}
function reverseBuffer(buff) {
let reversed = new Buffer(buff.length);
let reversed = Buffer.alloc(buff.length);
for (var i = buff.length - 1; i >= 0; i--) reversed[buff.length - i - 1] = buff[i];
return reversed;
}
@@ -44,10 +46,28 @@ function hash256(buffer) {
return sha256(sha256(buffer));
};
function getMerkleRoot(transactions) {
if (transactions.length === 0) return new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex')
const forWitness = txesHaveWitnessCommit(transactions);
const hashes = transactions.map(transaction => transaction.getHash(forWitness));
function sha256_3(buffer) {
return crypto.createHash('sha3-256').update(buffer).digest();
};
function hash256_3(buffer) {
return sha256_3(sha256_3(buffer));
};
function transaction_hash(transaction, forWitness) {
if (forWitness && transaction.isCoinbase()) return Buffer.alloc(32, 0);
return hash256(transaction.__toBuffer(undefined, undefined, forWitness));
}
function transaction_hash3(transaction, forWitness) {
if (forWitness && transaction.isCoinbase()) return Buffer.alloc(32, 0);
return hash256_3(transaction.__toBuffer(undefined, undefined, forWitness));
}
function getMerkleRoot(transactions, transaction_hash_func, detectWitness) {
if (transactions.length === 0) return Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex')
const forWitness = detectWitness ? txesHaveWitnessCommit(transactions) : false;
const hashes = transactions.map(transaction => transaction_hash_func(transaction, forWitness));
const rootHash = fastMerkleRoot(hashes, hash256);
return forWitness ? hash256(Buffer.concat([rootHash, transactions[0].ins[0].witness[0]])) : rootHash;
}
@@ -55,12 +75,12 @@ function getMerkleRoot(transactions) {
let last_epoch_number;
let last_seed_hash;
module.exports.baseRavenDiff = function() {
return parseInt('0x00000000ff000000000000000000000000000000000000000000000000000000');
module.exports.baseDiff = function() {
return bignum('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 16);
};
module.exports.baseEthDiff = function() {
return parseInt('0x00000000ffff0000000000000000000000000000000000000000000000000000');
module.exports.baseRavenDiff = function() {
return parseInt('0x00000000ff000000000000000000000000000000000000000000000000000000');
};
module.exports.RavenBlockTemplate = function(rpcData, poolAddress) {
@@ -75,27 +95,32 @@ module.exports.RavenBlockTemplate = function(rpcData, poolAddress) {
bytesHeight = Math.ceil((rpcData.height << 1).toString(2).length / 8);
const lengthDiff = blockHeightSerial.length/2 - bytesHeight;
for (let i = 0; i < lengthDiff; i++) blockHeightSerial = blockHeightSerial + '00';
const serializedBlockHeight = new Buffer.concat([
new Buffer('0' + bytesHeight, 'hex'),
reverseBuffer(new Buffer(blockHeightSerial, 'hex')),
new Buffer('00', 'hex') // OP_0
const serializedBlockHeight = Buffer.concat([
Buffer.from('0' + bytesHeight, 'hex'),
reverseBuffer(Buffer.from(blockHeightSerial, 'hex')),
Buffer.from('00', 'hex') // OP_0
]);
txCoinbase.addInput(
// will be used for our reserved_offset extra_nonce
new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex'),
Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'),
0xFFFFFFFF, 0xFFFFFFFF,
new Buffer.concat([serializedBlockHeight, Buffer('CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC', 'hex')]) // 17 bytes
Buffer.concat([serializedBlockHeight, Buffer.alloc(17, 0xCC)]) // 17 bytes
);
txCoinbase.addOutput(scriptCompile(poolAddrHash), Math.floor(rpcData.coinbasevalue));
// For CLORE
if (rpcData.CommunityAutonomousAddress && rpcData.CommunityAutonomousValue) {
txCoinbase.addOutput(scriptCompile(bitcoin.address.fromBase58Check(rpcData.CommunityAutonomousAddress).hash), Math.floor(rpcData.CommunityAutonomousValue));
}
if (rpcData.default_witness_commitment) {
txCoinbase.addOutput(new Buffer(rpcData.default_witness_commitment, 'hex'), 0);
txCoinbase.addOutput(Buffer.from(rpcData.default_witness_commitment, 'hex'), 0);
}
}
let header = new Buffer(80);
let header = Buffer.alloc(80);
{ let position = 0;
header.writeUInt32BE(rpcData.height, position, 4); // height 42-46
header.write(rpcData.bits, position += 4, 4, 'hex'); // bits 47-50
@@ -106,17 +131,17 @@ module.exports.RavenBlockTemplate = function(rpcData, poolAddress) {
header = reverseBuffer(header);
}
let blob = new Buffer.concat([
let blob = Buffer.concat([
header, // 80 bytes
new Buffer('AAAAAAAAAAAAAAAA', 'hex'), // 8 bytes
new Buffer('BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB', 'hex'), // 32 bytes
varuint.encode(rpcData.transactions.length + 1, new Buffer(varuint.encodingLength(rpcData.transactions.length + 1)), 0)
Buffer.from('AAAAAAAAAAAAAAAA', 'hex'), // 8 bytes
Buffer.from('BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB', 'hex'), // 32 bytes
varuint.encode(rpcData.transactions.length + 1, Buffer.alloc(varuint.encodingLength(rpcData.transactions.length + 1)), 0)
]);
const offset1 = blob.length;
blob = new Buffer.concat([ blob, new Buffer(txCoinbase.toHex(), 'hex') ]);
blob = Buffer.concat([ blob, Buffer.from(txCoinbase.toHex(), 'hex') ]);
rpcData.transactions.forEach(function (value) {
blob = new Buffer.concat([ blob, new Buffer(value.data, 'hex') ]);
blob = Buffer.concat([ blob, Buffer.from(value.data, 'hex') ]);
});
const EPOCH_LENGTH = 7500;
@@ -126,7 +151,7 @@ module.exports.RavenBlockTemplate = function(rpcData, poolAddress) {
if (last_epoch_number && last_epoch_number + 1 === epoch_number) {
last_seed_hash = sha3.update(last_seed_hash).digest();
} else {
last_seed_hash = new Buffer(32, 0);
last_seed_hash = Buffer.alloc(32, 0);
for (let i = 0; i < epoch_number; i++) {
last_seed_hash = sha3.update(last_seed_hash).digest();
sha3.reset();
@@ -150,27 +175,34 @@ module.exports.RavenBlockTemplate = function(rpcData, poolAddress) {
};
};
function update_merkle_root_hash(blob_in, blob_out) {
let offset = 80 + 8 + 32;
function update_merkle_root_hash(offset, payload, blob_in, blob_out, transaction_hash_func, detectWitness) {
const nTransactions = varuint.decode(blob_in, offset);
offset += varuint.decode.bytes;
let transactions = [];
for (let i = 0; i < nTransactions; ++i) {
const tx = bitcoin.Transaction.fromBuffer(blob_in.slice(offset), true);
const tx = bitcoin.Transaction.fromBuffer(blob_in.slice(offset), true, payload && i == 0);
transactions.push(tx);
offset += tx.byteLength();
}
getMerkleRoot(transactions).copy(blob_out, 4 + 32);
getMerkleRoot(transactions, transaction_hash_func, detectWitness).copy(blob_out, 4 + 32);
};
module.exports.blockHashBuff = function(blobBuffer) {
return reverseBuffer(hash256(blobBuffer));
};
module.exports.blockHashBuff3 = function(blobBuffer) {
return reverseBuffer(hash256_3(blobBuffer));
};
module.exports.convertRavenBlob = function(blobBuffer) {
let header = blobBuffer.slice(0, 80);
update_merkle_root_hash(blobBuffer, header);
return reverseBuffer(hash256(header));
update_merkle_root_hash(80 + 8 + 32, false, blobBuffer, header, transaction_hash, true);
return module.exports.blockHashBuff(header);
};
module.exports.constructNewRavenBlob = function(blockTemplate, nonceBuff, mixhashBuff) {
update_merkle_root_hash(blockTemplate, blockTemplate);
update_merkle_root_hash(80 + 8 + 32, false, blockTemplate, blockTemplate, transaction_hash, true);
nonceBuff.copy (blockTemplate, 80, 0, 8);
mixhashBuff.copy(blockTemplate, 88, 0, 32);
return blockTemplate;
@@ -182,10 +214,49 @@ module.exports.constructNewDeroBlob = function(blockTemplate, nonceBuff) {
};
module.exports.EthBlockTemplate = function(rpcData) {
const difficulty = parseFloat((module.exports.baseEthDiff() / bignum(rpcData[2].substr(2), 16).toNumber()).toFixed(19));
const difficulty = module.exports.baseDiff().div(bignum(rpcData[2].substr(2), 16)).toNumber();
return {
hash: rpcData[0].substr(2),
seed_hash: rpcData[1].substr(2),
difficulty: difficulty
difficulty: difficulty,
height: parseInt(rpcData[3])
};
};
};
module.exports.ErgBlockTemplate = function(rpcData) {
const difficulty = module.exports.baseDiff().div(bignum(rpcData.b)).toNumber();
return {
hash: rpcData.msg,
hash2: rpcData.pk,
difficulty: difficulty,
height: parseInt(rpcData.h)
};
};
module.exports.RtmBlockTemplate = function(rpcData, poolAddress) {
return rtm.RtmBlockTemplate(rpcData, poolAddress);
};
module.exports.convertRtmBlob = function(blobBuffer) {
let header = blobBuffer.slice(0, 80);
update_merkle_root_hash(80, true, blobBuffer, header, transaction_hash, true);
return header;
};
module.exports.convertKcnBlob = function(blobBuffer) {
let header = blobBuffer.slice(0, 80);
update_merkle_root_hash(80, false, blobBuffer, header, transaction_hash3, false);
return header;
};
module.exports.constructNewRtmBlob = function(blockTemplate, nonceBuff) {
update_merkle_root_hash(80, true, blockTemplate, blockTemplate, transaction_hash, true);
nonceBuff.copy(blockTemplate, 76, 0, 4);
return blockTemplate;
};
module.exports.constructNewKcnBlob = function(blockTemplate, nonceBuff) {
update_merkle_root_hash(80, false, blockTemplate, blockTemplate, transaction_hash3, false);
nonceBuff.copy(blockTemplate, 76, 0, 4);
return blockTemplate;
};
+11 -9
View File
@@ -1,23 +1,25 @@
{
"name": "cryptoforknote-util",
"version": "9.1.2",
"main": "cryptoforknote-util",
"version": "15.5.11",
"author": {
"name": "LucasJones",
"email": "lucasjonesdev@hotmail.co.uk"
},
"repository": {
"type": "git",
"url": "https://github.com/MoneroOcean/node-cryptoforknote-util.git"
"url": "https://github.com/haven-protocol-org/node-cryptoforknote-util.git"
},
"dependencies": {
"promise": "*",
"base58-native": "*",
"bech32": "*",
"bignum": "^0.13.1",
"bindings": "*",
"nan": "^2.0.0",
"bignum": "^0.13.1",
"sha3": "*",
"varuint-bitcoin": "^1.0.4",
"bitcoinjs-lib": "git+https://github.com/bitcoinjs/bitcoinjs-lib.git#533d6c2e6d0aa4111f7948b1c12003cf6ef83137"
"bitcoinjs-lib": "git+https://github.com/MoneroOcean/bitcoinjs-lib.git",
"merkle-lib": "^2.0.10",
"nan": "^2.20.0",
"promise": "*",
"sha3": "*",
"varuint-bitcoin": "^1.0.4"
},
"keywords": [
"cryptonight",
+308
View File
@@ -0,0 +1,308 @@
const bignum = require('bignum');
const base58 = require('base58-native');
const bech32 = require('bech32');
const bitcoin = require('bitcoinjs-lib');
const diff1 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
function reverseBuffer(buff) {
let reversed = Buffer.alloc(buff.length);
for (let i = buff.length - 1; i >= 0; i--) reversed[buff.length - i - 1] = buff[i];
return reversed;
}
function reverseByteOrder(buff) {
for (let i = 0; i < 8; i++) buff.writeUInt32LE(buff.readUInt32BE(i * 4), i * 4);
return reverseBuffer(buff);
}
function packInt32LE(num) {
let buff = Buffer.alloc(4);
buff.writeInt32LE(num, 0);
return buff;
}
function packInt32BE(num) {
let buff = Buffer.alloc(4);
buff.writeInt32BE(num, 0);
return buff;
}
function packUInt16LE(num) {
let buff = Buffer.alloc(2);
buff.writeUInt16LE(num, 0);
return buff;
}
function packUInt32LE(num) {
let buff = Buffer.alloc(4);
buff.writeUInt32LE(num, 0);
return buff;
}
function packUInt32BE(num) {
let buff = Buffer.alloc(4);
buff.writeUInt32BE(num, 0);
return buff;
}
function packInt64LE(num){
let buff = Buffer.alloc(8);
buff.writeUInt32LE(num % Math.pow(2, 32), 0);
buff.writeUInt32LE(Math.floor(num / Math.pow(2, 32)), 4);
return buff;
}
// Defined in bitcoin protocol here:
// https://en.bitcoin.it/wiki/Protocol_specification#Variable_length_integer
function varIntBuffer(n) {
if (n < 0xfd) {
return Buffer.from([n]);
} else if (n <= 0xffff) {
let buff = Buffer.alloc(3);
buff[0] = 0xfd;
buff.writeUInt16LE(n, 1);
return buff;
} else if (n <= 0xffffffff) {
let buff = Buffer.alloc(5);
buff[0] = 0xfe;
buff.writeUInt32LE(n, 1);
return buff;
} else{
let buff = Buffer.alloc(9);
buff[0] = 0xff;
packUInt16LE(n).copy(buff, 1);
return buff;
}
}
// "serialized CScript" formatting as defined here:
// https://github.com/bitcoin/bips/blob/master/bip-0034.mediawiki#specification
// Used to format height and date when putting into script signature:
// https://en.bitcoin.it/wiki/Script
function serializeNumber(n) {
// New version from TheSeven
if (n >= 1 && n <= 16) return Buffer.from([0x50 + n]);
var l = 1;
var buff = Buffer.alloc(9);
while (n > 0x7f) {
buff.writeUInt8(n & 0xff, l++);
n >>= 8;
}
buff.writeUInt8(l, 0);
buff.writeUInt8(n, l++);
return buff.slice(0, l);
}
// Used for serializing strings used in script signature
function serializeString(s) {
if (s.length < 253) {
return Buffer.concat([ Buffer.from([s.length]), Buffer.from(s) ]);
} else if (s.length < 0x10000) {
return Buffer.concat([ Buffer.from([253]), packUInt16LE(s.length), Buffer.from(s) ]);
} else if (s.length < 0x100000000) {
return Buffer.concat([ Buffer.from([254]), packUInt32LE(s.length), Buffer.from(s) ]);
} else {
return Buffer.concat([ Buffer.from([255]), packUInt16LE(s.length), Buffer.from(s) ]);
}
}
// An exact copy of python's range feature. Written by Tadeck:
// http://stackoverflow.com/a/8273091
function range(start, stop, step) {
if (typeof stop === 'undefined') {
stop = start;
start = 0;
}
if (typeof step === 'undefined') {
step = 1;
}
if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) {
return [];
}
let result = [];
for (let i = start; step > 0 ? i < stop : i > stop; i += step) {
result.push(i);
}
return result;
}
function uint256BufferFromHash(hex) {
let fromHex = Buffer.from(hex, 'hex');
if (fromHex.length != 32) {
let empty = Buffer.alloc(32);
empty.fill(0);
fromHex.copy(empty);
fromHex = empty;
}
return reverseBuffer(fromHex);
}
function getTransactionBuffers(txs) {
let txHashes = txs.map(function(tx) {
if (tx.txid !== undefined) {
return uint256BufferFromHash(tx.txid);
}
return uint256BufferFromHash(tx.hash);
});
return [null].concat(txHashes);
}
function addressToScript(addr) {
let decoded;
try {
decoded = base58.decode(addr);
} catch(err) {}
if (!decoded || decoded.length != 25) {
const decoded2 = Buffer.from(bech32.bech32.fromWords(bech32.bech32.decode(addr).words.slice(1)));
if (decoded2.length != 20) throw new Error('Invalid address ' + addr);
return Buffer.concat([Buffer.from([0x0, 0x14]), decoded2]);
}
const pubkey = decoded.slice(1, -4);
return Buffer.concat([Buffer.from([0x76, 0xa9, 0x14]), pubkey, Buffer.from([0x88, 0xac])]);
}
function createTransactionOutput(amount, payee, rewardToPool, reward, txOutputBuffers, payeeScript) {
const payeeReward = amount;
if (!payeeScript) payeeScript = addressToScript(payee);
txOutputBuffers.push(Buffer.concat([
packInt64LE(payeeReward),
varIntBuffer(payeeScript.length),
payeeScript
]));
return { reward: reward - amount, rewardToPool: rewardToPool - amount };
}
function generateTransactionOutputs(rpcData, poolAddress) {
let reward = rpcData.coinbasevalue + (rpcData.coinbasedevreward ? rpcData.coinbasedevreward.value : 0);
let rewardToPool = reward;
let txOutputBuffers = [];
if (rpcData.coinbasedevreward) {
const rewards = createTransactionOutput(rpcData.coinbasedevreward.value, null, rewardToPool, reward, txOutputBuffers, Buffer.from(rpcData.coinbasedevreward.scriptpubkey, 'hex'));
reward = rewards.reward;
rewardToPool = rewards.rewardToPool;
}
if (rpcData.smartnode) {
if (rpcData.smartnode.payee) {
const rewards = createTransactionOutput(rpcData.smartnode.amount, rpcData.smartnode.payee, rewardToPool, reward, txOutputBuffers);
reward = rewards.reward;
rewardToPool = rewards.rewardToPool;
} else if (Array.isArray(rpcData.smartnode)) {
for (let i in rpcData.smartnode) {
const rewards = createTransactionOutput(rpcData.smartnode[i].amount, rpcData.smartnode[i].payee, rewardToPool, reward, txOutputBuffers);
reward = rewards.reward;
rewardToPool = rewards.rewardToPool;
}
}
}
if (rpcData.superblock) {
for (let i in rpcData.superblock) {
const rewards = createTransactionOutput(rpcData.superblock[i].amount, rpcData.superblock[i].payee, rewardToPool, reward, txOutputBuffers);
reward = rewards.reward;
rewardToPool = rewards.rewardToPool;
}
}
if (rpcData.founder_payments_started && rpcData.founder) {
const founderReward = rpcData.founder.amount || 0;
const rewards = createTransactionOutput(founderReward, rpcData.founder.payee, rewardToPool, reward, txOutputBuffers);
reward = rewards.reward;
rewardToPool = rewards.rewardToPool;
}
createTransactionOutput(rewardToPool, null, rewardToPool, reward, txOutputBuffers, Buffer.from(addressToScript(poolAddress), "hex"));
if (rpcData.default_witness_commitment) {
createTransactionOutput(0, null, rewardToPool, reward, txOutputBuffers, Buffer.from(rpcData.default_witness_commitment, 'hex'));
txOutputBuffers.push(Buffer.concat([
varIntBuffer(1),
varIntBuffer(32),
Buffer.alloc(32, 0)
]));
}
return Buffer.concat([ varIntBuffer(rpcData.default_witness_commitment ? txOutputBuffers.length - 1 : txOutputBuffers.length), Buffer.concat(txOutputBuffers)]);
}
module.exports.RtmBlockTemplate = function(rpcData, poolAddress) {
const extraNoncePlaceholderLength = 17;
const coinbaseVersion = rpcData.coinbasedevreward ? Buffer.concat([packUInt16LE(1), packUInt16LE(0)]) : Buffer.concat([packUInt16LE(3), packUInt16LE(5)]);
const scriptSigPart1 = Buffer.concat([
serializeNumber(rpcData.height),
Buffer.from(rpcData.coinbaseaux.flags ? rpcData.coinbaseaux.flags : "", 'hex'),
serializeNumber(Date.now() / 1000 | 0),
Buffer.from([extraNoncePlaceholderLength])
]);
const scriptSigPart2 = serializeString('/nodeStratum/');
const is_witness = rpcData.default_witness_commitment !== undefined;
const blob1 = Buffer.concat([
coinbaseVersion,
// transaction input
Buffer.from(is_witness ? "0001" : "", 'hex'),
varIntBuffer(1), // txInputsCount
uint256BufferFromHash(""), // txInPrevOutHash
packUInt32LE(Math.pow(2, 32) - 1), // txInPrevOutIndex
varIntBuffer(scriptSigPart1.length + extraNoncePlaceholderLength + scriptSigPart2.length),
scriptSigPart1
]);
let blob2 = Buffer.concat([
scriptSigPart2,
packUInt32LE(0), // txInSequence
// end transaction input
// transaction output
generateTransactionOutputs(rpcData, poolAddress, is_witness),
// end transaction ouput
packUInt32LE(0) // txLockTime
]);
if (rpcData.coinbase_payload) {
blob2 = Buffer.concat([
blob2,
varIntBuffer(rpcData.coinbase_payload.length / 2),
Buffer.from(rpcData.coinbase_payload, 'hex')
]);
}
const prev_hash = reverseBuffer(Buffer.from(rpcData.previousblockhash, 'hex')).toString('hex');
const version = packInt32LE(rpcData.version).toString('hex');
const curtime = packUInt32LE(rpcData.curtime).toString('hex');
let bits = Buffer.from(rpcData.bits, 'hex');
bits.writeUInt32LE(bits.readUInt32BE());
let txs = [];
// skip version 1 transaction because they contain some OP_RETURN(0x6A) opcode in the beginning of
// tx input scripts instead of size of script part so not sure how to parse them
// just drop them for now
// example: https://explorer.raptoreum.com/tx/1461d70fa8362b0896e2e9be6312521f2684f22c9b0f9152695f33f67d9f9d3f
rpcData.transactions.forEach(function(tx) {
if (tx.version != 1) {
try {
bitcoin.Transaction.fromBuffer(Buffer.from(tx.data, 'hex'), false, false);
} catch(err) {
console.error("Skip RTM tx due to parse error: " + tx.data);
return; // skip transaction if it is not parsed OK (varint coding seems to be different for RTM)
}
txs.push(tx);
} else {
console.error("Skip RTM v1 tx: " + tx.data);
}
});
const txn = varIntBuffer(txs.length + 1);
return {
difficulty: parseFloat((diff1 / bignum(rpcData.target, 16).toNumber()).toFixed(9)),
height: rpcData.height,
prev_hash: prev_hash,
blocktemplate_blob: version + prev_hash + Buffer.alloc(32, 0).toString('hex') + curtime + bits.toString('hex') + Buffer.alloc(4, 0).toString('hex') +
txn.toString('hex') + blob1.toString('hex') + Buffer.alloc(extraNoncePlaceholderLength, 0xCC).toString('hex') + blob2.toString('hex') +
Buffer.concat(txs.map(function(tx) { return Buffer.from(tx.data, 'hex'); })).toString('hex'),
reserved_offset: 80 + txn.length + blob1.length
}
}
@@ -26,6 +26,7 @@
#pragma once
#include <cstdint>
#include <set>
#include <list>
#include <vector>
@@ -37,6 +37,7 @@
#include "portable_storage_val_converters.h"
#include "span.h"
#include "int-util.h"
#include <boost/mpl/contains.hpp>
namespace epee
{
-271
View File
@@ -50,279 +50,8 @@ namespace crypto {
return &reinterpret_cast<const unsigned char &>(scalar);
}
static inline void random_scalar(ec_scalar &res) {
unsigned char tmp[64];
generate_random_bytes(64, tmp);
sc_reduce(tmp);
memcpy(&res, tmp, 32);
}
static inline void hash_to_scalar(const void *data, size_t length, ec_scalar &res) {
cn_fast_hash(data, length, reinterpret_cast<hash &>(res));
sc_reduce32(&res);
}
void crypto_ops::generate_keys(public_key &pub, secret_key &sec) {
lock_guard<mutex> lock(random_lock);
ge_p3 point;
random_scalar(sec);
ge_scalarmult_base(&point, &sec);
ge_p3_tobytes(&pub, &point);
}
bool crypto_ops::check_key(const public_key &key) {
ge_p3 point;
return ge_frombytes_vartime(&point, &key) == 0;
}
bool crypto_ops::secret_key_to_public_key(const secret_key &sec, public_key &pub) {
ge_p3 point;
if (sc_check(&sec) != 0) {
return false;
}
ge_scalarmult_base(&point, &sec);
ge_p3_tobytes(&pub, &point);
return true;
}
bool crypto_ops::generate_key_derivation(const public_key &key1, const secret_key &key2, key_derivation &derivation) {
ge_p3 point;
ge_p2 point2;
ge_p1p1 point3;
assert(sc_check(&key2) == 0);
if (ge_frombytes_vartime(&point, &key1) != 0) {
return false;
}
ge_scalarmult(&point2, &key2, &point);
ge_mul8(&point3, &point2);
ge_p1p1_to_p2(&point2, &point3);
ge_tobytes(&derivation, &point2);
return true;
}
static void derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res) {
struct {
key_derivation derivation;
char output_index[(sizeof(size_t) * 8 + 6) / 7];
} buf;
char *end = buf.output_index;
buf.derivation = derivation;
tools::write_varint(end, output_index);
assert(end <= buf.output_index + sizeof buf.output_index);
hash_to_scalar(&buf, end - reinterpret_cast<char *>(&buf), res);
}
bool crypto_ops::derive_public_key(const key_derivation &derivation, size_t output_index,
const public_key &base, public_key &derived_key) {
ec_scalar scalar;
ge_p3 point1;
ge_p3 point2;
ge_cached point3;
ge_p1p1 point4;
ge_p2 point5;
if (ge_frombytes_vartime(&point1, &base) != 0) {
return false;
}
derivation_to_scalar(derivation, output_index, scalar);
ge_scalarmult_base(&point2, &scalar);
ge_p3_to_cached(&point3, &point2);
ge_add(&point4, &point1, &point3);
ge_p1p1_to_p2(&point5, &point4);
ge_tobytes(&derived_key, &point5);
return true;
}
void crypto_ops::derive_secret_key(const key_derivation &derivation, size_t output_index,
const secret_key &base, secret_key &derived_key) {
ec_scalar scalar;
assert(sc_check(&base) == 0);
derivation_to_scalar(derivation, output_index, scalar);
sc_add(&derived_key, &base, &scalar);
}
struct s_comm {
hash h;
ec_point key;
ec_point comm;
};
void crypto_ops::generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig) {
lock_guard<mutex> lock(random_lock);
ge_p3 tmp3;
ec_scalar k;
s_comm buf;
#if !defined(NDEBUG)
{
ge_p3 t;
public_key t2;
assert(sc_check(&sec) == 0);
ge_scalarmult_base(&t, &sec);
ge_p3_tobytes(&t2, &t);
assert(pub == t2);
}
#endif
buf.h = prefix_hash;
buf.key = pub;
random_scalar(k);
ge_scalarmult_base(&tmp3, &k);
ge_p3_tobytes(&buf.comm, &tmp3);
hash_to_scalar(&buf, sizeof(s_comm), sig.c);
sc_mulsub(&sig.r, &sig.c, &sec, &k);
}
bool crypto_ops::check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig) {
ge_p2 tmp2;
ge_p3 tmp3;
ec_scalar c;
s_comm buf;
assert(check_key(pub));
buf.h = prefix_hash;
buf.key = pub;
if (ge_frombytes_vartime(&tmp3, &pub) != 0) {
abort();
}
if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0) {
return false;
}
ge_double_scalarmult_base_vartime(&tmp2, &sig.c, &tmp3, &sig.r);
ge_tobytes(&buf.comm, &tmp2);
hash_to_scalar(&buf, sizeof(s_comm), c);
sc_sub(&c, &c, &sig.c);
return sc_isnonzero(&c) == 0;
}
static void hash_to_ec(const public_key &key, ge_p3 &res) {
hash h;
ge_p2 point;
ge_p1p1 point2;
cn_fast_hash(std::addressof(key), sizeof(public_key), h);
ge_fromfe_frombytes_vartime(&point, reinterpret_cast<const unsigned char *>(&h));
ge_mul8(&point2, &point);
ge_p1p1_to_p3(&res, &point2);
}
void crypto_ops::generate_key_image(const public_key &pub, const secret_key &sec, key_image &image) {
ge_p3 point;
ge_p2 point2;
assert(sc_check(&sec) == 0);
hash_to_ec(pub, point);
ge_scalarmult(&point2, &sec, &point);
ge_tobytes(&image, &point2);
}
PUSH_WARNINGS
DISABLE_VS_WARNINGS(4200)
struct rs_comm {
hash h;
struct {
ec_point a, b;
} ab[];
} rcs;
POP_WARNINGS
static inline size_t rs_comm_size(size_t pubs_count) {
return sizeof(rs_comm) + pubs_count * sizeof(rcs.ab[0]);
}
void crypto_ops::generate_ring_signature(const hash &prefix_hash, const key_image &image,
const public_key *const *pubs, size_t pubs_count,
const secret_key &sec, size_t sec_index,
signature *sig) {
lock_guard<mutex> lock(random_lock);
size_t i;
ge_p3 image_unp;
ge_dsmp image_pre;
ec_scalar sum, k, h;
rs_comm *const buf = reinterpret_cast<rs_comm *>(alloca(rs_comm_size(pubs_count)));
assert(sec_index < pubs_count);
#if !defined(NDEBUG)
{
ge_p3 t;
public_key t2;
key_image t3;
assert(sc_check(&sec) == 0);
ge_scalarmult_base(&t, &sec);
ge_p3_tobytes(&t2, &t);
assert(*pubs[sec_index] == t2);
generate_key_image(*pubs[sec_index], sec, t3);
assert(image == t3);
for (i = 0; i < pubs_count; i++) {
assert(check_key(*pubs[i]));
}
}
#endif
if (ge_frombytes_vartime(&image_unp, &image) != 0) {
abort();
}
ge_dsm_precomp(image_pre, &image_unp);
sc_0(&sum);
buf->h = prefix_hash;
for (i = 0; i < pubs_count; i++) {
ge_p2 tmp2;
ge_p3 tmp3;
if (i == sec_index) {
random_scalar(k);
ge_scalarmult_base(&tmp3, &k);
ge_p3_tobytes(&buf->ab[i].a, &tmp3);
hash_to_ec(*pubs[i], tmp3);
ge_scalarmult(&tmp2, &k, &tmp3);
ge_tobytes(&buf->ab[i].b, &tmp2);
} else {
random_scalar(sig[i].c);
random_scalar(sig[i].r);
if (ge_frombytes_vartime(&tmp3, &*pubs[i]) != 0) {
abort();
}
ge_double_scalarmult_base_vartime(&tmp2, &sig[i].c, &tmp3, &sig[i].r);
ge_tobytes(&buf->ab[i].a, &tmp2);
hash_to_ec(*pubs[i], tmp3);
ge_double_scalarmult_precomp_vartime(&tmp2, &sig[i].r, &tmp3, &sig[i].c, image_pre);
ge_tobytes(&buf->ab[i].b, &tmp2);
sc_add(&sum, &sum, &sig[i].c);
}
}
hash_to_scalar(buf, rs_comm_size(pubs_count), h);
sc_sub(&sig[sec_index].c, &h, &sum);
sc_mulsub(&sig[sec_index].r, &sig[sec_index].c, &sec, &k);
}
bool crypto_ops::check_ring_signature(const hash &prefix_hash, const key_image &image,
const public_key *const *pubs, size_t pubs_count,
const signature *sig) {
size_t i;
ge_p3 image_unp;
ge_dsmp image_pre;
ec_scalar sum, h;
rs_comm *const buf = reinterpret_cast<rs_comm *>(alloca(rs_comm_size(pubs_count)));
#if !defined(NDEBUG)
for (i = 0; i < pubs_count; i++) {
assert(check_key(*pubs[i]));
}
#endif
if (ge_frombytes_vartime(&image_unp, &image) != 0) {
return false;
}
ge_dsm_precomp(image_pre, &image_unp);
sc_0(&sum);
buf->h = prefix_hash;
for (i = 0; i < pubs_count; i++) {
ge_p2 tmp2;
ge_p3 tmp3;
if (sc_check(&sig[i].c) != 0 || sc_check(&sig[i].r) != 0) {
return false;
}
if (ge_frombytes_vartime(&tmp3, &*pubs[i]) != 0) {
abort();
}
ge_double_scalarmult_base_vartime(&tmp2, &sig[i].c, &tmp3, &sig[i].r);
ge_tobytes(&buf->ab[i].a, &tmp2);
hash_to_ec(*pubs[i], tmp3);
ge_double_scalarmult_precomp_vartime(&tmp2, &sig[i].r, &tmp3, &sig[i].c, image_pre);
ge_tobytes(&buf->ab[i].b, &tmp2);
sc_add(&sum, &sum, &sig[i].c);
}
hash_to_scalar(buf, rs_comm_size(pubs_count), h);
sc_sub(&h, &h, &sum);
return sc_isnonzero(&h) == 0;
}
}
+6 -99
View File
@@ -49,12 +49,16 @@ namespace crypto {
ec_scalar c, r;
friend class crypto_ops;
};
POD_CLASS view_tag {
char data;
};
#pragma pack(pop)
static_assert(sizeof(ec_point) == 32 && sizeof(ec_scalar) == 32 &&
sizeof(public_key) == 32 && sizeof(secret_key) == 32 &&
sizeof(key_derivation) == 32 && sizeof(key_image) == 32 &&
sizeof(signature) == 64, "Invalid structure size");
sizeof(signature) == 64 && sizeof(view_tag) == 1, "Invalid structure size");
class crypto_ops {
crypto_ops();
@@ -62,32 +66,8 @@ namespace crypto {
void operator=(const crypto_ops &);
~crypto_ops();
static void generate_keys(public_key &, secret_key &);
friend void generate_keys(public_key &, secret_key &);
static bool check_key(const public_key &);
friend bool check_key(const public_key &);
static bool secret_key_to_public_key(const secret_key &, public_key &);
friend bool secret_key_to_public_key(const secret_key &, public_key &);
static bool generate_key_derivation(const public_key &, const secret_key &, key_derivation &);
friend bool generate_key_derivation(const public_key &, const secret_key &, key_derivation &);
static bool derive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &);
friend bool derive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &);
static void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &);
friend void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &);
static void generate_signature(const hash &, const public_key &, const secret_key &, signature &);
friend void generate_signature(const hash &, const public_key &, const secret_key &, signature &);
static bool check_signature(const hash &, const public_key &, const signature &);
friend bool check_signature(const hash &, const public_key &, const signature &);
static void generate_key_image(const public_key &, const secret_key &, key_image &);
friend void generate_key_image(const public_key &, const secret_key &, key_image &);
static void generate_ring_signature(const hash &, const key_image &,
const public_key *const *, std::size_t, const secret_key &, std::size_t, signature *);
friend void generate_ring_signature(const hash &, const key_image &,
const public_key *const *, std::size_t, const secret_key &, std::size_t, signature *);
static bool check_ring_signature(const hash &, const key_image &,
const public_key *const *, std::size_t, const signature *);
friend bool check_ring_signature(const hash &, const key_image &,
const public_key *const *, std::size_t, const signature *);
};
/* Generate a value filled with random bytes.
@@ -100,87 +80,14 @@ namespace crypto {
return res;
}
/* Generate a new key pair
*/
inline void generate_keys(public_key &pub, secret_key &sec) {
crypto_ops::generate_keys(pub, sec);
}
/* Check a public key. Returns true if it is valid, false otherwise.
*/
inline bool check_key(const public_key &key) {
return crypto_ops::check_key(key);
}
/* Checks a private key and computes the corresponding public key.
*/
inline bool secret_key_to_public_key(const secret_key &sec, public_key &pub) {
return crypto_ops::secret_key_to_public_key(sec, pub);
}
/* To generate an ephemeral key used to send money to:
* * The sender generates a new key pair, which becomes the transaction key. The public transaction key is included in "extra" field.
* * Both the sender and the receiver generate key derivation from the transaction key, the receivers' "view" key and the output index.
* * The sender uses key derivation and the receivers' "spend" key to derive an ephemeral public key.
* * The receiver can either derive the public key (to check that the transaction is addressed to him) or the private key (to spend the money).
*/
inline bool generate_key_derivation(const public_key &key1, const secret_key &key2, key_derivation &derivation) {
return crypto_ops::generate_key_derivation(key1, key2, derivation);
}
inline bool derive_public_key(const key_derivation &derivation, std::size_t output_index,
const public_key &base, public_key &derived_key) {
return crypto_ops::derive_public_key(derivation, output_index, base, derived_key);
}
inline void derive_secret_key(const key_derivation &derivation, std::size_t output_index,
const secret_key &base, secret_key &derived_key) {
crypto_ops::derive_secret_key(derivation, output_index, base, derived_key);
}
/* Generation and checking of a standard signature.
*/
inline void generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig) {
crypto_ops::generate_signature(prefix_hash, pub, sec, sig);
}
inline bool check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig) {
return crypto_ops::check_signature(prefix_hash, pub, sig);
}
/* To send money to a key:
* * The sender generates an ephemeral key and includes it in transaction output.
* * To spend the money, the receiver generates a key image from it.
* * Then he selects a bunch of outputs, including the one he spends, and uses them to generate a ring signature.
* To check the signature, it is necessary to collect all the keys that were used to generate it. To detect double spends, it is necessary to check that each key image is used at most once.
*/
inline void generate_key_image(const public_key &pub, const secret_key &sec, key_image &image) {
crypto_ops::generate_key_image(pub, sec, image);
}
inline void generate_ring_signature(const hash &prefix_hash, const key_image &image,
const public_key *const *pubs, std::size_t pubs_count,
const secret_key &sec, std::size_t sec_index,
signature *sig) {
crypto_ops::generate_ring_signature(prefix_hash, image, pubs, pubs_count, sec, sec_index, sig);
}
inline bool check_ring_signature(const hash &prefix_hash, const key_image &image,
const public_key *const *pubs, std::size_t pubs_count,
const signature *sig) {
return crypto_ops::check_ring_signature(prefix_hash, image, pubs, pubs_count, sig);
}
/* Variants with vector<const public_key *> parameters.
*/
inline void generate_ring_signature(const hash &prefix_hash, const key_image &image,
const std::vector<const public_key *> &pubs,
const secret_key &sec, std::size_t sec_index,
signature *sig) {
generate_ring_signature(prefix_hash, image, pubs.data(), pubs.size(), sec, sec_index, sig);
}
inline bool check_ring_signature(const hash &prefix_hash, const key_image &image,
const std::vector<const public_key *> &pubs,
const signature *sig) {
return check_ring_signature(prefix_hash, image, pubs.data(), pubs.size(), sig);
}
}
CRYPTO_MAKE_COMPARABLE(public_key)
CRYPTO_MAKE_HASHABLE(key_image)
CRYPTO_MAKE_COMPARABLE(signature)
CRYPTO_MAKE_COMPARABLE(view_tag)
+22 -2
View File
@@ -1,7 +1,24 @@
#pragma once
#define CURRENT_TRANSACTION_VERSION 1
#define OFFSHORE_TRANSACTION_VERSION 3
#define CURRENT_TRANSACTION_VERSION 1
#define POU_TRANSACTION_VERSION 6
#define COLLATERAL_TRANSACTION_VERSION 7
#define HAVEN_TYPES_TRANSACTION_VERSION 8
#define OFFSHORE_TRANSACTION_VERSION 3
#define HF_VERSION_XASSET_FEES_V2 17
#define HF_VERSION_HAVEN2 18
#define HF_VERSION_USE_COLLATERAL 20
// UNLOCK TIMES
#define TX_V6_OFFSHORE_UNLOCK_BLOCKS 21*720 // 21 day unlock time
#define TX_V6_ONSHORE_UNLOCK_BLOCKS 360 // 12 hour unlock time
#define TX_V7_ONSHORE_UNLOCK_BLOCKS 21*720 // 21 day unlock time
#define TX_V6_XASSET_UNLOCK_BLOCKS 1440 // 2 day unlock time
#define TX_V6_OFFSHORE_UNLOCK_BLOCKS_TESTNET 60 // 2 hour unlock time - FOR TESTING ONLY
#define TX_V6_ONSHORE_UNLOCK_BLOCKS_TESTNET 30 // 1 hour unlock time - FOR TESTING ONLY
#define TX_V6_XASSET_UNLOCK_BLOCKS_TESTNET 60 // 2 hour unlock time - FOR TESTING ONLY
#define PRICING_RECORD_VALID_TIME_DIFF_FROM_BLOCK 120 // seconds
enum BLOB_TYPE {
BLOB_TYPE_CRYPTONOTE = 0,
@@ -17,4 +34,7 @@ enum BLOB_TYPE {
BLOB_TYPE_CRYPTONOTE_TUBE = 10, // TUBE
BLOB_TYPE_CRYPTONOTE_XHV = 11, // Haven
BLOB_TYPE_CRYPTONOTE_XTA = 12, // ITALO
BLOB_TYPE_CRYPTONOTE_ZEPHYR = 13, // ZEPHYR
BLOB_TYPE_CRYPTONOTE_XLA = 14, // XLA
BLOB_TYPE_CRYPTONOTE_SALVIUM= 15, // Salvium
};
+655 -78
View File
@@ -14,10 +14,9 @@
#include "serialization/variant.h"
#include "serialization/vector.h"
#include "serialization/binary_archive.h"
#include "serialization/json_archive.h"
#include "serialization/debug_archive.h"
#include "serialization/crypto.h"
#include "serialization/pricing_record.h"
#include "serialization/zephyr_pricing_record.h"
#include "serialization/keyvalue_serialization.h" // eepe named serialization
#include "string_tools.h"
#include "cryptonote_config.h"
@@ -28,6 +27,8 @@
#include "ringct/rctTypes.h"
#include "cryptonote_protocol/blobdatatype.h"
#include "offshore/pricing_record.h"
#include "zephyr_oracle/pricing_record.h"
#include "salvium_oracle/pricing_record.h"
namespace cryptonote
@@ -48,7 +49,19 @@ namespace cryptonote
typedef std::vector<crypto::signature> ring_signature;
enum salvium_transaction_type
{
UNSET = 0,
MINER = 1,
PROTOCOL = 2,
TRANSFER = 3,
CONVERT = 4,
BURN = 5,
STAKE = 6,
RETURN = 7,
MAX = 7
};
/* outputs */
struct txout_to_script
@@ -67,6 +80,7 @@ namespace cryptonote
crypto::hash hash;
};
// outputs <= HF_VERSION_VIEW_TAGS
struct txout_to_key
{
txout_to_key() { }
@@ -74,6 +88,62 @@ namespace cryptonote
crypto::public_key key;
};
// outputs >= HF_VERSION_VIEW_TAGS
struct txout_to_tagged_key
{
txout_to_tagged_key() { }
txout_to_tagged_key(const crypto::public_key &_key, const crypto::view_tag &_view_tag) : key(_key), view_tag(_view_tag) { }
crypto::public_key key;
crypto::view_tag view_tag; // optimization to reduce scanning time
BEGIN_SERIALIZE_OBJECT()
FIELD(key)
FIELD(view_tag)
END_SERIALIZE()
};
// outputs <= HF_VERSION_VIEW_TAGS
struct txout_haven_key
{
txout_haven_key() { }
txout_haven_key(const crypto::public_key &_key, const std::string &_asset_type, const uint64_t &_unlock_time, const bool &_is_collateral, const bool &_is_collateral_change) : key(_key), asset_type(_asset_type), unlock_time(_unlock_time), is_collateral(_is_collateral), is_collateral_change(_is_collateral_change) { }
crypto::public_key key;
std::string asset_type;
uint64_t unlock_time;
bool is_collateral;
bool is_collateral_change;
BEGIN_SERIALIZE_OBJECT()
FIELD(key)
FIELD(asset_type)
VARINT_FIELD(unlock_time)
FIELD(is_collateral)
FIELD(is_collateral_change)
END_SERIALIZE()
};
// outputs >= HF_VERSION_VIEW_TAGS
struct txout_haven_tagged_key
{
txout_haven_tagged_key() { }
txout_haven_tagged_key(const crypto::public_key &_key, const std::string &_asset_type, const uint64_t &_unlock_time, const bool &_is_collateral, const bool &_is_collateral_change, const crypto::view_tag &_view_tag) : key(_key), asset_type(_asset_type), unlock_time(_unlock_time), is_collateral(_is_collateral), is_collateral_change(_is_collateral_change), view_tag(_view_tag) { }
crypto::public_key key;
std::string asset_type;
uint64_t unlock_time;
bool is_collateral;
bool is_collateral_change;
crypto::view_tag view_tag; // optimization to reduce scanning time
BEGIN_SERIALIZE_OBJECT()
FIELD(key)
FIELD(asset_type)
VARINT_FIELD(unlock_time)
FIELD(is_collateral)
FIELD(is_collateral_change)
FIELD(view_tag)
END_SERIALIZE()
};
struct txout_offshore
{
txout_offshore() { }
@@ -81,6 +151,70 @@ namespace cryptonote
crypto::public_key key;
};
struct txout_xasset
{
txout_xasset() { }
txout_xasset(const crypto::public_key &_key, const std::string &_asset_type) : key(_key), asset_type(_asset_type) { }
crypto::public_key key;
std::string asset_type;
BEGIN_SERIALIZE_OBJECT()
FIELD(key)
FIELD(asset_type)
END_SERIALIZE()
};
// ZEPHYR
struct txout_zephyr_tagged_key
{
txout_zephyr_tagged_key() { }
txout_zephyr_tagged_key(const crypto::public_key &_key, const std::string &_asset_type, const crypto::view_tag &_view_tag) : key(_key), asset_type(_asset_type), view_tag(_view_tag) { }
crypto::public_key key;
std::string asset_type;
crypto::view_tag view_tag; // optimization to reduce scanning time
BEGIN_SERIALIZE_OBJECT()
FIELD(key)
FIELD(asset_type)
FIELD(view_tag)
END_SERIALIZE()
};
// SALVIUM
struct txout_salvium_key
{
txout_salvium_key() { }
txout_salvium_key(const crypto::public_key &_key, const std::string &_asset_type, const uint64_t &_unlock_time) :
key(_key), asset_type(_asset_type), unlock_time(_unlock_time) { }
crypto::public_key key;
std::string asset_type;
uint64_t unlock_time;
BEGIN_SERIALIZE_OBJECT()
FIELD(key)
FIELD(asset_type)
VARINT_FIELD(unlock_time)
END_SERIALIZE()
};
struct txout_salvium_tagged_key
{
txout_salvium_tagged_key() { }
txout_salvium_tagged_key(const crypto::public_key &_key, const std::string &_asset_type, const uint64_t &_unlock_time, const crypto::view_tag &_view_tag) :
key(_key), asset_type(_asset_type), unlock_time(_unlock_time), view_tag(_view_tag) { }
crypto::public_key key;
std::string asset_type;
uint64_t unlock_time;
crypto::view_tag view_tag; // optimization to reduce scanning time
BEGIN_SERIALIZE_OBJECT()
FIELD(key)
FIELD(asset_type)
VARINT_FIELD(unlock_time)
FIELD(view_tag)
END_SERIALIZE()
};
/* inputs */
struct txin_gen
@@ -159,11 +293,76 @@ namespace cryptonote
END_SERIALIZE()
};
typedef boost::variant<txin_gen, txin_to_script, txin_to_scripthash, txin_to_key, txin_offshore, txin_onshore> txin_v;
struct txin_haven_key
{
uint64_t amount;
std::string asset_type;
std::vector<uint64_t> key_offsets;
crypto::key_image k_image; // double spending protection
typedef boost::variant<txout_to_script, txout_to_scripthash, txout_to_key, txout_offshore> txout_target_v;
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(amount)
FIELD(asset_type)
FIELD(key_offsets)
FIELD(k_image)
END_SERIALIZE()
};
struct txin_xasset
{
uint64_t amount;
std::string asset_type;
std::vector<uint64_t> key_offsets;
crypto::key_image k_image; // double spending protection
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(amount)
FIELD(asset_type)
FIELD(key_offsets)
FIELD(k_image)
END_SERIALIZE()
};
struct txin_zephyr_key
{
uint64_t amount;
std::string asset_type;
std::vector<uint64_t> key_offsets;
crypto::key_image k_image; // double spending protection
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(amount)
FIELD(asset_type)
FIELD(key_offsets)
FIELD(k_image)
END_SERIALIZE()
};
struct txin_salvium_key
{
uint64_t amount;
std::string asset_type;
std::vector<uint64_t> key_offsets;
crypto::key_image k_image; // double spending protection
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(amount)
FIELD(asset_type)
FIELD(key_offsets)
FIELD(k_image)
END_SERIALIZE()
};
typedef boost::variant<txin_gen, txin_to_script, txin_to_scripthash, txin_to_key, txin_offshore, txin_onshore, txin_xasset, txin_haven_key> txin_v;
typedef boost::variant<txin_gen, txin_to_script, txin_to_scripthash, txin_zephyr_key> txin_zephyr_v;
typedef boost::variant<txin_gen, txin_to_script, txin_to_scripthash, txin_salvium_key> txin_salvium_v;
typedef boost::variant<txout_to_script, txout_to_scripthash, txout_to_key, txout_to_tagged_key> txout_target_v;
typedef boost::variant<txout_to_script, txout_to_scripthash, txout_to_key, txout_offshore, txout_xasset, txout_haven_key, txout_haven_tagged_key> txout_xhv_target_v;
typedef boost::variant<txout_to_script, txout_to_scripthash, txout_salvium_key, txout_salvium_tagged_key> txout_salvium_target_v;
typedef boost::variant<txout_to_script, txout_to_scripthash, txout_zephyr_tagged_key> txout_stablero_target_v;
//typedef std::pair<uint64_t, txout> out_t;
struct tx_out
{
uint64_t amount;
@@ -175,6 +374,40 @@ namespace cryptonote
END_SERIALIZE()
};
struct tx_out_xhv
{
uint64_t amount;
txout_xhv_target_v target;
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(amount)
FIELD(target)
END_SERIALIZE()
};
struct tx_out_zephyr
{
uint64_t amount;
txout_stablero_target_v target;
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(amount)
FIELD(target)
END_SERIALIZE()
};
struct tx_out_salvium
{
uint64_t amount;
txout_salvium_target_v target;
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(amount)
FIELD(target)
END_SERIALIZE()
};
enum loki_version
{
loki_version_0 = 0,
@@ -194,7 +427,12 @@ namespace cryptonote
uint64_t unlock_time; //number of block (or time), used as a limitation like: spend this tx not early then block/time
std::vector<txin_v> vin;
std::vector<txin_zephyr_v> vin_zephyr;
std::vector<txin_salvium_v> vin_salvium;
std::vector<tx_out> vout;
std::vector<tx_out_xhv> vout_xhv;
std::vector<tx_out_zephyr> vout_zephyr;
std::vector<tx_out_salvium> vout_salvium;
//extra
std::vector<uint8_t> extra;
// Block height to use PR from
@@ -203,11 +441,29 @@ namespace cryptonote
std::vector<uint8_t> offshore_data;
uint64_t amount_burnt;
uint64_t amount_minted;
std::vector<uint64_t> output_unlock_times;
std::vector<uint32_t> collateral_indices;
// SALVIUM-SPECIFIC FIELDS
// TX type
cryptonote::salvium_transaction_type tx_type;
// Return address
crypto::public_key return_address;
// Return TX public key
crypto::public_key return_pubkey;
// Source asset type
std::string source_asset_type;
// Destination asset type (this is only necessary for CONVERT transactions)
std::string destination_asset_type;
// Circulating supply information - already provided by Haven
//uint64_t amount_burnt;
// Slippage limit
uint64_t amount_slippage_limit;
//
// NOTE: Loki specific
//
std::vector<uint64_t> output_unlock_times;
enum loki_type_t
{
loki_type_standard,
@@ -223,31 +479,309 @@ namespace cryptonote
};
BEGIN_SERIALIZE()
VARINT_FIELD(version)
if (version > loki_version_2 && (blob_type == BLOB_TYPE_CRYPTONOTE_LOKI || blob_type == BLOB_TYPE_CRYPTONOTE_XTNC))
{
FIELD(output_unlock_times)
if (version == loki_version_3_per_output_unlock_times)
FIELD(is_deregister)
}
VARINT_FIELD(unlock_time)
FIELD(vin)
FIELD(vout)
if (blob_type == BLOB_TYPE_CRYPTONOTE_LOKI || blob_type == BLOB_TYPE_CRYPTONOTE_XTNC)
{
if (version >= loki_version_3_per_output_unlock_times && vout.size() != output_unlock_times.size()) return false;
}
FIELD(extra)
if ((blob_type == BLOB_TYPE_CRYPTONOTE_LOKI || blob_type == BLOB_TYPE_CRYPTONOTE_XTNC) && version >= loki_version_4_tx_types)
{
VARINT_FIELD(type)
if (static_cast<uint16_t>(type) >= loki_type_count) return false;
}
if (blob_type == BLOB_TYPE_CRYPTONOTE_XHV && version >= OFFSHORE_TRANSACTION_VERSION) {
if (blob_type == BLOB_TYPE_CRYPTONOTE_XHV) {
VARINT_FIELD(version)
//if(version == 0 || CURRENT_TRANSACTION_VERSION < version) return false;
// Only transactions prior to HAVEN_TYPES_TRANSACTION_VERSION are permitted to be anything other than HAVEN_TYPES and need translation
if (version < HAVEN_TYPES_TRANSACTION_VERSION) {
if (version < POU_TRANSACTION_VERSION) {
VARINT_FIELD(unlock_time)
}
if (!typename Archive<W>::is_saving()) {
FIELD(vin)
FIELD(vout_xhv)
FIELD(extra)
if(version >= OFFSHORE_TRANSACTION_VERSION) {
VARINT_FIELD(pricing_record_height)
if (version < 5)
FIELD(offshore_data)
if (version >= POU_TRANSACTION_VERSION) {
FIELD(output_unlock_times)
if (vout_xhv.size() != output_unlock_times.size()) {
return false;
}
}
VARINT_FIELD(amount_burnt)
VARINT_FIELD(amount_minted)
if (version >= COLLATERAL_TRANSACTION_VERSION && amount_burnt) {
FIELD(collateral_indices)
if (collateral_indices.size() != 2) {
return false;
}
for (const auto vout_idx: collateral_indices) {
if (vout_idx >= vout_xhv.size())
return false;
}
}
}
std::vector<txin_v> vin_tmp(vin);
bool is_conversion_tx = (amount_burnt != 0);
bool is_offshore_tx = is_conversion_tx;
bool is_onshore_tx = false;
vin.clear();
for (auto &vin_entry: vin_tmp) {
if (vin_entry.type() == typeid(txin_gen)) {
vin.push_back(vin_entry);
continue;
}
txin_haven_key in;
if (vin_entry.type() == typeid(txin_to_key)) {
in.asset_type = "XHV";
in.amount = boost::get<txin_to_key>(vin_entry).amount;
in.key_offsets = boost::get<txin_to_key>(vin_entry).key_offsets;
in.k_image = boost::get<txin_to_key>(vin_entry).k_image;
} else if (vin_entry.type() == typeid(txin_offshore)) {
is_offshore_tx = false;
is_onshore_tx = false;
in.asset_type = "XUSD";
in.amount = boost::get<txin_offshore>(vin_entry).amount;
in.key_offsets = boost::get<txin_offshore>(vin_entry).key_offsets;
in.k_image = boost::get<txin_offshore>(vin_entry).k_image;
} else if (vin_entry.type() == typeid(txin_onshore)) {
is_offshore_tx = false;
is_onshore_tx = true;
in.asset_type = "XUSD";
in.amount = boost::get<txin_onshore>(vin_entry).amount;
in.key_offsets = boost::get<txin_onshore>(vin_entry).key_offsets;
in.k_image = boost::get<txin_onshore>(vin_entry).k_image;
} else if (vin_entry.type() == typeid(txin_xasset)) {
is_offshore_tx = false;
is_onshore_tx = false;
in.amount = boost::get<txin_xasset>(vin_entry).amount;
in.key_offsets = boost::get<txin_xasset>(vin_entry).key_offsets;
in.k_image = boost::get<txin_xasset>(vin_entry).k_image;
in.asset_type = boost::get<txin_xasset>(vin_entry).asset_type;
} else {
return false;
}
vin.push_back(in);
}
std::vector<tx_out_xhv> vout_tmp(vout_xhv);
vout_xhv.clear();
for (size_t i=0; i<vout_tmp.size(); i++) {
txout_haven_key out;
if (vout_tmp[i].target.type() == typeid(txout_to_key)) {
out.asset_type = "XHV";
out.key = boost::get<txout_to_key>(vout_tmp[i].target).key;
} else if (vout_tmp[i].target.type() == typeid(txout_offshore)) {
out.asset_type = "XUSD";
out.key = boost::get<txout_offshore>(vout_tmp[i].target).key;
} else if (vout_tmp[i].target.type() == typeid(txout_xasset)) {
out.asset_type = boost::get<txout_xasset>(vout_tmp[i].target).asset_type;
out.key = boost::get<txout_xasset>(vout_tmp[i].target).key;
} else {
return false;
}
out.unlock_time = (version >= POU_TRANSACTION_VERSION) ? output_unlock_times[i] : unlock_time;
out.is_collateral = false;
out.is_collateral_change = false;
if (version >= COLLATERAL_TRANSACTION_VERSION && amount_burnt) {
if (((is_onshore_tx) &&
(collateral_indices[0] == i)) ||
((!is_onshore_tx) &&
(is_offshore_tx) &&
(collateral_indices[0] == i && collateral_indices[1] == 0))) {
out.is_collateral = true;
}
if (is_onshore_tx && collateral_indices[1] == i) {
out.is_collateral_change = true;
}
}
tx_out_xhv foo;
foo.amount = vout_tmp[i].amount;
foo.target = out;
vout_xhv.push_back(foo);
}
return true;
}
bool is_offshore_tx = (amount_burnt != 0);
bool is_onshore_tx = false;
std::vector<txin_v> vin_tmp;
vin_tmp.reserve(vin.size());
for (auto &vin_entry_v: vin) {
if (vin_entry_v.type() == typeid(txin_gen)) {
vin_tmp.push_back(vin_entry_v);
continue;
}
txin_haven_key vin_entry = boost::get<txin_haven_key>(vin_entry_v);
if (vin_entry.asset_type == "XHV") {
txin_to_key in;
in.amount = vin_entry.amount;
in.key_offsets = vin_entry.key_offsets;
in.k_image = vin_entry.k_image;
vin_tmp.push_back(in);
} else if (vin_entry.asset_type == "XUSD") {
is_offshore_tx = false;
int xhv_outputs = std::count_if(vout_xhv.begin(), vout_xhv.end(), [](tx_out_xhv &foo_v) {
if (foo_v.target.type() == typeid(txout_haven_key)) {
txout_haven_key out = boost::get<txout_haven_key>(foo_v.target);
return out.asset_type == "XHV";
} else if (foo_v.target.type() == typeid(txout_haven_tagged_key)) {
txout_haven_tagged_key out = boost::get<txout_haven_tagged_key>(foo_v.target);
return out.asset_type == "XHV";
} else {
return false;
}
});
if (xhv_outputs) {
is_onshore_tx = true;
txin_onshore in;
in.amount = vin_entry.amount;
in.key_offsets = vin_entry.key_offsets;
in.k_image = vin_entry.k_image;
vin_tmp.push_back(in);
} else {
txin_offshore in;
in.amount = vin_entry.amount;
in.key_offsets = vin_entry.key_offsets;
in.k_image = vin_entry.k_image;
vin_tmp.push_back(in);
}
} else {
is_offshore_tx = false;
txin_xasset in;
in.amount = vin_entry.amount;
in.asset_type = vin_entry.asset_type;
in.key_offsets = vin_entry.key_offsets;
in.k_image = vin_entry.k_image;
vin_tmp.push_back(in);
}
}
std::vector<tx_out_xhv> vout_tmp;
vout_tmp.reserve(vout_xhv.size());
output_unlock_times.resize(vout_xhv.size());
std::vector<uint32_t> collateral_indices_temp;
collateral_indices_temp.resize(2);
for (size_t i=0; i<vout_xhv.size(); i++) {
txout_haven_key outhk = boost::get<txout_haven_key>(vout_xhv[i].target);
tx_out_xhv foo;
foo.amount = vout_xhv[i].amount;
if (outhk.asset_type == "XHV") {
txout_to_key out;
out.key = outhk.key;
foo.target = out;
} else if (outhk.asset_type == "XUSD") {
txout_offshore out;
out.key = outhk.key;
foo.target = out;
} else {
txout_xasset out;
out.asset_type = outhk.asset_type;
out.key = outhk.key;
foo.target = out;
}
output_unlock_times[i] = outhk.unlock_time;
if (outhk.is_collateral) {
collateral_indices_temp[0] = i;
} else if (outhk.is_collateral_change) {
collateral_indices_temp[1] = i;
}
vout_tmp.push_back(foo);
}
FIELD_N("vin", vin_tmp)
FIELD_N("vout", vout_tmp)
FIELD(extra)
if(version >= OFFSHORE_TRANSACTION_VERSION) {
VARINT_FIELD(pricing_record_height)
if (version < 5)
FIELD(offshore_data)
if (version >= POU_TRANSACTION_VERSION) {
FIELD(output_unlock_times)
if (vout_xhv.size() != output_unlock_times.size()) {
return false;
}
}
VARINT_FIELD(amount_burnt)
VARINT_FIELD(amount_minted)
if (version >= COLLATERAL_TRANSACTION_VERSION && amount_burnt) {
if (collateral_indices.size() != 2) {
if ((is_offshore_tx || is_onshore_tx) && collateral_indices_temp.size() != 2) {
return false;
}
if (is_offshore_tx || is_onshore_tx) {
collateral_indices = collateral_indices_temp;
} else {
collateral_indices.clear();
collateral_indices.push_back(0);
collateral_indices.push_back(0);
}
}
FIELD(collateral_indices)
for (const auto vout_idx: collateral_indices) {
if (vout_idx >= vout_xhv.size())
return false;
}
}
}
return true;
}
FIELD(vin)
FIELD(vout_xhv)
FIELD(extra)
VARINT_FIELD(pricing_record_height)
FIELD(offshore_data)
VARINT_FIELD(amount_burnt)
VARINT_FIELD(amount_minted)
} else if (blob_type == BLOB_TYPE_CRYPTONOTE_SALVIUM) {
VARINT_FIELD(version)
//if(version == 0 || CURRENT_TRANSACTION_VERSION < version) return false;
VARINT_FIELD(unlock_time)
FIELD(vin_salvium)
FIELD(vout_salvium)
FIELD(extra)
VARINT_FIELD(tx_type)
if (tx_type != cryptonote::salvium_transaction_type::PROTOCOL) {
VARINT_FIELD(amount_burnt)
if (tx_type != cryptonote::salvium_transaction_type::MINER) {
FIELD(return_address)
FIELD(return_pubkey)
FIELD(source_asset_type)
FIELD(destination_asset_type)
VARINT_FIELD(amount_slippage_limit)
}
}
} else {
VARINT_FIELD(version)
if (version > loki_version_2 && (blob_type == BLOB_TYPE_CRYPTONOTE_LOKI || blob_type == BLOB_TYPE_CRYPTONOTE_XTNC))
{
FIELD(output_unlock_times)
if (version == loki_version_3_per_output_unlock_times)
FIELD(is_deregister)
}
VARINT_FIELD(unlock_time)
if (blob_type == BLOB_TYPE_CRYPTONOTE_ZEPHYR)
FIELD(vin_zephyr)
else
FIELD(vin)
if (blob_type == BLOB_TYPE_CRYPTONOTE_ZEPHYR)
FIELD(vout_zephyr)
else
FIELD(vout)
if (blob_type == BLOB_TYPE_CRYPTONOTE_LOKI || blob_type == BLOB_TYPE_CRYPTONOTE_XTNC)
{
if (version >= loki_version_3_per_output_unlock_times && vout.size() != output_unlock_times.size()) return false;
}
FIELD(extra)
if ((blob_type == BLOB_TYPE_CRYPTONOTE_LOKI || blob_type == BLOB_TYPE_CRYPTONOTE_XTNC) && version >= loki_version_4_tx_types)
{
VARINT_FIELD(type)
if (static_cast<uint16_t>(type) >= loki_type_count) return false;
}
if (blob_type == BLOB_TYPE_CRYPTONOTE_ZEPHYR) {
VARINT_FIELD(pricing_record_height)
VARINT_FIELD(amount_burnt)
VARINT_FIELD(amount_minted)
}
}
END_SERIALIZE()
@@ -303,25 +837,42 @@ namespace cryptonote
else
{
ar.tag("rct_signatures");
if (!vin.empty())
if (blob_type == BLOB_TYPE_CRYPTONOTE_SALVIUM ? !vin_salvium.empty() : (blob_type == BLOB_TYPE_CRYPTONOTE_ZEPHYR ? !vin_zephyr.empty() : !vin.empty()))
{
ar.begin_object();
bool r = rct_signatures.serialize_rctsig_base(ar, vin.size(), vout.size());
bool r;
if (blob_type == BLOB_TYPE_CRYPTONOTE_XHV)
r = rct_signatures.serialize_rctsig_base(ar, vin.size(), vout_xhv.size());
else if (blob_type == BLOB_TYPE_CRYPTONOTE_ZEPHYR)
r = rct_signatures.serialize_rctsig_base(ar, vin_zephyr.size(), vout_zephyr.size());
else if (blob_type == BLOB_TYPE_CRYPTONOTE_SALVIUM)
r = rct_signatures.serialize_rctsig_base(ar, vin_salvium.size(), vout_salvium.size());
else
r = rct_signatures.serialize_rctsig_base(ar, vin.size(), vout.size());
if (!r || !ar.stream().good()) return false;
ar.end_object();
if (rct_signatures.type != rct::RCTTypeNull)
{
ar.tag("rctsig_prunable");
ar.begin_object();
if (blob_type != BLOB_TYPE_CRYPTONOTE_XHV) {
r = rct_signatures.p.serialize_rctsig_prunable(ar, rct_signatures.type, vin.size(), vout.size(),
vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(vin[0]).key_offsets.size() - 1 : 0);
} else {
r = rct_signatures.p.serialize_rctsig_prunable(ar, rct_signatures.type, vin.size(), vout.size(),
if (blob_type == BLOB_TYPE_CRYPTONOTE_ZEPHYR) {
r = rct_signatures.p.serialize_rctsig_prunable(ar, rct_signatures.type, vin_zephyr.size(), vout_zephyr.size(),
vin_zephyr[0].type() == typeid(txin_zephyr_key) ? boost::get<txin_zephyr_key>(vin_zephyr[0]).key_offsets.size() - 1 : 0);
} else if (blob_type == BLOB_TYPE_CRYPTONOTE_SALVIUM) {
r = rct_signatures.p.serialize_rctsig_prunable(ar, rct_signatures.type, vin_salvium.size(), vout_salvium.size(),
vin_salvium[0].type() == typeid(txin_salvium_key) ? boost::get<txin_salvium_key>(vin_salvium[0]).key_offsets.size() - 1 : 0);
} else if (blob_type == BLOB_TYPE_CRYPTONOTE_XHV) {
r = rct_signatures.p.serialize_rctsig_prunable(ar, rct_signatures.type, vin.size(), vout_xhv.size(),
vin.size() > 0 && vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(vin[0]).key_offsets.size() - 1 :
vin.size() > 0 && vin[0].type() == typeid(txin_offshore) ? boost::get<txin_offshore>(vin[0]).key_offsets.size() - 1 :
vin.size() > 0 && vin[0].type() == typeid(txin_onshore) ? boost::get<txin_onshore>(vin[0]).key_offsets.size() - 1 :
0);
vin.size() > 0 && vin[0].type() == typeid(txin_xasset) ? boost::get<txin_xasset>(vin[0]).key_offsets.size() - 1 :
vin.size() > 0 && vin[0].type() == typeid(txin_haven_key) ? boost::get<txin_haven_key>(vin[0]).key_offsets.size() - 1 :
0
);
} else {
r = rct_signatures.p.serialize_rctsig_prunable(ar, rct_signatures.type, vin.size(), vout.size(),
vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(vin[0]).key_offsets.size() - 1 : 0);
}
if (!r || !ar.stream().good()) return false;
ar.end_object();
@@ -352,13 +903,18 @@ namespace cryptonote
version = 0;
unlock_time = 0;
vin.clear();
vin_zephyr.clear();
vout.clear();
vout_xhv.clear();
vout_zephyr.clear();
extra.clear();
signatures.clear();
pricing_record_height = 0;
offshore_data.clear();
amount_burnt = 0;
amount_minted = 0;
output_unlock_times.clear();
collateral_indices.clear();
}
inline
@@ -372,6 +928,9 @@ namespace cryptonote
size_t operator()(const txin_to_key& txin) const {return txin.key_offsets.size();}
size_t operator()(const txin_offshore& txin) const {return txin.key_offsets.size();}
size_t operator()(const txin_onshore& txin) const {return txin.key_offsets.size();}
size_t operator()(const txin_xasset& txin) const {return txin.key_offsets.size();}
size_t operator()(const txin_haven_key& txin) const {return txin.key_offsets.size();}
size_t operator()(const txin_zephyr_key& txin) const {return txin.key_offsets.size();}
};
return boost::apply_visitor(txin_signature_size_visitor(), tx_in);
@@ -492,9 +1051,12 @@ namespace cryptonote
uint64_t nonce;
uint64_t nonce8;
offshore::pricing_record pricing_record;
zephyr_oracle::pricing_record zephyr_pricing_record;
salvium_oracle::pricing_record salvium_pricing_record;
crypto::cycle cycle;
crypto::cycle40 cycle40;
crypto::cycle48 cycle48;
crypto::signature signature;
BEGIN_SERIALIZE()
VARINT_FIELD(major_version)
@@ -515,7 +1077,45 @@ namespace cryptonote
if (blob_type == BLOB_TYPE_CRYPTONOTE_XTNC || blob_type == BLOB_TYPE_CRYPTONOTE_CUCKOO) FIELD(cycle)
if (blob_type == BLOB_TYPE_CRYPTONOTE_TUBE) FIELD(cycle40)
if (blob_type == BLOB_TYPE_CRYPTONOTE_XTA) FIELD(cycle48)
if (blob_type == BLOB_TYPE_CRYPTONOTE_XHV) FIELD(pricing_record)
if (blob_type == BLOB_TYPE_CRYPTONOTE_XHV) {
FIELD(pricing_record)
} else if (blob_type == BLOB_TYPE_CRYPTONOTE_SALVIUM) {
if (major_version >= 2) FIELD(salvium_pricing_record)
} else if (blob_type == BLOB_TYPE_CRYPTONOTE_ZEPHYR) {
if (major_version >= 4)
{
FIELD_N("pricing_record", zephyr_pricing_record)
}
else if (major_version >= 3)
{
zephyr_oracle::pricing_record_v2 pr_v2;
if (!typename Archive<W>::is_saving())
{
FIELD(pr_v2)
pr_v2.write_to_pr(zephyr_pricing_record);
}
else
{
pr_v2.read_from_pr(zephyr_pricing_record);
FIELD(pr_v2)
}
}
else
{
zephyr_oracle::pricing_record_v1 pr_v1;
if (!typename Archive<W>::is_saving())
{
FIELD(pr_v1)
pr_v1.write_to_pr(zephyr_pricing_record);
}
else
{
pr_v1.read_from_pr(zephyr_pricing_record);
FIELD(pr_v1)
}
}
}
if (blob_type == BLOB_TYPE_CRYPTONOTE_XLA && major_version >= 13) FIELD(signature)
END_SERIALIZE()
};
@@ -525,10 +1125,11 @@ namespace cryptonote
bytecoin_block parent_block;
transaction miner_tx;
transaction protocol_tx;
std::vector<crypto::hash> tx_hashes;
mutable crypto::hash uncle = cryptonote::null_hash;
void set_blob_type(enum BLOB_TYPE bt) { miner_tx.blob_type = blob_type = bt; }
void set_blob_type(enum BLOB_TYPE bt) { miner_tx.blob_type = protocol_tx.blob_type = blob_type = bt; }
BEGIN_SERIALIZE_OBJECT()
FIELDS(*static_cast<block_header *>(this))
@@ -538,6 +1139,10 @@ namespace cryptonote
FIELD_N("parent_block", sbb);
}
FIELD(miner_tx)
if (blob_type == BLOB_TYPE_CRYPTONOTE_SALVIUM)
{
FIELD(protocol_tx)
}
FIELD(tx_hashes)
if (blob_type == BLOB_TYPE_CRYPTONOTE3)
{
@@ -585,19 +1190,6 @@ namespace cryptonote
KV_SERIALIZE(payment_id)
END_KV_SERIALIZE_MAP()
};
struct keypair
{
crypto::public_key pub;
crypto::secret_key sec;
static inline keypair generate()
{
keypair k;
generate_keys(k.pub, k.sec);
return k;
}
};
//---------------------------------------------------------------
}
@@ -610,37 +1202,22 @@ VARIANT_TAG(binary_archive, cryptonote::txin_gen, 0xff);
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::txin_zephyr_key, 0x2);
VARIANT_TAG(binary_archive, cryptonote::txin_offshore, 0x3);
VARIANT_TAG(binary_archive, cryptonote::txin_salvium_key, 0x2);
VARIANT_TAG(binary_archive, cryptonote::txin_onshore, 0x4);
VARIANT_TAG(binary_archive, cryptonote::txin_xasset, 0x5);
VARIANT_TAG(binary_archive, cryptonote::txin_haven_key, 0x6);
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::txout_salvium_key, 0x2);
VARIANT_TAG(binary_archive, cryptonote::txout_zephyr_tagged_key, 0x2);
VARIANT_TAG(binary_archive, cryptonote::txout_to_tagged_key, 0x3);
VARIANT_TAG(binary_archive, cryptonote::txout_salvium_tagged_key, 0x3);
VARIANT_TAG(binary_archive, cryptonote::txout_offshore, 0x3);
VARIANT_TAG(binary_archive, cryptonote::txout_xasset, 0x5);
VARIANT_TAG(binary_archive, cryptonote::txout_haven_key, 0x6);
VARIANT_TAG(binary_archive, cryptonote::txout_haven_tagged_key, 0x7);
VARIANT_TAG(binary_archive, cryptonote::transaction, 0xcc);
VARIANT_TAG(binary_archive, cryptonote::block, 0xbb);
VARIANT_TAG(json_archive, cryptonote::txin_gen, "gen");
VARIANT_TAG(json_archive, cryptonote::txin_to_script, "script");
VARIANT_TAG(json_archive, cryptonote::txin_to_scripthash, "scripthash");
VARIANT_TAG(json_archive, cryptonote::txin_to_key, "key");
VARIANT_TAG(json_archive, cryptonote::txin_offshore, "offshore");
VARIANT_TAG(json_archive, cryptonote::txin_onshore, "onshore");
VARIANT_TAG(json_archive, cryptonote::txout_to_script, "script");
VARIANT_TAG(json_archive, cryptonote::txout_to_scripthash, "scripthash");
VARIANT_TAG(json_archive, cryptonote::txout_to_key, "key");
VARIANT_TAG(json_archive, cryptonote::txout_offshore, "offshore");
VARIANT_TAG(json_archive, cryptonote::transaction, "tx");
VARIANT_TAG(json_archive, cryptonote::block, "block");
VARIANT_TAG(debug_archive, cryptonote::txin_gen, "gen");
VARIANT_TAG(debug_archive, cryptonote::txin_to_script, "script");
VARIANT_TAG(debug_archive, cryptonote::txin_to_scripthash, "scripthash");
VARIANT_TAG(debug_archive, cryptonote::txin_to_key, "key");
VARIANT_TAG(debug_archive, cryptonote::txin_offshore, "offshore");
VARIANT_TAG(debug_archive, cryptonote::txin_onshore, "onshore");
VARIANT_TAG(debug_archive, cryptonote::txout_to_script, "script");
VARIANT_TAG(debug_archive, cryptonote::txout_to_scripthash, "scripthash");
VARIANT_TAG(debug_archive, cryptonote::txout_to_key, "key");
VARIANT_TAG(debug_archive, cryptonote::txout_offshore, "offshore");
VARIANT_TAG(debug_archive, cryptonote::transaction, "tx");
VARIANT_TAG(debug_archive, cryptonote::block, "block");
+29 -218
View File
@@ -56,61 +56,6 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki)
{
crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation);
bool r = crypto::generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation);
CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")");
r = crypto::derive_public_key(recv_derivation, real_output_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub);
CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to derive_public_key(" << recv_derivation << ", " << real_output_index << ", " << ack.m_account_address.m_spend_public_key << ")");
crypto::derive_secret_key(recv_derivation, real_output_index, ack.m_spend_secret_key, in_ephemeral.sec);
crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki);
return true;
}
//---------------------------------------------------------------
uint64_t power_integral(uint64_t a, uint64_t b)
{
if(b == 0)
return 1;
uint64_t total = a;
for(uint64_t i = 1; i != b; i++)
total *= a;
return total;
}
//---------------------------------------------------------------
bool get_tx_fee(const transaction& tx, uint64_t & fee)
{
uint64_t amount_in = 0;
uint64_t amount_out = 0;
BOOST_FOREACH(auto& in, tx.vin)
{
if (tx.blob_type != BLOB_TYPE_CRYPTONOTE_XHV) {
CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key), 0, "unexpected type id in transaction");
amount_in += boost::get<txin_to_key>(in).amount;
} else {
CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key) || in.type() == typeid(txin_offshore) || in.type() == typeid(txin_onshore), 0, "unexpected type id in transaction");
amount_in += in.type() == typeid(txin_to_key) ? boost::get<txin_to_key>(in).amount : in.type() == typeid(txin_onshore) ? boost::get<txin_onshore>(in).amount : boost::get<txin_offshore>(in).amount;
}
}
BOOST_FOREACH(auto& o, tx.vout)
amount_out += o.amount;
CHECK_AND_ASSERT_MES(amount_in >= amount_out, false, "transaction spend (" <<amount_in << ") more than it has (" << amount_out << ")");
fee = amount_in - amount_out;
return true;
}
//---------------------------------------------------------------
uint64_t get_tx_fee(const transaction& tx)
{
uint64_t r = 0;
if(!get_tx_fee(tx, r))
return 0;
return r;
}
//---------------------------------------------------------------
bool parse_tx_extra(const std::vector<uint8_t>& tx_extra, std::vector<tx_extra_field>& tx_extra_fields)
{
tx_extra_fields.clear();
@@ -218,122 +163,6 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
bool get_inputs_money_amount(const transaction& tx, uint64_t& money)
{
money = 0;
BOOST_FOREACH(const auto& in, tx.vin)
{
CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false);
money += tokey_in.amount;
}
return true;
}
//---------------------------------------------------------------
uint64_t get_block_height(const block& b)
{
CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, 0, "wrong miner tx in block: " << get_block_hash(b) << ", b.miner_tx.vin.size() != 1");
CHECKED_GET_SPECIFIC_VARIANT(b.miner_tx.vin[0], const txin_gen, coinbase_in, 0);
return coinbase_in.height;
}
//---------------------------------------------------------------
bool check_inputs_types_supported(const transaction& tx)
{
BOOST_FOREACH(const auto& in, tx.vin)
{
if (tx.blob_type != BLOB_TYPE_CRYPTONOTE_XHV) {
CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key), false, "wrong variant type: "
<< in.type().name() << ", expected " << typeid(txin_to_key).name()
<< ", in transaction id=" << get_transaction_hash(tx));
} else {
CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key) || in.type() == typeid(txin_offshore) || in.type() == typeid(txin_onshore), false, "wrong variant type: "
<< in.type().name() << ", expected " << typeid(txin_to_key).name() << "or " << typeid(txin_onshore).name()
<< ", in transaction id=" << get_transaction_hash(tx));
}
}
return true;
}
//-----------------------------------------------------------------------------------------------
bool check_outs_valid(const transaction& tx)
{
BOOST_FOREACH(const tx_out& out, tx.vout)
{
if (tx.blob_type != BLOB_TYPE_CRYPTONOTE_XHV) {
CHECK_AND_ASSERT_MES(out.target.type() == typeid(txout_to_key), false, "wrong variant type: "
<< out.target.type().name() << ", expected " << typeid(txout_to_key).name()
<< ", in transaction id=" << get_transaction_hash(tx));
} else {
CHECK_AND_ASSERT_MES(out.target.type() == typeid(txout_to_key) || out.target.type() == typeid(txout_offshore), false, "wrong variant type: "
<< out.target.type().name() << ", expected " << typeid(txout_to_key).name()
<< "or " << typeid(txout_offshore).name()
<< ", in transaction id=" << get_transaction_hash(tx));
}
if (tx.version == 1)
{
CHECK_AND_NO_ASSERT_MES(0 < out.amount, false, "zero amount ouput in transaction id=" << get_transaction_hash(tx));
}
if (tx.blob_type != BLOB_TYPE_CRYPTONOTE_XHV) {
if(!check_key(boost::get<txout_to_key>(out.target).key))
return false;
} else {
if(!check_key(out.target.type() == typeid(txout_to_key) ? boost::get<txout_to_key>(out.target).key : boost::get<txout_offshore>(out.target).key))
return false;
}
}
return true;
}
//-----------------------------------------------------------------------------------------------
bool check_money_overflow(const transaction& tx)
{
return check_inputs_overflow(tx) && check_outs_overflow(tx);
}
//---------------------------------------------------------------
bool check_inputs_overflow(const transaction& tx)
{
uint64_t money = 0;
BOOST_FOREACH(const auto& in, tx.vin)
{
if (tx.blob_type == BLOB_TYPE_CRYPTONOTE_XHV && tx.vin[0].type() == typeid(txin_offshore)) {
CHECKED_GET_SPECIFIC_VARIANT(in, const txin_offshore, tokey_in, false);
if(money > tokey_in.amount + money)
return false;
money += tokey_in.amount;
} else if (tx.blob_type == BLOB_TYPE_CRYPTONOTE_XHV && tx.vin[0].type() == typeid(txin_onshore)) {
CHECKED_GET_SPECIFIC_VARIANT(in, const txin_onshore, tokey_in, false);
if(money > tokey_in.amount + money)
return false;
money += tokey_in.amount;
} else {
CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false);
if(money > tokey_in.amount + money)
return false;
money += tokey_in.amount;
}
}
return true;
}
//---------------------------------------------------------------
bool check_outs_overflow(const transaction& tx)
{
uint64_t money = 0;
BOOST_FOREACH(const auto& o, tx.vout)
{
if(money > o.amount + money)
return false;
money += o.amount;
}
return true;
}
//---------------------------------------------------------------
uint64_t get_outs_money_amount(const transaction& tx)
{
uint64_t outputs_amount = 0;
BOOST_FOREACH(const auto& o, tx.vout)
outputs_amount += o.amount;
return outputs_amount;
}
//---------------------------------------------------------------
std::string short_hash_str(const crypto::hash& h)
{
std::string res = string_tools::pod_to_hex(h);
@@ -343,40 +172,6 @@ namespace cryptonote
return res;
}
//---------------------------------------------------------------
bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, size_t output_index)
{
crypto::key_derivation derivation;
generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation);
crypto::public_key pk;
derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk);
return pk == out_key.key;
}
//---------------------------------------------------------------
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector<size_t>& outs, uint64_t& money_transfered)
{
crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
if(null_pkey == tx_pub_key)
return false;
return lookup_acc_outs(acc, tx, tx_pub_key, outs, money_transfered);
}
//---------------------------------------------------------------
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector<size_t>& outs, uint64_t& money_transfered)
{
money_transfered = 0;
size_t i = 0;
BOOST_FOREACH(const tx_out& o, tx.vout)
{
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key), false, "wrong type id in transaction out" );
if(is_out_to_acc(acc, boost::get<txout_to_key>(o.target), tx_pub_key, i))
{
outs.push_back(i);
money_transfered += o.amount;
}
i++;
}
return true;
}
//---------------------------------------------------------------
void get_blob_hash(const blobdata& blob, crypto::hash& res)
{
cn_fast_hash(blob.data(), blob.size(), res);
@@ -425,8 +220,8 @@ namespace cryptonote
{
std::stringstream ss;
binary_archive<true> ba(ss);
const size_t inputs = t.vin.size();
const size_t outputs = t.vout.size();
const size_t inputs = t.blob_type == BLOB_TYPE_CRYPTONOTE_SALVIUM ? t.vin_salvium.size() : (t.blob_type == BLOB_TYPE_CRYPTONOTE_ZEPHYR ? t.vin_zephyr.size() : t.vin.size());
const size_t outputs = t.blob_type == BLOB_TYPE_CRYPTONOTE_SALVIUM ? t.vout_salvium.size() : (t.blob_type == BLOB_TYPE_CRYPTONOTE_ZEPHYR ? t.vout_zephyr.size() : (t.blob_type != BLOB_TYPE_CRYPTONOTE_XHV ? t.vout.size() : t.vout_xhv.size()));
bool r = tt.rct_signatures.serialize_rctsig_base(ba, inputs, outputs);
CHECK_AND_ASSERT_MES(r, false, "Failed to serialize rct signatures base");
cryptonote::get_blob_hash(ss.str(), hashes[1]);
@@ -441,17 +236,23 @@ namespace cryptonote
{
std::stringstream ss;
binary_archive<true> ba(ss);
const size_t inputs = t.vin.size();
const size_t outputs = t.vout.size();
const size_t inputs = t.blob_type == BLOB_TYPE_CRYPTONOTE_SALVIUM ? t.vin_salvium.size() : (t.blob_type == BLOB_TYPE_CRYPTONOTE_ZEPHYR ? t.vin_zephyr.size() : t.vin.size());
const size_t outputs = t.blob_type == BLOB_TYPE_CRYPTONOTE_SALVIUM ? t.vout_salvium.size() : (t.blob_type == BLOB_TYPE_CRYPTONOTE_ZEPHYR ? t.vout_zephyr.size() : (t.blob_type != BLOB_TYPE_CRYPTONOTE_XHV ? t.vout.size() : t.vout_xhv.size()));
size_t mixin;
if (t.blob_type != BLOB_TYPE_CRYPTONOTE_XHV) {
mixin = t.vin.empty() ? 0 : t.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(t.vin[0]).key_offsets.size() - 1 : 0;
} else {
if (t.blob_type == BLOB_TYPE_CRYPTONOTE_SALVIUM) {
mixin = t.vin_salvium.empty() ? 0 : t.vin_salvium[0].type() == typeid(txin_salvium_key) ? boost::get<txin_salvium_key>(t.vin_salvium[0]).key_offsets.size() - 1 : 0;
} else if (t.blob_type == BLOB_TYPE_CRYPTONOTE_ZEPHYR) {
mixin = t.vin_zephyr.empty() ? 0 : t.vin_zephyr[0].type() == typeid(txin_zephyr_key) ? boost::get<txin_zephyr_key>(t.vin_zephyr[0]).key_offsets.size() - 1 : 0;
} else if (t.blob_type == BLOB_TYPE_CRYPTONOTE_XHV) {
mixin = t.vin.empty() ? 0 :
t.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(t.vin[0]).key_offsets.size() - 1 :
t.vin[0].type() == typeid(txin_offshore) ? boost::get<txin_offshore>(t.vin[0]).key_offsets.size() - 1 :
t.vin[0].type() == typeid(txin_onshore) ? boost::get<txin_onshore>(t.vin[0]).key_offsets.size() - 1 :
0;
t.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(t.vin[0]).key_offsets.size() - 1 :
t.vin[0].type() == typeid(txin_offshore) ? boost::get<txin_offshore>(t.vin[0]).key_offsets.size() - 1 :
t.vin[0].type() == typeid(txin_onshore) ? boost::get<txin_onshore>(t.vin[0]).key_offsets.size() - 1 :
t.vin[0].type() == typeid(txin_xasset) ? boost::get<txin_xasset>(t.vin[0]).key_offsets.size() - 1 :
t.vin[0].type() == typeid(txin_haven_key) ? boost::get<txin_haven_key>(t.vin[0]).key_offsets.size() - 1 :
0;
} else {
mixin = t.vin.empty() ? 0 : t.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(t.vin[0]).key_offsets.size() - 1 : 0;
}
bool r = tt.rct_signatures.p.serialize_rctsig_prunable(ba, t.rct_signatures.type, inputs, outputs, mixin);
CHECK_AND_ASSERT_MES(r, false, "Failed to serialize rct signatures prunable");
@@ -486,7 +287,11 @@ namespace cryptonote
}
crypto::hash tree_root_hash = get_tx_tree_hash(b);
blob.append(reinterpret_cast<const char*>(&tree_root_hash), sizeof(tree_root_hash));
blob.append(tools::get_varint_data(b.tx_hashes.size()+1));
if (b.blob_type == BLOB_TYPE_CRYPTONOTE_SALVIUM) {
blob.append(tools::get_varint_data(b.tx_hashes.size()+2));
} else {
blob.append(tools::get_varint_data(b.tx_hashes.size()+1));
}
if (b.blob_type == BLOB_TYPE_CRYPTONOTE3) {
blob.append(reinterpret_cast<const char*>(&b.uncle), sizeof(b.uncle));
}
@@ -572,7 +377,7 @@ namespace cryptonote
ss << b_blob;
binary_archive<false> ba(ss);
bool r = ::serialization::serialize(ba, b);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse block from blob");
CHECK_AND_ASSERT_MES(r, false, "Failed to parse block from blob 1");
return true;
}
//---------------------------------------------------------------
@@ -614,6 +419,12 @@ namespace cryptonote
crypto::hash h = null_hash;
size_t bl_sz = 0;
get_transaction_hash(b.miner_tx, h, bl_sz);
if (b.blob_type == BLOB_TYPE_CRYPTONOTE_SALVIUM) {
txs_ids.push_back(h);
h = null_hash;
bl_sz = 0;
get_transaction_hash(b.protocol_tx, h, bl_sz);
}
txs_ids.push_back(h);
BOOST_FOREACH(auto& th, b.tx_hashes)
txs_ids.push_back(th);
+1 -23
View File
@@ -63,12 +63,6 @@ namespace cryptonote
bool get_payment_id_from_tx_extra_nonce(const blobdata& extra_nonce, crypto::hash& payment_id);
bool append_mm_tag_to_extra(std::vector<uint8_t>& tx_extra, const tx_extra_merge_mining_tag& mm_tag);
bool get_mm_tag_from_extra(const std::vector<uint8_t>& tx_extra, tx_extra_merge_mining_tag& mm_tag);
bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, size_t output_index);
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector<size_t>& outs, uint64_t& money_transfered);
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector<size_t>& outs, uint64_t& money_transfered);
bool get_tx_fee(const transaction& tx, uint64_t & fee);
uint64_t get_tx_fee(const transaction& tx);
bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki);
void get_blob_hash(const blobdata& blob, crypto::hash& res);
crypto::hash get_blob_hash(const blobdata& blob);
std::string short_hash_str(const crypto::hash& h);
@@ -84,15 +78,9 @@ namespace cryptonote
bool get_block_header_hash(const block& b, crypto::hash& res);
bool get_bytecoin_block_longhash(const block& blk, crypto::hash& res);
bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b);
bool get_inputs_money_amount(const transaction& tx, uint64_t& money);
uint64_t get_outs_money_amount(const transaction& tx);
bool check_inputs_types_supported(const transaction& tx);
std::map<std::string, uint64_t> get_outs_money_amount(const transaction& tx);
bool check_outs_valid(const transaction& tx);
bool check_money_overflow(const transaction& tx);
bool check_outs_overflow(const transaction& tx);
bool check_inputs_overflow(const transaction& tx);
uint64_t get_block_height(const block& b);
std::vector<uint64_t> relative_output_offsets_to_absolute(const std::vector<uint64_t>& off);
std::vector<uint64_t> absolute_output_offsets_to_relative(const std::vector<uint64_t>& off);
//---------------------------------------------------------------
@@ -137,16 +125,6 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
template <typename T>
std::string obj_to_json_str(T& obj)
{
std::stringstream ss;
json_archive<true> ar(ss, true);
bool r = ::serialization::serialize(ar, obj);
CHECK_AND_ASSERT_MES(r, "", "obj_to_json_str failed: serialization::serialize returned false");
return ss.str();
}
//---------------------------------------------------------------
// 62387455827 -> 455827 + 7000000 + 80000000 + 300000000 + 2000000000 + 60000000000, where 455827 <= dust_threshold
template<typename chunk_handler_t, typename dust_handler_t>
void decompose_amount_into_digits(uint64_t amount, uint64_t dust_threshold, const chunk_handler_t& chunk_handler, const dust_handler_t& dust_handler)
+27
View File
@@ -32,12 +32,16 @@
#define TX_EXTRA_PADDING_MAX_COUNT 255
#define TX_EXTRA_NONCE_MAX_COUNT 255
#define TX_EXTRA_OFFSHORE_MAX_COUNT 255
#define TX_EXTRA_MEMO_MAX_COUNT 255
#define TX_EXTRA_TAG_PADDING 0x00
#define TX_EXTRA_TAG_PUBKEY 0x01
#define TX_EXTRA_NONCE 0x02
#define TX_EXTRA_MERGE_MINING_TAG 0x03
#define TX_EXTRA_TAG_ADDITIONAL_PUBKEYS 0x04
#define TX_EXTRA_TAG_OFFSHORE 0x17
#define TX_EXTRA_TAG_MEMO 0x18
#define TX_EXTRA_TAG_SERVICE_NODE_REGISTER 0x70
#define TX_EXTRA_TAG_SERVICE_NODE_DEREGISTER 0x71
#define TX_EXTRA_TAG_SERVICE_NODE_WINNER 0x72
@@ -186,6 +190,25 @@ namespace cryptonote
END_SERIALIZE()
};
struct tx_extra_offshore
{
std::string data;
BEGIN_SERIALIZE()
FIELD(data)
END_SERIALIZE()
};
struct tx_extra_memo
{
// Actual memo data as string
std::string data;
BEGIN_SERIALIZE()
FIELD(data)
END_SERIALIZE()
};
struct tx_extra_service_node_winner
{
crypto::public_key m_service_node_key;
@@ -301,6 +324,8 @@ namespace cryptonote
tx_extra_merge_mining_tag,
tx_extra_additional_pub_keys,
tx_extra_mysterious_minergate,
tx_extra_offshore,
tx_extra_memo,
tx_extra_service_node_pubkey,
tx_extra_service_node_register,
tx_extra_service_node_contributor,
@@ -321,6 +346,8 @@ VARIANT_TAG(binary_archive, cryptonote::tx_extra_nonce, TX_EX
VARIANT_TAG(binary_archive, cryptonote::tx_extra_merge_mining_tag, TX_EXTRA_MERGE_MINING_TAG);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_additional_pub_keys, TX_EXTRA_TAG_ADDITIONAL_PUBKEYS);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_mysterious_minergate, TX_EXTRA_MYSTERIOUS_MINERGATE_TAG);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_offshore, TX_EXTRA_TAG_OFFSHORE);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_memo, TX_EXTRA_TAG_MEMO);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_service_node_register, TX_EXTRA_TAG_SERVICE_NODE_REGISTER);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_service_node_deregister, TX_EXTRA_TAG_SERVICE_NODE_DEREGISTER);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_service_node_contributor, TX_EXTRA_TAG_SERVICE_NODE_CONTRIBUTOR);
+1 -1
View File
@@ -145,7 +145,7 @@ NAN_METHOD(convert_blob) { // (parentBlockBuffer, cnBlobType)
block b = AUTO_VAL_INIT(b);
b.set_blob_type(blob_type);
if (!parse_and_validate_block_from_blob(input, b)) return THROW_ERROR_EXCEPTION("Failed to parse block");
if (!parse_and_validate_block_from_blob(input, b)) return THROW_ERROR_EXCEPTION("Failed to parse block 2");
if (blob_type == BLOB_TYPE_FORKNOTE2) {
block parent_block;
+143
View File
@@ -0,0 +1,143 @@
// Copyright (c) 2021, Haven Protocol
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. 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.
//
// 3. Neither the name of the copyright holder 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 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.
#pragma once
#include <string>
#include <vector>
namespace offshore {
const std::vector<std::string> ASSET_TYPES = {"XHV", "XAG", "XAU", "XAUD", "XBTC", "XCAD", "XCHF", "XCNY", "XEUR", "XGBP", "XJPY", "XNOK", "XNZD", "XUSD"};
class asset_type_counts
{
public:
// Fields
uint64_t XHV;
uint64_t XAG;
uint64_t XAU;
uint64_t XAUD;
uint64_t XBTC;
uint64_t XCAD;
uint64_t XCHF;
uint64_t XCNY;
uint64_t XEUR;
uint64_t XGBP;
uint64_t XJPY;
uint64_t XNOK;
uint64_t XNZD;
uint64_t XUSD;
asset_type_counts() noexcept
: XHV(0)
, XAG(0)
, XAU(0)
, XAUD(0)
, XBTC(0)
, XCAD(0)
, XCHF(0)
, XCNY(0)
, XEUR(0)
, XGBP(0)
, XJPY(0)
, XNOK(0)
, XNZD(0)
, XUSD(0)
{
}
uint64_t operator[](const std::string asset_type) const noexcept
{
if (asset_type == "XHV") {
return XHV;
} else if (asset_type == "XUSD") {
return XUSD;
} else if (asset_type == "XAG") {
return XAG;
} else if (asset_type == "XAU") {
return XAU;
} else if (asset_type == "XAUD") {
return XAUD;
} else if (asset_type == "XBTC") {
return XBTC;
} else if (asset_type == "XCAD") {
return XCAD;
} else if (asset_type == "XCHF") {
return XCHF;
} else if (asset_type == "XCNY") {
return XCNY;
} else if (asset_type == "XEUR") {
return XEUR;
} else if (asset_type == "XGBP") {
return XGBP;
} else if (asset_type == "XJPY") {
return XJPY;
} else if (asset_type == "XNOK") {
return XNOK;
} else if (asset_type == "XNZD") {
return XNZD;
}
return 0;
}
void add(const std::string asset_type, const uint64_t val)
{
if (asset_type == "XHV") {
XHV += val;
} else if (asset_type == "XUSD") {
XUSD += val;
} else if (asset_type == "XAG") {
XAG += val;
} else if (asset_type == "XAU") {
XAU += val;
} else if (asset_type == "XAUD") {
XAUD += val;
} else if (asset_type == "XBTC") {
XBTC += val;
} else if (asset_type == "XCAD") {
XCAD += val;
} else if (asset_type == "XCHF") {
XCHF += val;
} else if (asset_type == "XCNY") {
XCNY += val;
} else if (asset_type == "XEUR") {
XEUR += val;
} else if (asset_type == "XGBP") {
XGBP += val;
} else if (asset_type == "XJPY") {
XJPY += val;
} else if (asset_type == "XNOK") {
XNOK += val;
} else if (asset_type == "XNZD") {
XNZD += val;
}
}
};
}
+157 -44
View File
@@ -29,20 +29,10 @@
#include "pricing_record.h"
#include <cstring>
#include <openssl/bio.h>
#include <openssl/crypto.h>
#include <openssl/ecdsa.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/ssl.h>
#include "serialization/keyvalue_serialization.h"
#include "storages/portable_storage.h"
#include "string_tools.h"
namespace offshore
{
@@ -66,6 +56,7 @@ namespace offshore
uint64_t unused1;
uint64_t unused2;
uint64_t unused3;
uint64_t timestamp;
std::string signature;
BEGIN_KV_SERIALIZE_MAP()
@@ -85,6 +76,7 @@ namespace offshore
KV_SERIALIZE(unused1)
KV_SERIALIZE(unused2)
KV_SERIALIZE(unused3)
KV_SERIALIZE(timestamp)
KV_SERIALIZE(signature)
END_KV_SERIALIZE_MAP()
};
@@ -107,6 +99,7 @@ namespace offshore
, unused1(0)
, unused2(0)
, unused3(0)
, timestamp(0)
{
std::memset(signature, 0, sizeof(signature));
}
@@ -133,9 +126,10 @@ namespace offshore
unused1 = in.unused1;
unused2 = in.unused2;
unused3 = in.unused3;
timestamp = in.timestamp;
for (unsigned int i = 0; i < in.signature.length(); i += 2) {
std::string byteString = in.signature.substr(i, 2);
signature[i>>1] = (char) strtol(byteString.c_str(), NULL, 16);
std::string byteString = in.signature.substr(i, 2);
signature[i>>1] = (char) strtol(byteString.c_str(), NULL, 16);
}
return true;
}
@@ -152,7 +146,7 @@ namespace offshore
ss << std::hex << std::setw(2) << std::setfill('0') << (0xff & signature[i]);
sig_hex += ss.str();
}
const pr_serialized out{xAG,xAU,xAUD,xBTC,xCAD,xCHF,xCNY,xEUR,xGBP,xJPY,xNOK,xNZD,xUSD,unused1,unused2,unused3,sig_hex};
const pr_serialized out{xAG,xAU,xAUD,xBTC,xCAD,xCHF,xCNY,xEUR,xGBP,xJPY,xNOK,xNZD,xUSD,unused1,unused2,unused3,timestamp,sig_hex};
return out.store(dest, hparent);
}
@@ -173,6 +167,7 @@ namespace offshore
, unused1(orig.unused1)
, unused2(orig.unused2)
, unused3(orig.unused3)
, timestamp(orig.timestamp)
{
std::memcpy(signature, orig.signature, sizeof(signature));
}
@@ -195,10 +190,46 @@ namespace offshore
unused1 = orig.unused1;
unused2 = orig.unused2;
unused3 = orig.unused3;
timestamp = orig.timestamp;
::memcpy(signature, orig.signature, sizeof(signature));
return *this;
}
uint64_t pricing_record::operator[](const std::string& asset_type) const
{
if (asset_type == "XHV") {
return 1000000000000;
} else if (asset_type == "XUSD") {
return unused1;
} else if (asset_type == "XAG") {
return xAG;
} else if (asset_type == "XAU") {
return xAU;
} else if (asset_type == "XAUD") {
return xAUD;
} else if (asset_type == "XBTC") {
return xBTC;
} else if (asset_type == "XCAD") {
return xCAD;
} else if (asset_type == "XCHF") {
return xCHF;
} else if (asset_type == "XCNY") {
return xCNY;
} else if (asset_type == "XEUR") {
return xEUR;
} else if (asset_type == "XGBP") {
return xGBP;
} else if (asset_type == "XJPY") {
return xJPY;
} else if (asset_type == "XNOK") {
return xNOK;
} else if (asset_type == "XNZD") {
return xNZD;
} else {
CHECK_AND_ASSERT_THROW_MES(false, "Asset type doesn't exist in pricing record!");
}
}
bool pricing_record::equal(const pricing_record& other) const noexcept
{
return ((xAG == other.xAG) &&
@@ -217,19 +248,31 @@ namespace offshore
(unused1 == other.unused1) &&
(unused2 == other.unused2) &&
(unused3 == other.unused3) &&
(timestamp == other.timestamp) &&
!::memcmp(signature, other.signature, sizeof(signature)));
}
bool pricing_record::verifySignature() const noexcept
bool pricing_record::empty() const noexcept
{
// Sanity check - accept empty pricing records
unsigned char test_sig[64];
std::memset(test_sig, 0, sizeof(test_sig));
if (std::memcmp(test_sig, signature, sizeof(signature)) == 0) {
return true;
}
const pricing_record empty_pr = offshore::pricing_record();
return (*this).equal(empty_pr);
}
bool pricing_record::verifySignature(const std::string& public_key) const
{
CHECK_AND_ASSERT_THROW_MES(!public_key.empty(), "Pricing record verification failed. NULL public key. PK Size: " << public_key.size()); // TODO: is this necessary or the one below already covers this case, meannin it will produce empty pubkey?
// extract the key
EVP_PKEY* pubkey;
BIO* bio = BIO_new_mem_buf(public_key.c_str(), public_key.size());
if (!bio) {
return false;
}
pubkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
BIO_free(bio);
CHECK_AND_ASSERT_THROW_MES(pubkey != NULL, "Pricing record verification failed. NULL public key.");
// Convert our internal 64-byte binary representation into 128-byte hex string
std::string sig_hex;
for (unsigned int i=0; i<64; i++) {
@@ -284,6 +327,8 @@ namespace offshore
oss << ",\"unused1\":" << unused1;
oss << ",\"unused2\":" << unused2;
oss << ",\"unused3\":" << unused3;
if (timestamp > 0)
oss << ",\"timestamp\":" << timestamp;
oss << "}";
std::string message = oss.str();
@@ -295,39 +340,24 @@ namespace offshore
compact += (byte);
}
// HERE BE DRAGONS!!!
// NEAC: the public key should be in a file
static const char public_key[] = "-----BEGIN PUBLIC KEY-----\n"
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5YBxWx1AZCA9jTUk8Pr2uZ9jpfRt\n"
"KWv3Vo1/Gny+1vfaxsXhBQiG1KlHkafNGarzoL0WHW4ocqaaqF5iv8i35A==\n"
"-----END PUBLIC KEY-----\n";
// LAND AHOY!!!
// Grab the public key and make it usable
BIO* bio = BIO_new_mem_buf(public_key, (int)sizeof(public_key));
assert(bio != NULL);
EVP_PKEY* pubkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
BIO_free(bio);
assert(pubkey != NULL);
// Create a verify digest from the message
EVP_MD_CTX *ctx = EVP_MD_CTX_create();
int ret = 0;
if (ctx) {
ret=EVP_DigestVerifyInit(ctx, NULL, EVP_sha256(), NULL, pubkey);
ret = EVP_DigestVerifyInit(ctx, NULL, EVP_sha256(), NULL, pubkey);
if (ret == 1) {
ret=EVP_DigestVerifyUpdate(ctx, message.data(), message.length());
if (ret == 1) {
ret=EVP_DigestVerifyFinal(ctx, (const unsigned char *)compact.data(), compact.length());
}
ret = EVP_DigestVerifyUpdate(ctx, message.data(), message.length());
if (ret == 1) {
ret = EVP_DigestVerifyFinal(ctx, (const unsigned char *)compact.data(), compact.length());
}
}
}
// Cleanup the context we created
EVP_MD_CTX_destroy(ctx);
// Cleanup the openssl stuff
EVP_PKEY_free(pubkey);
if (ret == 1)
return true;
@@ -336,4 +366,87 @@ namespace offshore
return false;
}
void pricing_record::set_for_height_821428() {
const std::string pr_821428 = "9b3f6f2f8f0000003d620e1202000000be71be2555120000b8627010000000000000000000000000ea0885b2270d00000000000000000000f797ff9be00b0000ddbdb005270a0000fc90cfe02b01060000000000000000000000000000000000d0a28224000e000000d643be960e0000002e8bb6a40e000000f8a817f80d00002f5d27d45cdbfbac3d0f6577103f68de30895967d7562fbd56c161ae90130f54301b1ea9d5fd062f37dac75c3d47178bc6f149d21da1ff0e8430065cb762b93a";
this->xAG = 614976143259;
this->xAU = 8892867133;
this->xAUD = 20156914758078;
this->xBTC = 275800760;
this->xCAD = 0;
this->xCHF = 14464149948650;
this->xCNY = 0;
this->xEUR = 13059317798903;
this->xGBP = 11162715471325;
this->xJPY = 1690137827184892;
this->xNOK = 0;
this->xNZD = 0;
this->xUSD = 15393775330000;
this->unused1 = 16040600000000;
this->unused2 = 16100600000000;
this->unused3 = 15359200000000;
this->timestamp = 0;
std::string sig = "2f5d27d45cdbfbac3d0f6577103f68de30895967d7562fbd56c161ae90130f54301b1ea9d5fd062f37dac75c3d47178bc6f149d21da1ff0e8430065cb762b93a";
int j=0;
for (unsigned int i = 0; i < sig.size(); i += 2) {
std::string byteString = sig.substr(i, 2);
this->signature[j++] = (char) strtol(byteString.c_str(), NULL, 16);
}
}
// overload for pr validation for block
bool pricing_record::valid(uint32_t hf_version, uint64_t bl_timestamp, uint64_t last_bl_timestamp) const
{
// check for empty pr
if (hf_version >= HF_VERSION_XASSET_FEES_V2) {
if (this->empty())
return true;
} else {
unsigned char test_sig[64];
std::memset(test_sig, 0, sizeof(test_sig));
if (std::memcmp(test_sig, this->signature, sizeof(this->signature)) == 0) {
return true;
}
}
// Oracle public keys
std::string const mainnet_public_key = "-----BEGIN PUBLIC KEY-----\n"
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5YBxWx1AZCA9jTUk8Pr2uZ9jpfRt\n"
"KWv3Vo1/Gny+1vfaxsXhBQiG1KlHkafNGarzoL0WHW4ocqaaqF5iv8i35A==\n"
"-----END PUBLIC KEY-----\n";
std::string const testnet_public_key = "-----BEGIN PUBLIC KEY-----\n"
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEtWqvQh7OdXrdgXcDeBMRVfLWTW3F\n"
"wByeoVJFBfZymScJIJl46j66xG6ngnyj4ai4/QPFnSZ1I9jjMRlTWC4EPA==\n"
"-----END PUBLIC KEY-----\n";
std::string const stagenet_public_key = "-----BEGIN PUBLIC KEY-----\n"
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEtWqvQh7OdXrdgXcDeBMRVfLWTW3F\n"
"wByeoVJFBfZymScJIJl46j66xG6ngnyj4ai4/QPFnSZ1I9jjMRlTWC4EPA==\n"
"-----END PUBLIC KEY-----\n";
// Comment out all but 1 of the following lines to select the correct Oracle PK
std::string const public_key = mainnet_public_key;
//std::string const public_key = testnet_public_key;
//std::string const public_key = stagenet_public_key;
// verify the signature
if (!verifySignature(public_key)) {
LOG_ERROR("Invalid pricing record signature.");
return false;
}
// valiadte the timestmap
if (hf_version >= HF_VERSION_XASSET_FEES_V2) {
if (this->timestamp > bl_timestamp + PRICING_RECORD_VALID_TIME_DIFF_FROM_BLOCK) {
LOG_ERROR("Pricing record timestamp is too far in the future.");
return false;
}
if (this->timestamp <= last_bl_timestamp) {
LOG_ERROR("Pricing record timestamp is too old.");
return false;
}
}
return true;
}
}
+112 -27
View File
@@ -30,7 +30,21 @@
#pragma once
#include "common/pod-class.h"
#include <openssl/bio.h>
#include <openssl/crypto.h>
#include <openssl/ecdsa.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/ssl.h>
#include <cstdint>
#include <string>
#include <cstring>
#include "cryptonote_config.h"
#include "crypto/hash.h"
namespace epee
{
@@ -69,9 +83,61 @@ namespace offshore
class pricing_record
{
public:
public:
// Fields
// Fields
uint64_t xAG;
uint64_t xAU;
uint64_t xAUD;
uint64_t xBTC;
uint64_t xCAD;
uint64_t xCHF;
uint64_t xCNY;
uint64_t xEUR;
uint64_t xGBP;
uint64_t xJPY;
uint64_t xNOK;
uint64_t xNZD;
uint64_t xUSD;
uint64_t unused1;
uint64_t unused2;
uint64_t unused3;
uint64_t timestamp;
unsigned char signature[64];
// Default c'tor
pricing_record() noexcept;
//! Load from epee p2p format
bool _load(epee::serialization::portable_storage& src, epee::serialization::section* hparent);
//! Store in epee p2p format
bool store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const;
pricing_record(const pricing_record& orig) noexcept;
~pricing_record() = default;
void set_for_height_821428();
bool equal(const pricing_record& other) const noexcept;
bool empty() const noexcept;
bool verifySignature(const std::string& public_key) const;
bool valid(uint32_t hf_version, uint64_t bl_timestamp, uint64_t last_bl_timestamp) const;
pricing_record& operator=(const pricing_record& orig) noexcept;
uint64_t operator[](const std::string& asset_type) const;
};
inline bool operator==(const pricing_record& a, const pricing_record& b) noexcept
{
return a.equal(b);
}
inline bool operator!=(const pricing_record& a, const pricing_record& b) noexcept
{
return !a.equal(b);
}
// did not have a timestamp
class pricing_record_v1
{
public:
uint64_t xAG;
uint64_t xAU;
uint64_t xAUD;
@@ -90,31 +156,50 @@ namespace offshore
uint64_t unused3;
unsigned char signature[64];
// Default c'tor
pricing_record() noexcept;
//! Load from epee p2p format
bool _load(epee::serialization::portable_storage& src, epee::serialization::section* hparent);
//! Store in epee p2p format
bool store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const;
pricing_record(const pricing_record& orig) noexcept;
~pricing_record() = default;
pricing_record& operator=(const pricing_record& orig) noexcept;
bool equal(const pricing_record& other) const noexcept;
bool write_to_pr(offshore::pricing_record &pr)
{
pr.xAG = xAG;
pr.xAU = xAU;
pr.xAUD = xAUD;
pr.xBTC = xBTC;
pr.xCAD = xCAD;
pr.xCHF = xCHF;
pr.xCNY = xCNY;
pr.xEUR = xEUR;
pr.xGBP = xGBP;
pr.xJPY = xJPY;
pr.xNOK = xNOK;
pr.xNZD = xNZD;
pr.xUSD = xUSD;
pr.unused1 = unused1;
pr.unused2 = unused2;
pr.unused3 = unused3;
pr.timestamp = 0;
::memcpy(pr.signature, signature, sizeof(pr.signature));
return true;
};
bool verifySignature() const noexcept;
bool read_from_pr(offshore::pricing_record &pr)
{
xAG = pr.xAG;
xAU = pr.xAU;
xAUD = pr.xAUD;
xBTC = pr.xBTC;
xCAD = pr.xCAD;
xCHF = pr.xCHF;
xCNY = pr.xCNY;
xEUR = pr.xEUR;
xGBP = pr.xGBP;
xJPY = pr.xJPY;
xNOK = pr.xNOK;
xNZD = pr.xNZD;
xUSD = pr.xUSD;
unused1 = pr.unused1;
unused2 = pr.unused2;
unused3 = pr.unused3;
::memcpy(signature, pr.signature, sizeof(signature));
return true;
};
};
inline bool operator==(const pricing_record& a, const pricing_record& b) noexcept
{
return a.equal(b);
}
inline bool operator!=(const pricing_record& a, const pricing_record& b) noexcept
{
return !a.equal(b);
}
} // offshore
} // offshore
+325 -134
View File
@@ -48,10 +48,9 @@ extern "C" {
#include "hex.h"
#include "span.h"
#include "memwipe.h"
#include "serialization/vector.h"
#include "serialization/debug_archive.h"
#include "serialization/binary_archive.h"
#include "serialization/json_archive.h"
//Define this flag when debugging to get additional info on the console
@@ -88,6 +87,8 @@ namespace rct {
typedef std::vector<key> keyV; //vector of keys
typedef std::vector<keyV> keyM; //matrix of keys (indexed by column first)
static key null_key = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
//containers For CT operations
//if it's representing a private ctkey then "dest" contains the secret key of the address
// while "mask" contains a where C = aG + bH is CT pedersen commitment and b is the amount
@@ -106,6 +107,8 @@ namespace rct {
key L;
key R;
key ki;
~multisig_kLRki() { memwipe(&k, sizeof(k)); }
};
struct multisig_out {
@@ -235,11 +238,48 @@ namespace rct {
END_SERIALIZE()
};
struct BulletproofPlus
{
rct::keyV V;
rct::key A, A1, B;
rct::key r1, s1, d1;
rct::keyV L, R;
BulletproofPlus() {}
BulletproofPlus(const rct::key &V, const rct::key &A, const rct::key &A1, const rct::key &B, const rct::key &r1, const rct::key &s1, const rct::key &d1, const rct::keyV &L, const rct::keyV &R):
V({V}), A(A), A1(A1), B(B), r1(r1), s1(s1), d1(d1), L(L), R(R) {}
BulletproofPlus(const rct::keyV &V, const rct::key &A, const rct::key &A1, const rct::key &B, const rct::key &r1, const rct::key &s1, const rct::key &d1, const rct::keyV &L, const rct::keyV &R):
V(V), A(A), A1(A1), B(B), r1(r1), s1(s1), d1(d1), L(L), R(R) {}
bool operator==(const BulletproofPlus &other) const { return V == other.V && A == other.A && A1 == other.A1 && B == other.B && r1 == other.r1 && s1 == other.s1 && d1 == other.d1 && L == other.L && R == other.R; }
BEGIN_SERIALIZE_OBJECT()
// Commitments aren't saved, they're restored via outPk
// FIELD(V)
FIELD(A)
FIELD(A1)
FIELD(B)
FIELD(r1)
FIELD(s1)
FIELD(d1)
FIELD(L)
FIELD(R)
if (L.empty() || L.size() != R.size())
return false;
END_SERIALIZE()
};
size_t n_bulletproof_amounts(const Bulletproof &proof);
size_t n_bulletproof_max_amounts(const Bulletproof &proof);
size_t n_bulletproof_amounts(const std::vector<Bulletproof> &proofs);
size_t n_bulletproof_max_amounts(const std::vector<Bulletproof> &proofs);
size_t n_bulletproof_plus_amounts(const BulletproofPlus &proof);
size_t n_bulletproof_plus_max_amounts(const BulletproofPlus &proof);
size_t n_bulletproof_plus_amounts(const std::vector<BulletproofPlus> &proofs);
size_t n_bulletproof_plus_max_amounts(const std::vector<BulletproofPlus> &proofs);
//A container to hold all signatures necessary for RingCT
// rangeSigs holds all the rangeproof data of a transaction
// MG holds the MLSAG signature of a transaction
@@ -254,124 +294,268 @@ namespace rct {
RCTTypeBulletproof = 3,
RCTTypeBulletproof2 = 4,
RCTTypeCLSAG = 5,
RCTTypeCLSAGN = 6,
RCTTypeHaven2 = 7, // Add public mask sum terms, remove extraneous fields (txnFee_usd,txnFee_xasset,txnOffshoreFee_usd,txnOffshoreFee_xasset)
RCTTypeHaven3 = 8, // Add public mask sum term for collateral
RCTTypeBulletproofPlus = 9,
};
enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof, RangeProofPaddedBulletproof };
struct RCTConfig {
RangeProofType range_proof_type;
int bp_version;
BEGIN_SERIALIZE_OBJECT()
VERSION_FIELD(0)
VARINT_FIELD(range_proof_type)
VARINT_FIELD(bp_version)
END_SERIALIZE()
};
struct rctSigBase {
uint8_t type;
key message;
ctkeyM mixRing; //the set of all pubkeys / copy
//pairs that you mix with
keyV pseudoOuts; //C - for simple rct
std::vector<ecdhTuple> ecdhInfo;
ctkeyV outPk;
ctkeyV outPk_usd;
xmr_amount txnFee; // contains b
xmr_amount txnFee_usd;
xmr_amount txnOffshoreFee;
xmr_amount txnOffshoreFee_usd;
uint8_t type;
key message;
ctkeyM mixRing; //the set of all pubkeys / copy
//pairs that you mix with
keyV pseudoOuts; //C - for simple rct
std::vector<ecdhTuple> ecdhInfo;
ctkeyV outPk;
ctkeyV outPk_usd;
ctkeyV outPk_xasset;
xmr_amount txnFee = 0; // contains b
xmr_amount txnFee_usd = 0;
xmr_amount txnFee_xasset = 0;
xmr_amount txnOffshoreFee = 0;
xmr_amount txnOffshoreFee_usd = 0;
xmr_amount txnOffshoreFee_xasset = 0;
keyV maskSums; // contains 2 or 3 elements. 1. is the sum of masks of inputs. 2. is the sum of masks of change outputs. 3. mask of the col output.
key p_r;
template<bool W, template <bool> class Archive>
bool serialize_rctsig_base(Archive<W> &ar, size_t inputs, size_t outputs)
template<bool W, template <bool> class Archive>
bool serialize_rctsig_base(Archive<W> &ar, size_t inputs, size_t outputs)
{
FIELD(type)
if (type == RCTTypeNull)
return ar.stream().good();
if (type != RCTTypeBulletproofPlus)
return serialize_rctsig_base_old(ar, inputs, outputs);
VARINT_FIELD(txnFee)
VARINT_FIELD(txnOffshoreFee)
// inputs/outputs not saved, only here for serialization help
// FIELD(message) - not serialized, it can be reconstructed
// FIELD(mixRing) - not serialized, it can be reconstructed
ar.tag("ecdhInfo");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, ecdhInfo);
if (ecdhInfo.size() != outputs)
return false;
for (size_t i = 0; i < outputs; ++i)
{
FIELD(type)
if (type == RCTTypeNull)
return ar.stream().good();
if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG)
ar.begin_object();
if (!typename Archive<W>::is_saving())
memset(ecdhInfo[i].amount.bytes, 0, sizeof(ecdhInfo[i].amount.bytes));
crypto::hash8 &amount = (crypto::hash8&)ecdhInfo[i].amount;
FIELD(amount);
ar.end_object();
if (outputs - i > 1)
ar.delimit_array();
}
ar.end_array();
ar.tag("outPk");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, outPk);
if (outPk.size() != outputs)
return false;
for (size_t i = 0; i < outputs; ++i)
{
FIELDS(outPk[i].mask)
if (outputs - i > 1)
ar.delimit_array();
}
ar.end_array();
// if txnOffshoreFee is not 0, it is a conversion tx
if (txnOffshoreFee) {
ar.tag("maskSums");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(3, maskSums);
if (maskSums.size() != 3)
return false;
VARINT_FIELD(txnFee)
if (type == RCTTypeCLSAG)
FIELDS(maskSums[0])
ar.delimit_array();
FIELDS(maskSums[1])
ar.delimit_array();
FIELDS(maskSums[2])
ar.end_array();
}
if (crypto_verify_32(p_r.bytes, null_key.bytes))
FIELD(p_r)
return ar.stream().good();
}
template<bool W, template <bool> class Archive>
bool serialize_rctsig_base_old(Archive<W> &ar, size_t inputs, size_t outputs)
{
if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG && type != RCTTypeCLSAGN && type != RCTTypeHaven2 && type != RCTTypeHaven3)
return false;
VARINT_FIELD(txnFee)
if (type == RCTTypeHaven2 || type == RCTTypeHaven3) {
// serialize offshore fee
VARINT_FIELD(txnOffshoreFee)
} else if (type == RCTTypeCLSAG || type == RCTTypeCLSAGN) {
VARINT_FIELD(txnFee_usd)
if (type == RCTTypeCLSAGN)
{
VARINT_FIELD(txnFee_usd)
VARINT_FIELD(txnOffshoreFee)
VARINT_FIELD(txnOffshoreFee_usd)
} else {
txnFee_usd = 0;
txnOffshoreFee = 0;
txnOffshoreFee_usd = 0;
}
// inputs/outputs not saved, only here for serialization help
// FIELD(message) - not serialized, it can be reconstructed
// FIELD(mixRing) - not serialized, it can be reconstructed
if (type == RCTTypeSimple) // moved to prunable with bulletproofs
VARINT_FIELD(txnFee_xasset)
}
VARINT_FIELD(txnOffshoreFee)
VARINT_FIELD(txnOffshoreFee_usd)
if (type == RCTTypeCLSAGN)
{
ar.tag("pseudoOuts");
VARINT_FIELD(txnOffshoreFee_xasset)
}
} else {
txnFee_usd = 0;
txnFee_xasset = 0;
txnOffshoreFee = 0;
txnOffshoreFee_usd = 0;
txnOffshoreFee_xasset = 0;
}
// inputs/outputs not saved, only here for serialization help
// FIELD(message) - not serialized, it can be reconstructed
// FIELD(mixRing) - not serialized, it can be reconstructed
if (type == RCTTypeSimple) // moved to prunable with bulletproofs
{
ar.tag("pseudoOuts");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, pseudoOuts);
if (pseudoOuts.size() != inputs)
return false;
for (size_t i = 0; i < inputs; ++i)
{
FIELDS(pseudoOuts[i])
if (inputs - i > 1)
ar.delimit_array();
}
ar.end_array();
}
ar.tag("ecdhInfo");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, ecdhInfo);
if (ecdhInfo.size() != outputs)
return false;
for (size_t i = 0; i < outputs; ++i)
{
if (type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2 || type == RCTTypeHaven3)
{
ar.begin_object();
if (!typename Archive<W>::is_saving())
memset(ecdhInfo[i].amount.bytes, 0, sizeof(ecdhInfo[i].amount.bytes));
crypto::hash8 &amount = (crypto::hash8&)ecdhInfo[i].amount;
FIELD(amount);
ar.end_object();
}
else
{
FIELDS(ecdhInfo[i])
}
if (outputs - i > 1)
ar.delimit_array();
}
ar.end_array();
ar.tag("outPk");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, outPk);
if (outPk.size() != outputs)
return false;
for (size_t i = 0; i < outputs; ++i)
{
FIELDS(outPk[i].mask)
if (outputs - i > 1)
ar.delimit_array();
}
ar.end_array();
// if txnOffshoreFee is not 0, it is a conversion tx
if (type == RCTTypeHaven3 && txnOffshoreFee) {
ar.tag("maskSums");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(3, maskSums);
if (maskSums.size() != 3)
return false;
FIELDS(maskSums[0])
ar.delimit_array();
FIELDS(maskSums[1])
ar.delimit_array();
FIELDS(maskSums[2])
ar.end_array();
} else if (type == RCTTypeHaven2) {
ar.tag("maskSums");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(2, maskSums);
if (maskSums.size() != 2)
return false;
FIELDS(maskSums[0])
ar.delimit_array();
FIELDS(maskSums[1])
ar.end_array();
} else {
if ((type == RCTTypeCLSAG) || (type == RCTTypeCLSAGN))
{
ar.tag("outPk_usd");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, pseudoOuts);
if (pseudoOuts.size() != inputs)
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, outPk_usd);
if (outPk_usd.size() != outputs)
return false;
for (size_t i = 0; i < inputs; ++i)
for (size_t i = 0; i < outputs; ++i)
{
FIELDS(pseudoOuts[i])
if (inputs - i > 1)
ar.delimit_array();
FIELDS(outPk_usd[i].mask)
if (outputs - i > 1)
ar.delimit_array();
}
ar.end_array();
}
ar.tag("ecdhInfo");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, ecdhInfo);
if (ecdhInfo.size() != outputs)
return false;
for (size_t i = 0; i < outputs; ++i)
if (type == RCTTypeCLSAGN)
{
if (type == RCTTypeBulletproof2 || type == RCTTypeCLSAG)
ar.tag("outPk_xasset");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, outPk_xasset);
if (outPk_xasset.size() != outputs)
return false;
for (size_t i = 0; i < outputs; ++i)
{
ar.begin_object();
if (!typename Archive<W>::is_saving())
memset(ecdhInfo[i].amount.bytes, 0, sizeof(ecdhInfo[i].amount.bytes));
crypto::hash8 &amount = (crypto::hash8&)ecdhInfo[i].amount;
FIELD(amount);
ar.end_object();
FIELDS(outPk_xasset[i].mask)
if (outputs - i > 1)
ar.delimit_array();
}
else
{
FIELDS(ecdhInfo[i])
}
if (outputs - i > 1)
ar.delimit_array();
ar.end_array();
}
ar.end_array();
}
return ar.stream().good();
}
ar.tag("outPk");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, outPk);
if (outPk.size() != outputs)
return false;
for (size_t i = 0; i < outputs; ++i)
{
FIELDS(outPk[i].mask)
if (outputs - i > 1)
ar.delimit_array();
}
ar.end_array();
if (type == RCTTypeCLSAG)
{
ar.tag("outPk_usd");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, outPk_usd);
if (outPk_usd.size() != outputs)
return false;
for (size_t i = 0; i < outputs; ++i)
{
FIELDS(outPk_usd[i].mask)
if (outputs - i > 1)
ar.delimit_array();
}
ar.end_array();
}
return ar.stream().good();
}
BEGIN_SERIALIZE_OBJECT()
FIELD(type)
FIELD(message)
FIELD(mixRing)
FIELD(pseudoOuts)
FIELD(ecdhInfo)
FIELD(outPk)
VARINT_FIELD(txnFee)
VARINT_FIELD(txnOffshoreFee)
FIELD(maskSums)
END_SERIALIZE()
};
struct rctSigPrunable {
std::vector<rangeSig> rangeSigs;
std::vector<Bulletproof> bulletproofs;
std::vector<BulletproofPlus> bulletproofs_plus;
std::vector<mgSig> MGs; // simple rct has N, full has 1
std::vector<clsag> CLSAGs;
keyV pseudoOuts; //C - for simple rct
@@ -380,14 +564,39 @@ namespace rct {
template<bool W, template <bool> class Archive>
bool serialize_rctsig_prunable(Archive<W> &ar, uint8_t type, size_t inputs, size_t outputs, size_t mixin)
{
if (inputs >= 0xffffffff)
return false;
if (outputs >= 0xffffffff)
return false;
if (mixin >= 0xffffffff)
return false;
if (type == RCTTypeNull)
return ar.stream().good();
if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG)
if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG && type != RCTTypeCLSAGN && type != RCTTypeHaven2 && type != RCTTypeHaven3 && type != RCTTypeBulletproofPlus)
return false;
if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG)
if (type == RCTTypeBulletproofPlus)
{
uint32_t nbp = bulletproofs_plus.size();
VARINT_FIELD(nbp)
ar.tag("bpp");
ar.begin_array();
if (nbp > outputs)
return false;
PREPARE_CUSTOM_VECTOR_SERIALIZATION(nbp, bulletproofs_plus);
for (size_t i = 0; i < nbp; ++i)
{
FIELDS(bulletproofs_plus[i])
if (nbp - i > 1)
ar.delimit_array();
}
if (n_bulletproof_plus_max_amounts(bulletproofs_plus) < outputs)
return false;
ar.end_array();
}
else if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2 || type == RCTTypeHaven3)
{
uint32_t nbp = bulletproofs.size();
if (type == RCTTypeBulletproof2 || type == RCTTypeCLSAG)
if (type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2 || type == RCTTypeHaven3)
VARINT_FIELD(nbp)
else
FIELD(nbp)
@@ -422,7 +631,7 @@ namespace rct {
ar.end_array();
}
if (type == RCTTypeCLSAG)
if (type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2 || type == RCTTypeHaven3 || type == RCTTypeBulletproofPlus)
{
ar.tag("CLSAGs");
ar.begin_array();
@@ -513,7 +722,7 @@ namespace rct {
}
ar.end_array();
}
if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG)
if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2 || type == RCTTypeHaven3 || type == RCTTypeBulletproofPlus)
{
ar.tag("pseudoOuts");
ar.begin_array();
@@ -531,19 +740,32 @@ namespace rct {
return ar.stream().good();
}
BEGIN_SERIALIZE_OBJECT()
FIELD(rangeSigs)
FIELD(bulletproofs)
FIELD(bulletproofs_plus)
FIELD(MGs)
FIELD(CLSAGs)
FIELD(pseudoOuts)
END_SERIALIZE()
};
struct rctSig: public rctSigBase {
rctSigPrunable p;
keyV& get_pseudo_outs()
{
return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG ? p.pseudoOuts : pseudoOuts;
return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2 || type == RCTTypeHaven3 || type == RCTTypeBulletproofPlus ? p.pseudoOuts : pseudoOuts;
}
keyV const& get_pseudo_outs() const
{
return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG ? p.pseudoOuts : pseudoOuts;
return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG || type == RCTTypeCLSAGN || type == RCTTypeHaven2 || type == RCTTypeHaven3 || type == RCTTypeBulletproofPlus ? p.pseudoOuts : pseudoOuts;
}
BEGIN_SERIALIZE_OBJECT()
FIELDS((rctSigBase&)*this)
FIELD(p)
END_SERIALIZE()
};
//other basepoint H = toPoint(cn_fast_hash(G)), G the basepoint
@@ -649,7 +871,9 @@ namespace rct {
bool is_rct_simple(int type);
bool is_rct_bulletproof(int type);
bool is_rct_bulletproof_plus(int type);
bool is_rct_borromean(int type);
bool is_rct_clsag(int type);
static inline const rct::key &pk2rct(const crypto::public_key &pk) { return (const rct::key&)pk; }
static inline const rct::key &sk2rct(const crypto::secret_key &sk) { return (const rct::key&)sk; }
@@ -689,23 +913,6 @@ BLOB_SERIALIZER(rct::ctkey);
BLOB_SERIALIZER(rct::multisig_kLRki);
BLOB_SERIALIZER(rct::boroSig);
VARIANT_TAG(debug_archive, rct::key, "rct::key");
VARIANT_TAG(debug_archive, rct::key64, "rct::key64");
VARIANT_TAG(debug_archive, rct::keyV, "rct::keyV");
VARIANT_TAG(debug_archive, rct::keyM, "rct::keyM");
VARIANT_TAG(debug_archive, rct::ctkey, "rct::ctkey");
VARIANT_TAG(debug_archive, rct::ctkeyV, "rct::ctkeyV");
VARIANT_TAG(debug_archive, rct::ctkeyM, "rct::ctkeyM");
VARIANT_TAG(debug_archive, rct::ecdhTuple, "rct::ecdhTuple");
VARIANT_TAG(debug_archive, rct::mgSig, "rct::mgSig");
VARIANT_TAG(debug_archive, rct::rangeSig, "rct::rangeSig");
VARIANT_TAG(debug_archive, rct::boroSig, "rct::boroSig");
VARIANT_TAG(debug_archive, rct::rctSig, "rct::rctSig");
VARIANT_TAG(debug_archive, rct::Bulletproof, "rct::bulletproof");
VARIANT_TAG(debug_archive, rct::multisig_kLRki, "rct::multisig_kLRki");
VARIANT_TAG(debug_archive, rct::multisig_out, "rct::multisig_out");
VARIANT_TAG(debug_archive, rct::clsag, "rct::clsag");
VARIANT_TAG(binary_archive, rct::key, 0x90);
VARIANT_TAG(binary_archive, rct::key64, 0x91);
VARIANT_TAG(binary_archive, rct::keyV, 0x92);
@@ -722,22 +929,6 @@ VARIANT_TAG(binary_archive, rct::Bulletproof, 0x9c);
VARIANT_TAG(binary_archive, rct::multisig_kLRki, 0x9d);
VARIANT_TAG(binary_archive, rct::multisig_out, 0x9e);
VARIANT_TAG(binary_archive, rct::clsag, 0x9f);
VARIANT_TAG(json_archive, rct::key, "rct_key");
VARIANT_TAG(json_archive, rct::key64, "rct_key64");
VARIANT_TAG(json_archive, rct::keyV, "rct_keyV");
VARIANT_TAG(json_archive, rct::keyM, "rct_keyM");
VARIANT_TAG(json_archive, rct::ctkey, "rct_ctkey");
VARIANT_TAG(json_archive, rct::ctkeyV, "rct_ctkeyV");
VARIANT_TAG(json_archive, rct::ctkeyM, "rct_ctkeyM");
VARIANT_TAG(json_archive, rct::ecdhTuple, "rct_ecdhTuple");
VARIANT_TAG(json_archive, rct::mgSig, "rct_mgSig");
VARIANT_TAG(json_archive, rct::rangeSig, "rct_rangeSig");
VARIANT_TAG(json_archive, rct::boroSig, "rct_boroSig");
VARIANT_TAG(json_archive, rct::rctSig, "rct_rctSig");
VARIANT_TAG(json_archive, rct::Bulletproof, "rct_bulletproof");
VARIANT_TAG(json_archive, rct::multisig_kLRki, "rct_multisig_kLR");
VARIANT_TAG(json_archive, rct::multisig_out, "rct_multisig_out");
VARIANT_TAG(json_archive, rct::clsag, "rct_clsag");
VARIANT_TAG(binary_archive, rct::BulletproofPlus, 0xa0);
#endif /* RCTTYPES_H */
+77
View File
@@ -0,0 +1,77 @@
// Copyright (c) 2021, Haven Protocol
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. 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.
//
// 3. Neither the name of the copyright holder 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 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.
#pragma once
#include <string>
#include <vector>
namespace salvium_oracle {
const std::vector<std::string> ASSET_TYPES = {"SAL", "VSD", "BURN"};
class asset_type_counts
{
public:
// Fields
uint64_t SAL;
uint64_t VSD;
uint64_t BURN;
asset_type_counts() noexcept
: SAL(0)
, VSD(0)
, BURN(0)
{
}
uint64_t operator[](const std::string asset_type) const noexcept
{
if (asset_type == "SAL") {
return SAL;
} else if (asset_type == "VSD") {
return VSD;
} else if (asset_type == "BURN") {
return BURN;
}
return 0;
}
void add(const std::string asset_type, const uint64_t val)
{
if (asset_type == "SAL") {
SAL += val;
} else if (asset_type == "VSD") {
VSD += val;
} else if (asset_type == "BURN") {
BURN += val;
}
}
};
}
+246
View File
@@ -0,0 +1,246 @@
// Copyright (c) 2019, Haven Protocol
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. 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.
//
// 3. Neither the name of the copyright holder 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 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.
//
// Portions of this code based upon code Copyright (c) 2019, The Monero Project
#include <boost/multiprecision/cpp_int.hpp>
#include "pricing_record.h"
#include "serialization/keyvalue_serialization.h"
#include "storages/portable_storage.h"
#include "string_tools.h"
namespace salvium_oracle
{
namespace
{
struct asset_data_serialized
{
std::string asset_type;
uint64_t spot_price;
uint64_t ma_price;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(asset_type)
KV_SERIALIZE(spot_price)
KV_SERIALIZE(ma_price)
END_KV_SERIALIZE_MAP()
};
struct supply_data_serialized
{
uint64_t SAL;
uint64_t VSD;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(SAL)
KV_SERIALIZE(VSD)
END_KV_SERIALIZE_MAP()
};
struct pr_serialized
{
uint64_t pr_version;
uint64_t height;
supply_data supply;
std::vector<asset_data> assets;
uint64_t timestamp;
std::string signature;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(pr_version)
KV_SERIALIZE(height)
KV_SERIALIZE(supply)
KV_SERIALIZE(assets)
KV_SERIALIZE(timestamp)
KV_SERIALIZE(signature)
END_KV_SERIALIZE_MAP()
};
}
pricing_record::pricing_record() noexcept
: pr_version(0)
, height(0)
, supply()
, assets()
, timestamp(0)
, signature()
{
}
pricing_record::~pricing_record() noexcept
{
}
bool supply_data::_load(epee::serialization::portable_storage& src, epee::serialization::section* hparent)
{
supply_data_serialized in{};
if (in._load(src, hparent))
{
// Copy everything into the local instance
sal = in.SAL;
vsd = in.VSD;
return true;
}
// Report error here?
return false;
}
bool supply_data::store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const
{
const supply_data_serialized out{sal, vsd};
return out.store(dest, hparent);
}
bool asset_data::_load(epee::serialization::portable_storage& src, epee::serialization::section* hparent)
{
asset_data_serialized in{};
if (in._load(src, hparent))
{
// Copy everything into the local instance
asset_type = in.asset_type;
spot_price = in.spot_price;
ma_price = in.ma_price;
return true;
}
// Report error here?
return false;
}
bool asset_data::store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const
{
const asset_data_serialized out{asset_type, spot_price, ma_price};
return out.store(dest, hparent);
}
bool pricing_record::_load(epee::serialization::portable_storage& src, epee::serialization::section* hparent)
{
pr_serialized in{};
if (in._load(src, hparent))
{
// Copy everything into the local instance
pr_version = in.pr_version;
height = in.height;
supply = in.supply;
assets = in.assets;
timestamp = in.timestamp;
// Signature arrives in HEX format, but needs to be used in BINARY format - convert it here
signature.resize(0);
assert(in.signature.size()%2 == 0);
signature.reserve(in.signature.size() >> 1);
for (unsigned int i = 0; i < in.signature.size(); i += 2) {
std::string byteString = in.signature.substr(i, 2);
signature.emplace_back((uint8_t)strtol(byteString.c_str(), NULL, 16));
}
return true;
}
// Report error here?
return false;
}
bool pricing_record::store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const
{
std::string sig_hex;
for (size_t i=0; i<signature.size(); ++i) {
std::stringstream ss;
ss << std::hex << std::setw(2) << std::setfill('0') << (0xff & signature.at(i));
sig_hex += ss.str();
}
const pr_serialized out{pr_version, height, supply, assets, timestamp, sig_hex};
return out.store(dest, hparent);
}
pricing_record::pricing_record(const pricing_record& orig) noexcept
: pr_version(orig.pr_version)
, height(orig.height)
, supply(orig.supply)
, assets(orig.assets)
, timestamp(orig.timestamp)
, signature(orig.signature)
{
}
pricing_record& pricing_record::operator=(const pricing_record& orig) noexcept
{
pr_version = orig.pr_version;
height = orig.height;
supply = orig.supply;
assets = orig.assets;
timestamp = orig.timestamp;
signature = orig.signature;
return *this;
}
uint64_t pricing_record::operator[](const std::string& asset_type) const
{
for (const auto& asset: assets) {
if (asset.asset_type != asset_type) continue;
return asset.spot_price;
}
return 0;
}
bool pricing_record::equal(const pricing_record& other) const noexcept
{
return ((pr_version == other.pr_version) &&
(height == other.height) &&
(supply == other.supply) &&
(assets == other.assets) &&
(timestamp == other.timestamp) &&
(signature == other.signature));
}
bool pricing_record::empty() const noexcept
{
const pricing_record empty_pr = salvium_oracle::pricing_record();
return (*this).equal(empty_pr);
}
// overload for pr validation for block
bool pricing_record::valid(uint32_t hf_version, uint64_t bl_timestamp, uint64_t last_bl_timestamp) const
{
if (this->empty())
return true;
// validate the timestmap
if (this->timestamp > bl_timestamp + PRICING_RECORD_VALID_TIME_DIFF_FROM_BLOCK) {
LOG_ERROR("Pricing record timestamp is too far in the future.");
return false;
}
if (this->timestamp <= last_bl_timestamp) {
LOG_ERROR("Pricing record timestamp: " << this->timestamp << ", block timestamp: " << bl_timestamp);
LOG_ERROR("Pricing record timestamp is too old.");
return false;
}
return true;
}
}
+159
View File
@@ -0,0 +1,159 @@
// Copyright (c) 2019, Haven Protocol
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. 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.
//
// 3. Neither the name of the copyright holder 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 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.
//
// Portions of this code based upon code Copyright (c) 2019, The Monero Project
#pragma once
#include "common/pod-class.h"
#include <openssl/bio.h>
#include <openssl/crypto.h>
#include <openssl/ecdsa.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/ssl.h>
#include <cstdint>
#include <string>
#include <cstring>
#include "serialization/vector.h"
#include "cryptonote_config.h"
#include "crypto/hash.h"
namespace epee
{
namespace serialization
{
class portable_storage;
struct section;
}
}
namespace salvium_oracle
{
#pragma pack(push, 1)
POD_CLASS pricing_record_pre {
uint64_t pr_version;
uint64_t price;
uint64_t timestamp;
};
#pragma pack(pop)
struct supply_data {
uint64_t sal;
uint64_t vsd;
//! Load from epee p2p format
bool _load(epee::serialization::portable_storage& src, epee::serialization::section* hparent);
//! Store in epee p2p format
bool store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const;
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(sal)
VARINT_FIELD(vsd)
END_SERIALIZE()
};
inline bool operator==(const supply_data& a, const supply_data& b) noexcept
{
return (a.sal == b.sal &&
a.vsd == b.vsd);
}
struct asset_data {
std::string asset_type;
uint64_t spot_price;
uint64_t ma_price;
//! Load from epee p2p format
bool _load(epee::serialization::portable_storage& src, epee::serialization::section* hparent);
//! Store in epee p2p format
bool store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const;
BEGIN_SERIALIZE_OBJECT()
FIELD(asset_type)
VARINT_FIELD(spot_price)
VARINT_FIELD(ma_price)
END_SERIALIZE()
};
inline bool operator==(const asset_data& a, const asset_data& b) noexcept
{
return (a.asset_type == b.asset_type &&
a.spot_price == b.spot_price &&
a.ma_price == b.ma_price);
}
struct pricing_record
{
// Fields
uint64_t pr_version;
uint64_t height;
supply_data supply;
std::vector<asset_data> assets;
uint64_t timestamp;
std::vector<uint8_t> signature;
// Default c'tor
pricing_record() noexcept;
//! Load from epee p2p format
bool _load(epee::serialization::portable_storage& src, epee::serialization::section* hparent);
//! Store in epee p2p format
bool store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const;
pricing_record(const pricing_record& orig) noexcept;
~pricing_record() noexcept;
bool equal(const pricing_record& other) const noexcept;
bool empty() const noexcept;
bool valid(uint32_t hf_version, uint64_t bl_timestamp, uint64_t last_bl_timestamp) const;
pricing_record& operator=(const pricing_record& orig) noexcept;
uint64_t operator[](const std::string& asset_type) const;
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(pr_version)
VARINT_FIELD(height)
FIELD(supply)
FIELD(assets)
VARINT_FIELD(timestamp)
FIELD(signature)
END_SERIALIZE()
};
inline bool operator==(const pricing_record& a, const pricing_record& b) noexcept
{
return a.equal(b);
}
inline bool operator!=(const pricing_record& a, const pricing_record& b) noexcept
{
return !a.equal(b);
}
} // salvium_oracle
+1 -1
View File
@@ -48,7 +48,7 @@ template <>
struct binary_archive<false> : public binary_archive_base<std::istream, false>
{
explicit binary_archive(stream_type &s) : base_type(s) {
stream_type::streampos pos = stream_.tellg();
auto pos = stream_.tellg();
stream_.seekg(0, std::ios_base::end);
eof_pos_ = stream_.tellg();
stream_.seekg(pos);
+1 -11
View File
@@ -7,7 +7,6 @@
#include <vector>
#include "serialization.h"
#include "debug_archive.h"
#include "crypto/chacha8.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
@@ -62,13 +61,4 @@ BLOB_SERIALIZER(crypto::secret_key);
BLOB_SERIALIZER(crypto::key_derivation);
BLOB_SERIALIZER(crypto::key_image);
BLOB_SERIALIZER(crypto::signature);
VARIANT_TAG(debug_archive, crypto::cycle, "cycle");
VARIANT_TAG(debug_archive, crypto::cycle40, "cycle40");
VARIANT_TAG(debug_archive, crypto::cycle48, "cycle48");
VARIANT_TAG(debug_archive, crypto::hash, "hash");
VARIANT_TAG(debug_archive, crypto::hash8, "hash8");
VARIANT_TAG(debug_archive, crypto::public_key, "public_key");
VARIANT_TAG(debug_archive, crypto::secret_key, "secret_key");
VARIANT_TAG(debug_archive, crypto::key_derivation, "key_derivation");
VARIANT_TAG(debug_archive, crypto::key_image, "key_image");
VARIANT_TAG(debug_archive, crypto::signature, "signature");
BLOB_SERIALIZER(crypto::view_tag);
-28
View File
@@ -1,28 +0,0 @@
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include "json_archive.h"
#include "variant.h"
template <bool W>
struct debug_archive : public json_archive<W> {
typedef typename json_archive<W>::stream_type stream_type;
debug_archive(stream_type &s) : json_archive<W>(s) { }
};
template <class T>
struct serializer<debug_archive<true>, T>
{
static void serialize(debug_archive<true> &ar, T &v)
{
ar.begin_object();
ar.tag(variant_serialization_traits<debug_archive<true>, T>::get_tag());
serializer<json_archive<true>, T>::serialize(ar, v);
ar.end_object();
ar.stream() << std::endl;
}
};
-145
View File
@@ -1,145 +0,0 @@
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
/* json_archive.h
*
* JSON archive */
#pragma once
#include "serialization.h"
#include <cassert>
#include <iostream>
#include <iomanip>
template <class Stream, bool IsSaving>
struct json_archive_base
{
typedef Stream stream_type;
typedef json_archive_base<Stream, IsSaving> base_type;
typedef boost::mpl::bool_<IsSaving> is_saving;
typedef const char *variant_tag_type;
json_archive_base(stream_type &s, bool indent = false) : stream_(s), indent_(indent), object_begin(false), depth_(0) { }
void tag(const char *tag) {
if (!object_begin)
stream_ << ", ";
make_indent();
stream_ << '"' << tag << "\": ";
object_begin = false;
}
void begin_object()
{
stream_ << "{";
++depth_;
object_begin = true;
}
void end_object()
{
--depth_;
make_indent();
stream_ << "}";
}
void begin_variant() { begin_object(); }
void end_variant() { end_object(); }
Stream &stream() { return stream_; }
protected:
void make_indent()
{
if (indent_)
{
stream_ << '\n' << std::string(2 * depth_, ' ');
}
}
protected:
stream_type &stream_;
bool indent_;
bool object_begin;
size_t depth_;
};
template <bool W>
struct json_archive;
template <>
struct json_archive<true> : public json_archive_base<std::ostream, true>
{
json_archive(stream_type &s, bool indent = false) : base_type(s, indent) { }
template<typename T>
static auto promote_to_printable_integer_type(T v) -> decltype(+v)
{
// Unary operator '+' performs integral promotion on type T [expr.unary.op].
// If T is signed or unsigned char, it's promoted to int and printed as number.
return +v;
}
template <class T>
void serialize_int(T v)
{
stream_ << std::dec << promote_to_printable_integer_type(v);
}
void serialize_blob(void *buf, size_t len, const char *delimiter="\"") {
begin_string(delimiter);
for (size_t i = 0; i < len; i++) {
unsigned char c = ((unsigned char *)buf)[i];
stream_ << std::hex << std::setw(2) << std::setfill('0') << (int)c;
}
end_string(delimiter);
}
template <class T>
void serialize_varint(T &v)
{
stream_ << std::dec << promote_to_printable_integer_type(v);
}
void begin_string(const char *delimiter="\"")
{
stream_ << delimiter;
}
void end_string(const char *delimiter="\"")
{
stream_ << delimiter;
}
void begin_array(size_t s=0)
{
inner_array_size_ = s;
++depth_;
stream_ << "[ ";
}
void delimit_array()
{
stream_ << ", ";
}
void end_array()
{
--depth_;
if (0 < inner_array_size_)
{
make_indent();
}
stream_ << "]";
}
void write_variant_tag(const char *t)
{
tag(t);
}
private:
size_t inner_array_size_;
};
-21
View File
@@ -1,21 +0,0 @@
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include <sstream>
#include "json_archive.h"
namespace serialization {
template<class T>
std::string dump_json(T &v)
{
std::stringstream ostr;
json_archive<true> oar(ostr);
assert(serialization::serialize(oar, v));
return ostr.str();
};
} // namespace serialization
+43 -14
View File
@@ -33,35 +33,64 @@
#include <vector>
#include "serialization.h"
#include "debug_archive.h"
#include "offshore/pricing_record.h"
#include "cryptonote_config.h"
/*
// read
template <template <bool> class Archive>
bool do_serialize(Archive<false> &ar, offshore::pricing_record &pr)
bool do_serialize(Archive<false> &ar, offshore::pricing_record &pr, uint8_t version)
{
// very basic sanity check
if (ar.remaining_bytes() < sizeof(offshore::pricing_record)) {
ar.stream().setstate(std::ios::failbit);
return false;
if (version < HF_VERSION_XASSET_FEES_V2)
{
// very basic sanity check
if (ar.remaining_bytes() < sizeof(offshore::pricing_record_v1)) {
ar.stream().setstate(std::ios::failbit);
return false;
}
offshore::pricing_record_v1 pr_v1;
ar.serialize_blob(&pr_v1, sizeof(offshore::pricing_record_v1), "");
if (!ar.stream().good())
return false;
if (!pr_v1.write_to_pr(pr))
return false;
}
else
{
// very basic sanity check
if (ar.remaining_bytes() < sizeof(offshore::pricing_record)) {
ar.stream().setstate(std::ios::failbit);
return false;
}
ar.serialize_blob(&pr, sizeof(offshore::pricing_record), "");
if (!ar.stream().good())
return false;
}
ar.serialize_blob(&pr, sizeof(offshore::pricing_record), "");
if (!ar.stream().good())
return false;
return true;
}
// write
template <template <bool> class Archive>
bool do_serialize(Archive<true> &ar, offshore::pricing_record &pr)
bool do_serialize(Archive<true> &ar, offshore::pricing_record &pr, uint8_t version)
{
ar.begin_string();
ar.serialize_blob(&pr, sizeof(offshore::pricing_record), "");
if (version < HF_VERSION_XASSET_FEES_V2)
{
offshore::pricing_record_v1 pr_v1;
if (!pr_v1.read_from_pr(pr))
return false;
ar.serialize_blob(&pr_v1, sizeof(offshore::pricing_record_v1), "");
}
else
{
ar.serialize_blob(&pr, sizeof(offshore::pricing_record), "");
}
if (!ar.stream().good())
return false;
ar.end_string();
return true;
}
*/
BLOB_SERIALIZER(offshore::pricing_record);
BLOB_SERIALIZER(offshore::pricing_record);
+7
View File
@@ -109,6 +109,13 @@ inline bool do_serialize(Archive &ar, bool &v)
ar.serialize_varint(f); \
if (!ar.stream().good()) return false; \
} while(0);
#define VERSION_FIELD(v) \
uint32_t version = v; \
do { \
ar.tag("version"); \
ar.serialize_varint(version); \
if (!ar.stream().good()) return false; \
} while(0);
namespace serialization {
namespace detail
+177
View File
@@ -0,0 +1,177 @@
// Copyright (c) 2019, Haven Protocol
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. 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.
//
// 3. Neither the name of the copyright holder 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 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.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
#include <vector>
#include "serialization.h"
#include "zephyr_oracle/pricing_record.h"
#include "cryptonote_config.h"
// read
template <template <bool> class Archive>
bool do_serialize(Archive<false> &ar, zephyr_oracle::pricing_record &pr, uint8_t version)
{
if (version >= 4)
{
// very basic sanity check
if (ar.remaining_bytes() < sizeof(zephyr_oracle::pricing_record)) {
return false;
}
ar.serialize_blob(&pr, sizeof(zephyr_oracle::pricing_record), "");
if (!ar.good())
return false;
}
else if (version >= 3)
{
// very basic sanity check
if (ar.remaining_bytes() < sizeof(zephyr_oracle::pricing_record_v2)) {
return false;
}
zephyr_oracle::pricing_record_v2 pr_v2;
ar.serialize_blob(&pr_v2, sizeof(zephyr_oracle::pricing_record_v2), "");
if (!ar.good())
return false;
if (!pr_v2.write_to_pr(pr))
return false;
}
else
{
// very basic sanity check
if (ar.remaining_bytes() < sizeof(zephyr_oracle::pricing_record_v1)) {
return false;
}
zephyr_oracle::pricing_record_v1 pr_v1;
ar.serialize_blob(&pr_v1, sizeof(zephyr_oracle::pricing_record_v1), "");
if (!ar.good())
return false;
if (!pr_v1.write_to_pr(pr))
return false;
}
return true;
}
// write
template <template <bool> class Archive>
bool do_serialize(Archive<true> &ar, zephyr_oracle::pricing_record &pr, uint8_t version)
{
ar.begin_string();
if (version >= 4)
{
ar.serialize_blob(&pr, sizeof(zephyr_oracle::pricing_record), "");
}
else if (version >= 3)
{
zephyr_oracle::pricing_record_v2 pr_v2;
if (!pr_v2.read_from_pr(pr))
return false;
ar.serialize_blob(&pr_v2, sizeof(zephyr_oracle::pricing_record_v2), "");
}
else
{
zephyr_oracle::pricing_record_v1 pr_v1;
if (!pr_v1.read_from_pr(pr))
return false;
ar.serialize_blob(&pr_v1, sizeof(zephyr_oracle::pricing_record_v1), "");
}
if (!ar.good())
return false;
ar.end_string();
return true;
}
// read
template <template <bool> class Archive>
bool do_serialize(Archive<false> &ar, zephyr_oracle::pricing_record_v1 &pr, uint8_t version)
{
// very basic sanity check
if (ar.remaining_bytes() < sizeof(zephyr_oracle::pricing_record_v1)) {
return false;
}
ar.serialize_blob(&pr, sizeof(zephyr_oracle::pricing_record_v1), "");
if (!ar.good())
return false;
return true;
}
// write
template <template <bool> class Archive>
bool do_serialize(Archive<true> &ar, zephyr_oracle::pricing_record_v1 &pr, uint8_t version)
{
ar.begin_string();
ar.serialize_blob(&pr, sizeof(zephyr_oracle::pricing_record_v1), "");
if (!ar.good())
return false;
ar.end_string();
return true;
}
// read
template <template <bool> class Archive>
bool do_serialize(Archive<false> &ar, zephyr_oracle::pricing_record_v2 &pr, uint8_t version)
{
// very basic sanity check
if (ar.remaining_bytes() < sizeof(zephyr_oracle::pricing_record_v2)) {
return false;
}
ar.serialize_blob(&pr, sizeof(zephyr_oracle::pricing_record_v2), "");
if (!ar.good())
return false;
return true;
}
// write
template <template <bool> class Archive>
bool do_serialize(Archive<true> &ar, zephyr_oracle::pricing_record_v2 &pr, uint8_t version)
{
ar.begin_string();
ar.serialize_blob(&pr, sizeof(zephyr_oracle::pricing_record_v2), "");
if (!ar.good())
return false;
ar.end_string();
return true;
}
BLOB_SERIALIZER(zephyr_oracle::pricing_record);
BLOB_SERIALIZER(zephyr_oracle::pricing_record_v1);
BLOB_SERIALIZER(zephyr_oracle::pricing_record_v2);
+77
View File
@@ -0,0 +1,77 @@
// Copyright (c) 2021, Haven Protocol
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. 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.
//
// 3. Neither the name of the copyright holder 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 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.
#pragma once
#include <string>
#include <vector>
namespace zephyr_oracle {
const std::vector<std::string> ASSET_TYPES = {"ZEPH", "ZEPHUSD", "ZEPHRSV"};
class asset_type_counts
{
public:
// Fields
uint64_t ZEPH;
uint64_t ZEPHUSD;
uint64_t ZEPHRSV;
asset_type_counts() noexcept
: ZEPH(0)
, ZEPHUSD(0)
, ZEPHRSV(0)
{
}
uint64_t operator[](const std::string asset_type) const noexcept
{
if (asset_type == "ZEPH") {
return ZEPH;
} else if (asset_type == "ZEPHUSD") {
return ZEPHUSD;
} else if (asset_type == "ZEPHRSV") {
return ZEPHRSV;
}
return 0;
}
void add(const std::string asset_type, const uint64_t val)
{
if (asset_type == "ZEPH") {
ZEPH += val;
} else if (asset_type == "ZEPHUSD") {
ZEPHUSD += val;
} else if (asset_type == "ZEPHRSV") {
ZEPHRSV += val;
}
}
};
}
+292
View File
@@ -0,0 +1,292 @@
// Copyright (c) 2019, Haven Protocol
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. 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.
//
// 3. Neither the name of the copyright holder 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 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.
//
// Portions of this code based upon code Copyright (c) 2019, The Monero Project
#include "pricing_record.h"
#include "serialization/keyvalue_serialization.h"
#include "storages/portable_storage.h"
#include "string_tools.h"
namespace zephyr_oracle
{
namespace
{
struct pr_serialized
{
uint64_t spot;
uint64_t moving_average;
uint64_t stable;
uint64_t stable_ma;
uint64_t reserve;
uint64_t reserve_ma;
uint64_t reserve_ratio;
uint64_t reserve_ratio_ma;
uint64_t timestamp;
std::string signature;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(spot)
KV_SERIALIZE(moving_average)
KV_SERIALIZE(stable)
KV_SERIALIZE(stable_ma)
KV_SERIALIZE(reserve)
KV_SERIALIZE(reserve_ma)
KV_SERIALIZE(reserve_ratio)
KV_SERIALIZE(reserve_ratio_ma)
KV_SERIALIZE(timestamp)
KV_SERIALIZE(signature)
END_KV_SERIALIZE_MAP()
};
}
pricing_record::pricing_record() noexcept
: spot(0)
, moving_average(0)
, stable(0)
, stable_ma(0)
, reserve(0)
, reserve_ma(0)
, reserve_ratio(0)
, reserve_ratio_ma(0)
, timestamp(0)
{
std::memset(signature, 0, sizeof(signature));
}
bool pricing_record::_load(epee::serialization::portable_storage& src, epee::serialization::section* hparent)
{
pr_serialized in{};
if (in._load(src, hparent))
{
// Copy everything into the local instance
spot = in.spot;
moving_average = in.moving_average;
stable = in.stable;
stable_ma = in.stable_ma;
reserve = in.reserve;
reserve_ma = in.reserve_ma;
reserve_ratio = in.reserve_ratio;
reserve_ratio_ma = in.reserve_ratio_ma;
timestamp = in.timestamp;
for (unsigned int i = 0; i < in.signature.length(); i += 2) {
std::string byteString = in.signature.substr(i, 2);
signature[i>>1] = (char) strtol(byteString.c_str(), NULL, 16);
}
return true;
}
// Report error here?
return false;
}
bool pricing_record::store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const
{
std::string sig_hex;
for (unsigned int i=0; i<64; i++) {
std::stringstream ss;
ss << std::hex << std::setw(2) << std::setfill('0') << (0xff & signature[i]);
sig_hex += ss.str();
}
const pr_serialized out{spot,moving_average,stable,stable_ma,reserve,reserve_ma,reserve_ratio,reserve_ratio_ma,timestamp,sig_hex};
return out.store(dest, hparent);
}
pricing_record::pricing_record(const pricing_record& orig) noexcept
: spot(orig.spot)
, moving_average(orig.moving_average)
, stable(orig.stable)
, stable_ma(orig.stable_ma)
, reserve(orig.reserve)
, reserve_ma(orig.reserve_ma)
, reserve_ratio(orig.reserve_ratio)
, reserve_ratio_ma(orig.reserve_ratio_ma)
, timestamp(orig.timestamp)
{
std::memcpy(signature, orig.signature, sizeof(signature));
}
pricing_record& pricing_record::operator=(const pricing_record& orig) noexcept
{
spot = orig.spot;
moving_average = orig.moving_average;
stable = orig.stable;
stable_ma = orig.stable_ma;
reserve = orig.reserve;
reserve_ma = orig.reserve_ma;
reserve_ratio = orig.reserve_ratio;
reserve_ratio_ma = orig.reserve_ratio_ma;
timestamp = orig.timestamp;
::memcpy(signature, orig.signature, sizeof(signature));
return *this;
}
bool pricing_record::equal(const pricing_record& other) const noexcept
{
return ((spot == other.spot) &&
(moving_average == other.moving_average) &&
(stable == other.stable) &&
(stable_ma == other.stable_ma) &&
(reserve == other.reserve) &&
(reserve_ma == other.reserve_ma) &&
(reserve_ratio == other.reserve_ratio) &&
(reserve_ratio_ma == other.reserve_ratio_ma) &&
(timestamp == other.timestamp) &&
!::memcmp(signature, other.signature, sizeof(signature)));
}
bool pricing_record::empty() const noexcept
{
const pricing_record empty_pr = zephyr_oracle::pricing_record();
return (*this).equal(empty_pr);
}
bool pricing_record::verifySignature(const std::string& public_key, const uint8_t hf_version) const
{
CHECK_AND_ASSERT_THROW_MES(!public_key.empty(), "Pricing record verification failed. NULL public key. PK Size: " << public_key.size()); // TODO: is this necessary or the one below already covers this case, meannin it will produce empty pubkey?
// extract the key
EVP_PKEY* pubkey;
BIO* bio = BIO_new_mem_buf(public_key.c_str(), public_key.size());
if (!bio) {
return false;
}
pubkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
BIO_free(bio);
CHECK_AND_ASSERT_THROW_MES(pubkey != NULL, "Pricing record verification failed. NULL public key.");
// Convert our internal 64-byte binary representation into 128-byte hex string
std::string sig_hex;
for (unsigned int i=0; i<64; i++) {
std::stringstream ss;
ss << std::hex << std::setw(2) << std::setfill('0') << (0xff & signature[i]);
sig_hex += ss.str();
}
// Build the JSON string, so that we can verify the signature
std::ostringstream oss;
oss << "{\"spot\":" << spot;
if (hf_version <= 4) {
oss << ",\"moving_average\":" << moving_average;
}
oss << ",\"timestamp\":" << timestamp;
oss << "}";
std::string message = oss.str();
// Create a verify digest from the message
EVP_MD_CTX *ctx = EVP_MD_CTX_create();
int ret = 0;
if (ctx) {
ret = EVP_DigestVerifyInit(ctx, NULL, EVP_sha256(), NULL, pubkey);
if (ret == 1) {
ret = EVP_DigestVerifyUpdate(ctx, message.data(), message.length());
if (ret == 1) {
ret = EVP_DigestVerifyFinal(ctx, (const unsigned char *)signature, 64);
}
}
}
// Cleanup the context we created
EVP_MD_CTX_destroy(ctx);
// Cleanup the openssl stuff
EVP_PKEY_free(pubkey);
if (ret == 1)
return true;
// Get the errors from OpenSSL
// ERR_print_errors_fp (stderr);
return false;
}
bool pricing_record::has_missing_rates(const uint8_t hf_version) const noexcept
{
bool missing_rates = (spot == 0) || (moving_average == 0) || (stable == 0) || (stable_ma == 0) || (reserve == 0) || (reserve_ma == 0);
if (hf_version <= 3) {
return missing_rates;
} else if (hf_version <= 4) {
return missing_rates || (reserve_ratio == 0);
}
return missing_rates || (reserve_ratio == 0) || (reserve_ratio_ma == 0);
}
bool pricing_record::has_essential_rates(const uint8_t hf_version) const noexcept
{
bool essential_rates = (spot != 0) && (stable != 0) && (reserve != 0);
if (hf_version <= 3) {
return essential_rates;
}
return essential_rates && (reserve_ratio != 0);
}
// overload for pr validation for block
bool pricing_record::valid(uint32_t hf_version, uint64_t bl_timestamp, uint64_t last_bl_timestamp) const
{
if (hf_version < 3) {
if (!this->empty())
return false;
}
if (this->empty())
return true;
if (this->has_missing_rates(hf_version)) {
if (hf_version < 4 || !this->has_essential_rates(hf_version)) {
LOG_ERROR("Pricing record has missing rates.");
return false;
}
}
std::string const MAINNET_ORACLE_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\n"
"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAO5hVuc6ylYMbj3WhqOMoAcJ0SD4e3zW\n"
"edsUmhQeYwBkelAaFyxhX4ZotP+b/cFr2mX5iuND1znEnMZkyg+YmtkCAwEAAQ==\n"
"-----END PUBLIC KEY-----\n";
if (!verifySignature(MAINNET_ORACLE_PUBLIC_KEY, hf_version)) {
LOG_ERROR("Invalid pricing record signature.");
return false;
}
// validate the timestmap
if (this->timestamp > bl_timestamp + PRICING_RECORD_VALID_TIME_DIFF_FROM_BLOCK) {
LOG_ERROR("Pricing record timestamp is too far in the future.");
return false;
}
if (this->timestamp <= last_bl_timestamp) {
LOG_ERROR("Pricing record timestamp: " << this->timestamp << ", block timestamp: " << bl_timestamp);
LOG_ERROR("Pricing record timestamp is too old.");
return false;
}
return true;
}
}
+195
View File
@@ -0,0 +1,195 @@
// Copyright (c) 2019, Haven Protocol
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. 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.
//
// 3. Neither the name of the copyright holder 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 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.
//
// Portions of this code based upon code Copyright (c) 2019, The Monero Project
#pragma once
#include "common/pod-class.h"
#include <openssl/bio.h>
#include <openssl/crypto.h>
#include <openssl/ecdsa.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/ssl.h>
#include <cstdint>
#include <string>
#include <cstring>
#include "cryptonote_config.h"
#include "crypto/hash.h"
namespace epee
{
namespace serialization
{
class portable_storage;
struct section;
}
}
namespace zephyr_oracle
{
#pragma pack(push, 1)
POD_CLASS pricing_record_pre {
uint64_t zEPHUSD;
uint64_t zEPHRSV;
uint64_t timestamp;
};
POD_CLASS pricing_record_pre_v2 {
uint64_t spot;
uint64_t moving_average;
uint64_t stable;
uint64_t stable_ma;
uint64_t reserve;
uint64_t reserve_ma;
uint64_t timestamp;
unsigned char signature[64];
};
#pragma pack(pop)
class pricing_record
{
public:
// Fields
uint64_t spot;
uint64_t moving_average;
uint64_t stable;
uint64_t stable_ma;
uint64_t reserve;
uint64_t reserve_ma;
uint64_t reserve_ratio;
uint64_t reserve_ratio_ma;
uint64_t timestamp;
unsigned char signature[64];
// Default c'tor
pricing_record() noexcept;
//! Load from epee p2p format
bool _load(epee::serialization::portable_storage& src, epee::serialization::section* hparent);
//! Store in epee p2p format
bool store(epee::serialization::portable_storage& dest, epee::serialization::section* hparent) const;
pricing_record(const pricing_record& orig) noexcept;
~pricing_record() = default;
bool equal(const pricing_record& other) const noexcept;
bool empty() const noexcept;
bool verifySignature(const std::string& public_key, const uint8_t hf_version) const;
bool has_missing_rates(const uint8_t hf_version) const noexcept;
bool has_essential_rates(const uint8_t hf_version) const noexcept;
bool valid(uint32_t hf_version, uint64_t bl_timestamp, uint64_t last_bl_timestamp) const;
pricing_record& operator=(const pricing_record& orig) noexcept;
uint64_t operator[](const std::string& asset_type) const;
};
inline bool operator==(const pricing_record& a, const pricing_record& b) noexcept
{
return a.equal(b);
}
inline bool operator!=(const pricing_record& a, const pricing_record& b) noexcept
{
return !a.equal(b);
}
class pricing_record_v1
{
public:
uint64_t zEPHUSD;
uint64_t zEPHRSV;
uint64_t timestamp;
bool write_to_pr(zephyr_oracle::pricing_record &pr)
{
pr.spot = 0;
pr.moving_average = 0;
pr.stable = 0;
pr.stable_ma = 0;
pr.reserve = 0;
pr.reserve_ma = 0;
pr.timestamp = 0;
std::memset(pr.signature, 0, sizeof(zephyr_oracle::pricing_record::signature));
return true;
};
bool read_from_pr(zephyr_oracle::pricing_record &pr)
{
zEPHUSD = 0;
zEPHRSV = 0;
timestamp = 0;
return true;
};
};
class pricing_record_v2
{
public:
uint64_t spot;
uint64_t moving_average;
uint64_t stable;
uint64_t stable_ma;
uint64_t reserve;
uint64_t reserve_ma;
uint64_t timestamp;
unsigned char signature[64];
bool write_to_pr(zephyr_oracle::pricing_record &pr)
{
pr.spot = spot;
pr.moving_average = moving_average;
pr.stable = stable;
pr.stable_ma = stable_ma;
pr.reserve = reserve;
pr.reserve_ma = reserve_ma;
pr.reserve_ratio = 0;
pr.reserve_ratio_ma = 0;
pr.timestamp = timestamp;
std::memcpy(pr.signature, signature, sizeof(pr.signature));
return true;
};
bool read_from_pr(zephyr_oracle::pricing_record &pr)
{
spot = pr.spot;
moving_average = pr.moving_average;
stable = pr.stable;
stable_ma = pr.stable_ma;
reserve = pr.reserve;
reserve_ma = pr.reserve_ma;
timestamp = pr.timestamp;
std::memcpy(signature, pr.signature, sizeof(signature));
return true;
};
};
} // oracle
Executable
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash -x
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd $DIR
node sal.js || exit 1
+14
View File
@@ -0,0 +1,14 @@
"use strict";
let u = require('../build/Release/cryptoforknote-util');
const b = Buffer.from(
'010194a5ebb406f613c4e7514facf3e5b9923c885357b53f2b02f8e17f9721371296b99113035f00000000020001ffcb6d018f9ffec12d03125e128c041c8a2d41fab9ebe2a7a4b10afbef4e134ec7ba3151c8c730a644310353414c3c7334015f99bdbbe70161dafb2da2fd9a4285da893a7519cff350981a959f525c43e5c60211000000000000000000000000000000000001e3c7bfb00b00020001ffcb6d0023016b6961b458286074406192961c1f0e5236455f45fcd6c175c7142d6353a481d60400020000'
, 'hex');
const b2 = u.convert_blob(b, 15);
if (b2.toString('hex') === '010194a5ebb406f613c4e7514facf3e5b9923c885357b53f2b02f8e17f9721371296b99113035f00000000ac81ca3e7bc9369e63563923187d2cfdb42eac839c7fe24e6d5d0080c96d758f02') {
console.log('PASSED');
else {
console.log('FAILED: ' + b2);
process.exit(1);
}