Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fa62a68afa | |||
| 1afe313308 | |||
| 516160568f | |||
| 157948193d | |||
| b627d4d36e | |||
| eb4d1a5225 | |||
| bdefbffd43 | |||
| 125a1ab637 | |||
| f97a87a97a | |||
| 3b5542e94a | |||
| 9bc777cf7d | |||
| 66fd45fd11 | |||
| 6c09ba09fd | |||
| 90aac49c77 | |||
| e6ebe4d355 | |||
| 0e72d68d94 | |||
| 170d0c0ec9 | |||
| 5548c6de53 | |||
| 56ea8f3792 | |||
| 4d15a85e70 | |||
| 494e6aa059 | |||
| 5d33cf7e90 | |||
| f4ff9bc8cb | |||
| acc0039b14 | |||
| 57d2f65800 | |||
| be85a60436 | |||
| 1f0941edd8 | |||
| 626fd45757 | |||
| dd7fc1aa05 | |||
| f5ccc22d2c | |||
| 22f9cf0bca | |||
| 8f3052679a | |||
| 0d0da4af7b | |||
| 3da08f4e74 | |||
| d9778fd1ef | |||
| f212be897e | |||
| d1a0cf9439 | |||
| b402ceb37f | |||
| f31a2751ab | |||
| af5a7c2186 | |||
| 916e440fb6 | |||
| bd305271cd | |||
| e86f0a8afd | |||
| 80b9b2be12 | |||
| d405a871a4 | |||
| de78291246 | |||
| 04ba92e6fd | |||
| 9021066354 | |||
| 7c139874ce | |||
| 58f8aeb67b | |||
| 2c6f6e6dd2 | |||
| 8729782845 | |||
| df11d9c2bf | |||
| 9d722a83a3 | |||
| 16acb844d7 | |||
| 1647e8ccd6 | |||
| 95870f3f47 |
@@ -6,7 +6,8 @@ const bitcoin = require('bitcoinjs-lib');
|
||||
const varuint = require('varuint-bitcoin');
|
||||
const crypto = require('crypto');
|
||||
const fastMerkleRoot = require('merkle-lib/fastRoot');
|
||||
const promise = require('promise');
|
||||
|
||||
const rtm = require('cryptoforknote-util/rtm');
|
||||
|
||||
function scriptCompile(addrHash) {
|
||||
return bitcoin.script.compile([
|
||||
@@ -19,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;
|
||||
}
|
||||
@@ -37,12 +38,16 @@ function txesHaveWitnessCommit(transactions) {
|
||||
);
|
||||
}
|
||||
|
||||
function hash256(buffer) {
|
||||
function sha256(buffer) {
|
||||
return crypto.createHash('sha256').update(buffer).digest();
|
||||
};
|
||||
|
||||
function hash256(buffer) {
|
||||
return sha256(sha256(buffer));
|
||||
};
|
||||
|
||||
function getMerkleRoot(transactions) {
|
||||
if (transactions.length === 0) return null;
|
||||
if (transactions.length === 0) return Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex')
|
||||
const forWitness = txesHaveWitnessCommit(transactions);
|
||||
const hashes = transactions.map(transaction => transaction.getHash(forWitness));
|
||||
const rootHash = fastMerkleRoot(hashes, hash256);
|
||||
@@ -51,7 +56,14 @@ function getMerkleRoot(transactions) {
|
||||
|
||||
let last_epoch_number;
|
||||
let last_seed_hash;
|
||||
const diff1 = 0x00000000ff000000000000000000000000000000000000000000000000000000;
|
||||
|
||||
module.exports.baseDiff = function() {
|
||||
return bignum('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 16);
|
||||
};
|
||||
|
||||
module.exports.baseRavenDiff = function() {
|
||||
return parseInt('0x00000000ff000000000000000000000000000000000000000000000000000000');
|
||||
};
|
||||
|
||||
module.exports.RavenBlockTemplate = function(rpcData, poolAddress) {
|
||||
const poolAddrHash = bitcoin.address.fromBase58Check(poolAddress).hash;
|
||||
@@ -65,49 +77,48 @@ 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));
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
const merkleRoot = new Buffer('DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD', 'hex'); // 32 bytes
|
||||
|
||||
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
|
||||
header.writeUInt32BE(rpcData.curtime, position += 4, 4, 'hex'); // nTime 51-54
|
||||
header.write(merkleRoot, position += 4, 32, 'hex'); // merkelRoot 55-87
|
||||
header.write('DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD', position += 4, 32, 'hex'); // merkelRoot 55-87
|
||||
header.write(rpcData.previousblockhash, position += 32, 32, 'hex'); // prevblockhash 88-120
|
||||
header.writeUInt32BE(rpcData.version, position += 32, 4); // version 121-153
|
||||
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;
|
||||
@@ -117,7 +128,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();
|
||||
@@ -126,7 +137,7 @@ module.exports.RavenBlockTemplate = function(rpcData, poolAddress) {
|
||||
last_epoch_number = epoch_number;
|
||||
}
|
||||
|
||||
const difficulty = parseFloat((diff1 / bignum(rpcData.target, 16).toNumber()).toFixed(9));
|
||||
const difficulty = parseFloat((module.exports.baseRavenDiff() / bignum(rpcData.target, 16).toNumber()).toFixed(9));
|
||||
|
||||
return {
|
||||
blocktemplate_blob: blob.toString('hex'),
|
||||
@@ -137,30 +148,34 @@ module.exports.RavenBlockTemplate = function(rpcData, poolAddress) {
|
||||
difficulty: difficulty,
|
||||
height: rpcData.height,
|
||||
bits: rpcData.bits,
|
||||
prev_hash: rpcData.previousblockhash,
|
||||
};
|
||||
};
|
||||
|
||||
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) {
|
||||
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();
|
||||
}
|
||||
reverseBuffer(getMerkleRoot(transactions)).copy(blob_out, 4 + 32);
|
||||
getMerkleRoot(transactions).copy(blob_out, 4 + 32);
|
||||
};
|
||||
|
||||
module.exports.blockHashBuff = function(blobBuffer) {
|
||||
return reverseBuffer(hash256(blobBuffer));
|
||||
};
|
||||
|
||||
module.exports.convertRavenBlob = function(blobBuffer) {
|
||||
let header = blobBuffer.slice(0, 80);
|
||||
update_merkle_root_hash(blobBuffer, header);
|
||||
return reverseBuffer(hash256(hash256(header)));
|
||||
update_merkle_root_hash(80 + 8 + 32, false, blobBuffer, header);
|
||||
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);
|
||||
nonceBuff.copy (blockTemplate, 80, 0, 8);
|
||||
mixhashBuff.copy(blockTemplate, 88, 0, 32);
|
||||
return blockTemplate;
|
||||
@@ -169,4 +184,40 @@ module.exports.constructNewRavenBlob = function(blockTemplate, nonceBuff, mixhas
|
||||
module.exports.constructNewDeroBlob = function(blockTemplate, nonceBuff) {
|
||||
nonceBuff.copy(blockTemplate, 39, 0, 4);
|
||||
return blockTemplate;
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.EthBlockTemplate = function(rpcData) {
|
||||
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,
|
||||
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);
|
||||
return header;
|
||||
};
|
||||
|
||||
module.exports.constructNewRtmBlob = function(blockTemplate, nonceBuff) {
|
||||
update_merkle_root_hash(80, true, blockTemplate, blockTemplate);
|
||||
nonceBuff.copy(blockTemplate, 76, 0, 4);
|
||||
return blockTemplate;
|
||||
};
|
||||
|
||||
+9
-7
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cryptoforknote-util",
|
||||
"version": "9.0.11",
|
||||
"version": "13.0.3",
|
||||
"main": "cryptoforknote-util",
|
||||
"author": {
|
||||
"name": "LucasJones",
|
||||
@@ -8,16 +8,18 @@
|
||||
},
|
||||
"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": "*",
|
||||
"promise": "*",
|
||||
"bindings": "*",
|
||||
"nan": "^2.0.0",
|
||||
"bignum": "^0.13.1",
|
||||
"sha3": "*",
|
||||
"nan": "^2.14.2",
|
||||
"bignum": "^0.13.1",
|
||||
"sha3": "*",
|
||||
"base58-native": "*",
|
||||
"varuint-bitcoin": "^1.0.4",
|
||||
"bitcoinjs-lib": "git+https://github.com/bitcoinjs/bitcoinjs-lib.git#533d6c2e6d0aa4111f7948b1c12003cf6ef83137"
|
||||
"merkle-lib": "^2.0.10",
|
||||
"bitcoinjs-lib": "git+https://github.com/MoneroOcean/bitcoinjs-lib.git"
|
||||
},
|
||||
"keywords": [
|
||||
"cryptonight",
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
const bignum = require('bignum');
|
||||
const base58 = require('base58-native');
|
||||
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) {
|
||||
const decoded = base58.decode(addr);
|
||||
if (decoded.length != 25) throw new Error('Invalid address length for ' + addr);
|
||||
if (!decoded) throw new Error('Base58 decode failed for ' + addr);
|
||||
const pubkey = decoded.slice(1, -4);
|
||||
return Buffer.concat([Buffer.from([0x76, 0xa9, 0x14]), pubkey, Buffer.from([0x88, 0xac])]);
|
||||
}
|
||||
|
||||
function createOutputTransaction(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 generateOutputTransactions(rpcData, poolAddress) {
|
||||
let reward = rpcData.coinbasevalue;
|
||||
let rewardToPool = reward;
|
||||
let txOutputBuffers = [];
|
||||
|
||||
if (rpcData.smartnode) {
|
||||
if (rpcData.smartnode.payee) {
|
||||
const rewards = createOutputTransaction(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 = createOutputTransaction(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 = createOutputTransaction(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 = createOutputTransaction(founderReward, rpcData.founder.payee, rewardToPool, reward, txOutputBuffers);
|
||||
reward = rewards.reward;
|
||||
rewardToPool = rewards.rewardToPool;
|
||||
}
|
||||
|
||||
createOutputTransaction(rewardToPool, null, rewardToPool, reward, txOutputBuffers, Buffer.from(addressToScript(poolAddress), "hex"));
|
||||
|
||||
if (rpcData.default_witness_commitment !== undefined) {
|
||||
const witness_commitment = Buffer.from(rpcData.default_witness_commitment, 'hex');
|
||||
txOutputBuffers.unshift(Buffer.concat([
|
||||
packInt64LE(0),
|
||||
varIntBuffer(witness_commitment.length),
|
||||
witness_commitment
|
||||
]));
|
||||
}
|
||||
|
||||
return Buffer.concat([ varIntBuffer(txOutputBuffers.length), Buffer.concat(txOutputBuffers)]);
|
||||
}
|
||||
|
||||
module.exports.RtmBlockTemplate = function(rpcData, poolAddress) {
|
||||
const extraNoncePlaceholderLength = 17;
|
||||
const coinbaseVersion = Buffer.concat([packUInt16LE(3), packUInt16LE(5)]);
|
||||
|
||||
const scriptSigPart1 = Buffer.concat([
|
||||
serializeNumber(rpcData.height),
|
||||
Buffer.from(rpcData.coinbaseaux.flags, 'hex'),
|
||||
serializeNumber(Date.now() / 1000 | 0),
|
||||
Buffer.from([extraNoncePlaceholderLength])
|
||||
]);
|
||||
|
||||
const scriptSigPart2 = serializeString('/nodeStratum/');
|
||||
|
||||
const blob1 = Buffer.concat([
|
||||
coinbaseVersion,
|
||||
// transaction input
|
||||
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
|
||||
generateOutputTransactions(rpcData, poolAddress),
|
||||
// end transaction ouput
|
||||
packUInt32LE(0), // txLockTime
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "portable_storage_val_converters.h"
|
||||
#include "span.h"
|
||||
#include "int-util.h"
|
||||
#include <boost/mpl/contains.hpp>
|
||||
|
||||
namespace epee
|
||||
{
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -1,7 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#define CURRENT_TRANSACTION_VERSION 1
|
||||
#define POU_TRANSACTION_VERSION 6
|
||||
#define COLLATERAL_TRANSACTION_VERSION 7
|
||||
#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,
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
#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/keyvalue_serialization.h" // eepe named serialization
|
||||
@@ -67,6 +65,7 @@ namespace cryptonote
|
||||
crypto::hash hash;
|
||||
};
|
||||
|
||||
// outputs <= HF_VERSION_VIEW_TAGS
|
||||
struct txout_to_key
|
||||
{
|
||||
txout_to_key() { }
|
||||
@@ -74,6 +73,20 @@ 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()
|
||||
};
|
||||
|
||||
struct txout_offshore
|
||||
{
|
||||
txout_offshore() { }
|
||||
@@ -81,6 +94,19 @@ 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()
|
||||
};
|
||||
|
||||
/* inputs */
|
||||
|
||||
struct txin_gen
|
||||
@@ -159,11 +185,26 @@ 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_xasset
|
||||
{
|
||||
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()
|
||||
};
|
||||
|
||||
typedef boost::variant<txin_gen, txin_to_script, txin_to_scripthash, txin_to_key, txin_offshore, txin_onshore, txin_xasset> txin_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_xhv_target_v;
|
||||
|
||||
//typedef std::pair<uint64_t, txout> out_t;
|
||||
struct tx_out
|
||||
{
|
||||
uint64_t amount;
|
||||
@@ -175,6 +216,18 @@ 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()
|
||||
};
|
||||
|
||||
|
||||
enum loki_version
|
||||
{
|
||||
loki_version_0 = 0,
|
||||
@@ -195,6 +248,7 @@ namespace cryptonote
|
||||
|
||||
std::vector<txin_v> vin;
|
||||
std::vector<tx_out> vout;
|
||||
std::vector<tx_out_xhv> vout_xhv;
|
||||
//extra
|
||||
std::vector<uint8_t> extra;
|
||||
// Block height to use PR from
|
||||
@@ -203,11 +257,12 @@ 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;
|
||||
|
||||
//
|
||||
// NOTE: Loki specific
|
||||
//
|
||||
std::vector<uint64_t> output_unlock_times;
|
||||
enum loki_type_t
|
||||
{
|
||||
loki_type_standard,
|
||||
@@ -230,9 +285,13 @@ namespace cryptonote
|
||||
if (version == loki_version_3_per_output_unlock_times)
|
||||
FIELD(is_deregister)
|
||||
}
|
||||
VARINT_FIELD(unlock_time)
|
||||
if (blob_type != BLOB_TYPE_CRYPTONOTE_XHV || version < POU_TRANSACTION_VERSION)
|
||||
VARINT_FIELD(unlock_time)
|
||||
FIELD(vin)
|
||||
FIELD(vout)
|
||||
if (blob_type != BLOB_TYPE_CRYPTONOTE_XHV)
|
||||
FIELD(vout)
|
||||
else
|
||||
FIELD(vout_xhv)
|
||||
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;
|
||||
@@ -245,9 +304,25 @@ namespace cryptonote
|
||||
}
|
||||
if (blob_type == BLOB_TYPE_CRYPTONOTE_XHV && version >= OFFSHORE_TRANSACTION_VERSION) {
|
||||
VARINT_FIELD(pricing_record_height)
|
||||
FIELD(offshore_data)
|
||||
if (version < 5)
|
||||
FIELD(offshore_data)
|
||||
if (version >= POU_TRANSACTION_VERSION)
|
||||
{
|
||||
FIELD(output_unlock_times)
|
||||
}
|
||||
if (version >= POU_TRANSACTION_VERSION && 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.size())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
END_SERIALIZE()
|
||||
|
||||
@@ -306,7 +381,7 @@ namespace cryptonote
|
||||
if (!vin.empty())
|
||||
{
|
||||
ar.begin_object();
|
||||
bool r = rct_signatures.serialize_rctsig_base(ar, vin.size(), vout.size());
|
||||
bool r = rct_signatures.serialize_rctsig_base(ar, vin.size(), blob_type != BLOB_TYPE_CRYPTONOTE_XHV ? vout.size() : vout_xhv.size());
|
||||
if (!r || !ar.stream().good()) return false;
|
||||
ar.end_object();
|
||||
if (rct_signatures.type != rct::RCTTypeNull)
|
||||
@@ -317,10 +392,11 @@ namespace cryptonote
|
||||
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(),
|
||||
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 :
|
||||
vin.size() > 0 && vin[0].type() == typeid(txin_xasset) ? boost::get<txin_xasset>(vin[0]).key_offsets.size() - 1 :
|
||||
0);
|
||||
}
|
||||
if (!r || !ar.stream().good()) return false;
|
||||
@@ -353,12 +429,15 @@ namespace cryptonote
|
||||
unlock_time = 0;
|
||||
vin.clear();
|
||||
vout.clear();
|
||||
vout_xhv.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 +451,7 @@ 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();}
|
||||
};
|
||||
|
||||
return boost::apply_visitor(txin_signature_size_visitor(), tx_in);
|
||||
@@ -590,13 +670,6 @@ namespace cryptonote
|
||||
{
|
||||
crypto::public_key pub;
|
||||
crypto::secret_key sec;
|
||||
|
||||
static inline keypair generate()
|
||||
{
|
||||
keypair k;
|
||||
generate_keys(k.pub, k.sec);
|
||||
return k;
|
||||
}
|
||||
};
|
||||
//---------------------------------------------------------------
|
||||
|
||||
@@ -612,35 +685,12 @@ VARIANT_TAG(binary_archive, cryptonote::txin_to_scripthash, 0x1);
|
||||
VARIANT_TAG(binary_archive, cryptonote::txin_to_key, 0x2);
|
||||
VARIANT_TAG(binary_archive, cryptonote::txin_offshore, 0x3);
|
||||
VARIANT_TAG(binary_archive, cryptonote::txin_onshore, 0x4);
|
||||
VARIANT_TAG(binary_archive, cryptonote::txin_xasset, 0x5);
|
||||
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_to_tagged_key, 0x3);
|
||||
VARIANT_TAG(binary_archive, cryptonote::txout_offshore, 0x3);
|
||||
VARIANT_TAG(binary_archive, cryptonote::txout_xasset, 0x5);
|
||||
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");
|
||||
|
||||
@@ -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();
|
||||
@@ -245,94 +190,16 @@ namespace cryptonote
|
||||
<< 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));
|
||||
CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key) || in.type() == typeid(txin_offshore) || in.type() == typeid(txin_onshore) || in.type() == typeid(txin_xasset), false, "wrong variant type: "
|
||||
<< in.type().name() << ", expected " << typeid(txin_to_key).name()
|
||||
<< "or " << typeid(txin_offshore).name()
|
||||
<< "or " << typeid(txin_onshore).name()
|
||||
<< "or " << typeid(txin_xasset).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)
|
||||
{
|
||||
@@ -343,40 +210,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);
|
||||
@@ -426,7 +259,7 @@ 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 outputs = 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]);
|
||||
@@ -442,7 +275,7 @@ 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 outputs = 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;
|
||||
@@ -451,6 +284,7 @@ namespace cryptonote
|
||||
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 :
|
||||
0;
|
||||
}
|
||||
bool r = tt.rct_signatures.p.serialize_rctsig_prunable(ba, t.rct_signatures.type, inputs, outputs, mixin);
|
||||
|
||||
@@ -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);
|
||||
@@ -85,13 +79,10 @@ namespace cryptonote
|
||||
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);
|
||||
std::map<std::string, uint64_t> get_outs_money_amount(const transaction& tx);
|
||||
bool check_inputs_types_supported(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 +128,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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
+165
-135
@@ -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
|
||||
@@ -106,6 +105,8 @@ namespace rct {
|
||||
key L;
|
||||
key R;
|
||||
key ki;
|
||||
|
||||
~multisig_kLRki() { memwipe(&k, sizeof(k)); }
|
||||
};
|
||||
|
||||
struct multisig_out {
|
||||
@@ -254,6 +255,9 @@ 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
|
||||
};
|
||||
enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof, RangeProofPaddedBulletproof };
|
||||
struct RCTConfig {
|
||||
@@ -261,113 +265,173 @@ namespace rct {
|
||||
int bp_version;
|
||||
};
|
||||
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.
|
||||
|
||||
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 != 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_xasset)
|
||||
}
|
||||
VARINT_FIELD(txnOffshoreFee)
|
||||
VARINT_FIELD(txnOffshoreFee_usd)
|
||||
if (type == RCTTypeCLSAGN)
|
||||
{
|
||||
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
|
||||
{
|
||||
FIELD(type)
|
||||
if (type == RCTTypeNull)
|
||||
return ar.stream().good();
|
||||
if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeCLSAG)
|
||||
ar.tag("pseudoOuts");
|
||||
ar.begin_array();
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, pseudoOuts);
|
||||
if (pseudoOuts.size() != inputs)
|
||||
return false;
|
||||
VARINT_FIELD(txnFee)
|
||||
if (type == RCTTypeCLSAG)
|
||||
for (size_t i = 0; i < inputs; ++i)
|
||||
{
|
||||
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
|
||||
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.tag("pseudoOuts");
|
||||
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();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
return ar.stream().good();
|
||||
}
|
||||
};
|
||||
struct rctSigPrunable {
|
||||
std::vector<rangeSig> rangeSigs;
|
||||
@@ -382,12 +446,12 @@ namespace rct {
|
||||
{
|
||||
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)
|
||||
return false;
|
||||
if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeCLSAG)
|
||||
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 +486,7 @@ namespace rct {
|
||||
ar.end_array();
|
||||
}
|
||||
|
||||
if (type == RCTTypeCLSAG)
|
||||
if ((type == RCTTypeCLSAG) || (type == RCTTypeCLSAGN) || (type == RCTTypeHaven2) || (type == RCTTypeHaven3))
|
||||
{
|
||||
ar.tag("CLSAGs");
|
||||
ar.begin_array();
|
||||
@@ -513,7 +577,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)
|
||||
{
|
||||
ar.tag("pseudoOuts");
|
||||
ar.begin_array();
|
||||
@@ -537,12 +601,12 @@ namespace rct {
|
||||
|
||||
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 ? 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 ? p.pseudoOuts : pseudoOuts;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -689,23 +753,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);
|
||||
@@ -723,21 +770,4 @@ 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");
|
||||
|
||||
#endif /* RCTTYPES_H */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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_;
|
||||
};
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user