@@ -1,61 +0,0 @@
|
|||||||
# MigrateCoin protocol
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## ExportCoins tx:
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
vin:
|
|
||||||
|
|
||||||
[ any ]
|
|
||||||
|
|
||||||
vout:
|
|
||||||
|
|
||||||
- amount: {burnAmount}
|
|
||||||
|
|
||||||
script: OP_RETURN "send to ledger {id} {voutsHash}"
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* ExportCoin is a standard tx which burns coins in an OP_RETURN
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## ImportCoins tx:
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
vin:
|
|
||||||
|
|
||||||
- txid: 0000000000000000000000000000000000000000000000000000000000000000
|
|
||||||
|
|
||||||
idx: 0
|
|
||||||
|
|
||||||
script: CC_EVAL(EVAL_IMPORTCOINS, {momoProof},{exportCoin}) OP_CHECKCRYPTOCONDITION_UNILATERAL
|
|
||||||
|
|
||||||
vout:
|
|
||||||
|
|
||||||
- [ vouts matching voutsHash in exportCoin ]
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* ImportCoin transaction has no signature
|
|
||||||
|
|
||||||
* ImportCoin is non malleable
|
|
||||||
|
|
||||||
* ImportCoin satisfies tx.IsCoinBase()
|
|
||||||
|
|
||||||
* ImportCoin uses a new opcode which allows a one sided check (no scriptPubKey)
|
|
||||||
|
|
||||||
* ImportCoin must contain CC opcode EVAL_IMPORTCOINS
|
|
||||||
|
|
||||||
* ImportCoin fees are equal to the difference between burnAmount in exportCoins and the sum of outputs.
|
|
||||||
43
migratecoin.sh
Normal file
43
migratecoin.sh
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/bash
|
||||||
|
|
||||||
|
# This script makes the neccesary transactions to migrate
|
||||||
|
# coin between 2 assetchains on the same -ac_cc id
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
source=TXSCL
|
||||||
|
target=TXSCL000
|
||||||
|
address="RFw7byY4xZpZCrtkMk3nFuuG1NTs9rSGgQ"
|
||||||
|
amount=1
|
||||||
|
|
||||||
|
# Alias for running cli on source chain
|
||||||
|
cli_source="komodo-cli -ac_name=$source"
|
||||||
|
|
||||||
|
# Raw tx that we will work with
|
||||||
|
txraw=`$cli_source createrawtransaction "[]" "{\"$address\":$amount}"`
|
||||||
|
|
||||||
|
# Convert to an export tx
|
||||||
|
exportData=`$cli_source migrate_converttoexport $txraw $target $amount`
|
||||||
|
exportRaw=`echo $exportData | jq -r .exportTx`
|
||||||
|
exportPayouts=`echo $exportData | jq -r .payouts`
|
||||||
|
|
||||||
|
# Fund
|
||||||
|
exportFundedData=`$cli_source fundrawtransaction $exportRaw`
|
||||||
|
exportFundedTx=`echo $exportFundedData | jq -r .hex`
|
||||||
|
|
||||||
|
# Sign
|
||||||
|
exportSignedData=`$cli_source signrawtransaction $exportFundedTx`
|
||||||
|
exportSignedTx=`echo $exportSignedData | jq -r .hex`
|
||||||
|
|
||||||
|
# Send
|
||||||
|
echo "Sending export tx"
|
||||||
|
$cli_source sendrawtransaction $exportSignedTx
|
||||||
|
|
||||||
|
read -p "Wait for a notarisation to KMD, and then two more notarisations from the target chain, and then press enter to continue"
|
||||||
|
|
||||||
|
# Create import
|
||||||
|
importTx=`$cli_source migrate_createimporttransaction $exportSignedTx $payouts`
|
||||||
|
importTx=`komodo-cli migrate_completeimporttransaction $importTx`
|
||||||
|
|
||||||
|
# Send import
|
||||||
|
komodo-cli -ac_name=$target sendrawtransaction $importTx
|
||||||
@@ -149,12 +149,137 @@ class CryptoConditionsTest (BitcoinTestFramework):
|
|||||||
result = rpc.dicelist()
|
result = rpc.dicelist()
|
||||||
assert_equal(result, [])
|
assert_equal(result, [])
|
||||||
|
|
||||||
|
# creating dice plan with too long name (>8 chars)
|
||||||
|
result = rpc.dicefund("THISISTOOLONG", "10000", "10", "10000", "10", "5")
|
||||||
|
assert_error(result)
|
||||||
|
|
||||||
|
# creating dice plan with < 100 funding
|
||||||
|
result = rpc.dicefund("LUCKY","10","1","10000","10","5")
|
||||||
|
assert_error(result)
|
||||||
|
|
||||||
|
# creating dice plan with 0 blocks timeout
|
||||||
|
result = rpc.dicefund("LUCKY","10","1","10000","10","0")
|
||||||
|
assert_error(result)
|
||||||
|
|
||||||
|
# creating dice plan
|
||||||
|
dicefundtx = rpc.dicefund("LUCKY","1000","1","800","10","5")
|
||||||
|
diceid = self.send_and_mine(dicefundtx['hex'])
|
||||||
|
|
||||||
|
# checking if it in plans list now
|
||||||
|
result = rpc.dicelist()
|
||||||
|
assert_equal(result[0], diceid)
|
||||||
|
|
||||||
|
# set dice name for futher usage
|
||||||
|
dicename = "LUCKY"
|
||||||
|
|
||||||
|
# adding zero funds to plan
|
||||||
|
result = rpc.diceaddfunds(dicename,diceid,"0")
|
||||||
|
assert_error(result)
|
||||||
|
|
||||||
|
# adding negative funds to plan
|
||||||
|
result = rpc.diceaddfunds(dicename,diceid,"-1")
|
||||||
|
assert_error(result)
|
||||||
|
|
||||||
|
# adding funds to plan
|
||||||
|
addfundstx = rpc.diceaddfunds(dicename,diceid,"1100")
|
||||||
|
result = self.send_and_mine(addfundstx['hex'])
|
||||||
|
|
||||||
|
# checking if funds added to plan
|
||||||
|
result = rpc.diceinfo(diceid)
|
||||||
|
assert_equal(result["funding"], "2100.00000000")
|
||||||
|
|
||||||
|
# not valid dice info checking
|
||||||
result = rpc.diceinfo("invalid")
|
result = rpc.diceinfo("invalid")
|
||||||
assert_error(result)
|
assert_error(result)
|
||||||
|
|
||||||
result = rpc.dicefund("THISISTOOLONG", "10000", "10", "10000", "10", "5")
|
# placing 0 amount bet
|
||||||
|
result = rpc.dicebet(dicename,diceid,"0","1")
|
||||||
assert_error(result)
|
assert_error(result)
|
||||||
|
|
||||||
|
# placing negative amount bet
|
||||||
|
result = rpc.dicebet(dicename,diceid,"-1","1")
|
||||||
|
assert_error(result)
|
||||||
|
|
||||||
|
# placing bet more than maxbet
|
||||||
|
result = rpc.dicebet(dicename,diceid,"900","1")
|
||||||
|
assert_error(result)
|
||||||
|
|
||||||
|
# placing bet with amount more than funding
|
||||||
|
result = rpc.dicebet(dicename,diceid,"3000","1")
|
||||||
|
assert_error(result)
|
||||||
|
|
||||||
|
# placing bet with potential won more than funding
|
||||||
|
result = rpc.dicebet(dicename,diceid,"750","9")
|
||||||
|
assert_error(result)
|
||||||
|
|
||||||
|
# placing 0 odds bet
|
||||||
|
result = rpc.dicebet(dicename,diceid,"1","0")
|
||||||
|
assert_error(result)
|
||||||
|
|
||||||
|
# placing negative odds bet
|
||||||
|
result = rpc.dicebet(dicename,diceid,"1","-1")
|
||||||
|
assert_error(result)
|
||||||
|
|
||||||
|
# placing bet with odds more than allowed
|
||||||
|
result = rpc.dicebet(dicename,diceid,"1","11")
|
||||||
|
assert_error(result)
|
||||||
|
|
||||||
|
# placing bet with not correct dice name
|
||||||
|
result = rpc.dicebet("nope",diceid,"100","1")
|
||||||
|
assert_error(result)
|
||||||
|
|
||||||
|
# placing bet with not correct dice id
|
||||||
|
result = rpc.dicebet(dicename,self.pubkey,"100","1")
|
||||||
|
assert_error(result)
|
||||||
|
|
||||||
|
# valid bet placing
|
||||||
|
placebet = rpc.dicebet(dicename,diceid,"100","1")
|
||||||
|
betid = self.send_and_mine(placebet["hex"])
|
||||||
|
assert result, "bet placed"
|
||||||
|
|
||||||
|
# check bet status
|
||||||
|
result = rpc.dicestatus(dicename,diceid,betid)
|
||||||
|
assert_success(result)
|
||||||
|
|
||||||
|
# have to make some entropy for the next test
|
||||||
|
entropytx = 0
|
||||||
|
fundingsum = 1
|
||||||
|
while entropytx < 10:
|
||||||
|
fundingsuminput = str(fundingsum)
|
||||||
|
fundinghex = rpc.diceaddfunds(dicename,diceid,fundingsuminput)
|
||||||
|
result = self.send_and_mine(fundinghex['hex'])
|
||||||
|
entropytx = entropytx + 1
|
||||||
|
fundingsum = fundingsum + 1
|
||||||
|
|
||||||
|
rpc.generate(2)
|
||||||
|
|
||||||
|
# note initial dice funding state at this point.
|
||||||
|
# TODO: track player balance somehow (hard to do because of mining and fees)
|
||||||
|
diceinfo = rpc.diceinfo(diceid)
|
||||||
|
funding = float(diceinfo['funding'])
|
||||||
|
|
||||||
|
# placing same amount bets with amount 1 and odds 1:2, checking if balance changed correct
|
||||||
|
losscounter = 0
|
||||||
|
wincounter = 0
|
||||||
|
betcounter = 0
|
||||||
|
|
||||||
|
while (betcounter < 10):
|
||||||
|
placebet = rpc.dicebet(dicename,diceid,"1","1")
|
||||||
|
betid = self.send_and_mine(placebet["hex"])
|
||||||
|
finish = rpc.dicefinish(dicename,diceid,betid)
|
||||||
|
self.send_and_mine(finish["hex"])
|
||||||
|
betresult = rpc.dicestatus(dicename,diceid,betid)
|
||||||
|
betcounter = betcounter + 1
|
||||||
|
if betresult["status"] == "loss":
|
||||||
|
losscounter = losscounter + 1
|
||||||
|
elif betresult["status"] == "win":
|
||||||
|
wincounter = wincounter + 1
|
||||||
|
|
||||||
|
# funding balance should increase if player loss, decrease if player won
|
||||||
|
fundbalanceguess = funding + losscounter - wincounter
|
||||||
|
fundinfoactual = rpc.diceinfo(diceid)
|
||||||
|
assert_equal(round(fundbalanceguess),round(float(fundinfoactual['funding'])))
|
||||||
|
|
||||||
def run_token_tests(self):
|
def run_token_tests(self):
|
||||||
rpc = self.nodes[0]
|
rpc = self.nodes[0]
|
||||||
result = rpc.tokenaddress()
|
result = rpc.tokenaddress()
|
||||||
@@ -170,12 +295,15 @@ class CryptoConditionsTest (BitcoinTestFramework):
|
|||||||
result = rpc.tokenlist()
|
result = rpc.tokenlist()
|
||||||
assert_equal(result, [])
|
assert_equal(result, [])
|
||||||
|
|
||||||
|
# trying to create token with negaive supply
|
||||||
result = rpc.tokencreate("NUKE", "-1987420", "no bueno supply")
|
result = rpc.tokencreate("NUKE", "-1987420", "no bueno supply")
|
||||||
assert_error(result)
|
assert_error(result)
|
||||||
|
|
||||||
|
# creating token with name more than 32 chars
|
||||||
result = rpc.tokencreate("NUKE123456789012345678901234567890", "1987420", "name too long")
|
result = rpc.tokencreate("NUKE123456789012345678901234567890", "1987420", "name too long")
|
||||||
assert_error(result)
|
assert_error(result)
|
||||||
|
|
||||||
|
# creating valid token
|
||||||
result = rpc.tokencreate("DUKE", "1987.420", "Duke's custom token")
|
result = rpc.tokencreate("DUKE", "1987.420", "Duke's custom token")
|
||||||
assert_success(result)
|
assert_success(result)
|
||||||
|
|
||||||
@@ -276,23 +404,23 @@ class CryptoConditionsTest (BitcoinTestFramework):
|
|||||||
|
|
||||||
# invalid numtokens bid
|
# invalid numtokens bid
|
||||||
result = rpc.tokenbid("-1", tokenid, "1")
|
result = rpc.tokenbid("-1", tokenid, "1")
|
||||||
assert_error(result);
|
assert_error(result)
|
||||||
|
|
||||||
# invalid numtokens bid
|
# invalid numtokens bid
|
||||||
result = rpc.tokenbid("0", tokenid, "1")
|
result = rpc.tokenbid("0", tokenid, "1")
|
||||||
assert_error(result);
|
assert_error(result)
|
||||||
|
|
||||||
# invalid price bid
|
# invalid price bid
|
||||||
result = rpc.tokenbid("1", tokenid, "-1")
|
result = rpc.tokenbid("1", tokenid, "-1")
|
||||||
assert_error(result);
|
assert_error(result)
|
||||||
|
|
||||||
# invalid price bid
|
# invalid price bid
|
||||||
result = rpc.tokenbid("1", tokenid, "0")
|
result = rpc.tokenbid("1", tokenid, "0")
|
||||||
assert_error(result);
|
assert_error(result)
|
||||||
|
|
||||||
# invalid tokenid bid
|
# invalid tokenid bid
|
||||||
result = rpc.tokenbid("100", "deadbeef", "1")
|
result = rpc.tokenbid("100", "deadbeef", "1")
|
||||||
assert_error(result);
|
assert_error(result)
|
||||||
|
|
||||||
tokenbid = rpc.tokenbid("100", tokenid, "10")
|
tokenbid = rpc.tokenbid("100", tokenid, "10")
|
||||||
tokenbidhex = tokenbid['hex']
|
tokenbidhex = tokenbid['hex']
|
||||||
@@ -330,11 +458,11 @@ class CryptoConditionsTest (BitcoinTestFramework):
|
|||||||
# invalid token transfer amount (have to add status to CC code!)
|
# invalid token transfer amount (have to add status to CC code!)
|
||||||
randompubkey = "021a559101e355c907d9c553671044d619769a6e71d624f68bfec7d0afa6bd6a96"
|
randompubkey = "021a559101e355c907d9c553671044d619769a6e71d624f68bfec7d0afa6bd6a96"
|
||||||
result = rpc.tokentransfer(tokenid,randompubkey,"0")
|
result = rpc.tokentransfer(tokenid,randompubkey,"0")
|
||||||
assert_error(result);
|
assert_error(result)
|
||||||
|
|
||||||
# invalid token transfer amount (have to add status to CC code!)
|
# invalid token transfer amount (have to add status to CC code!)
|
||||||
result = rpc.tokentransfer(tokenid,randompubkey,"-1")
|
result = rpc.tokentransfer(tokenid,randompubkey,"-1")
|
||||||
assert_error(result);
|
assert_error(result)
|
||||||
|
|
||||||
# valid token transfer
|
# valid token transfer
|
||||||
sendtokens = rpc.tokentransfer(tokenid,randompubkey,"1")
|
sendtokens = rpc.tokentransfer(tokenid,randompubkey,"1")
|
||||||
@@ -361,14 +489,31 @@ class CryptoConditionsTest (BitcoinTestFramework):
|
|||||||
result = rpc.rewardsinfo("none")
|
result = rpc.rewardsinfo("none")
|
||||||
assert_error(result)
|
assert_error(result)
|
||||||
|
|
||||||
|
# creating rewards plan with name > 8 chars, should return error
|
||||||
|
result = rpc.rewardscreatefunding("STUFFSTUFF", "7777", "25", "0", "10", "10")
|
||||||
|
assert_error(result)
|
||||||
|
|
||||||
|
# creating rewards plan with 0 funding
|
||||||
|
result = rpc.rewardscreatefunding("STUFF", "0", "25", "0", "10", "10")
|
||||||
|
assert_error(result)
|
||||||
|
|
||||||
|
# creating rewards plan with 0 maxdays
|
||||||
|
result = rpc.rewardscreatefunding("STUFF", "7777", "25", "0", "10", "0")
|
||||||
|
assert_error(result)
|
||||||
|
|
||||||
|
# creating rewards plan with > 25% APR
|
||||||
|
result = rpc.rewardscreatefunding("STUFF", "7777", "30", "0", "10", "10")
|
||||||
|
assert_error(result)
|
||||||
|
|
||||||
|
# creating valid rewards plan
|
||||||
result = rpc.rewardscreatefunding("STUFF", "7777", "25", "0", "10", "10")
|
result = rpc.rewardscreatefunding("STUFF", "7777", "25", "0", "10", "10")
|
||||||
assert result['hex'], 'got raw xtn'
|
assert result['hex'], 'got raw xtn'
|
||||||
txid = rpc.sendrawtransaction(result['hex'])
|
fundingtxid = rpc.sendrawtransaction(result['hex'])
|
||||||
assert txid, 'got txid'
|
assert fundingtxid, 'got txid'
|
||||||
|
|
||||||
# confirm the above xtn
|
# confirm the above xtn
|
||||||
rpc.generate(1)
|
rpc.generate(1)
|
||||||
result = rpc.rewardsinfo(txid)
|
result = rpc.rewardsinfo(fundingtxid)
|
||||||
assert_success(result)
|
assert_success(result)
|
||||||
assert_equal(result['name'], 'STUFF')
|
assert_equal(result['name'], 'STUFF')
|
||||||
assert_equal(result['APR'], "25.00000000")
|
assert_equal(result['APR'], "25.00000000")
|
||||||
@@ -376,39 +521,38 @@ class CryptoConditionsTest (BitcoinTestFramework):
|
|||||||
assert_equal(result['maxseconds'], 864000)
|
assert_equal(result['maxseconds'], 864000)
|
||||||
assert_equal(result['funding'], "7777.00000000")
|
assert_equal(result['funding'], "7777.00000000")
|
||||||
assert_equal(result['mindeposit'], "10.00000000")
|
assert_equal(result['mindeposit'], "10.00000000")
|
||||||
assert_equal(result['fundingtxid'], txid)
|
assert_equal(result['fundingtxid'], fundingtxid)
|
||||||
|
|
||||||
# funding amount must be positive
|
# checking if new plan in rewardslist
|
||||||
result = rpc.rewardsaddfunding("STUFF", txid, "0")
|
result = rpc.rewardslist()
|
||||||
|
assert_equal(result[0], fundingtxid)
|
||||||
|
|
||||||
|
# creating reward plan with already existing name, should return error
|
||||||
|
result = rpc.rewardscreatefunding("STUFF", "7777", "25", "0", "10", "10")
|
||||||
assert_error(result)
|
assert_error(result)
|
||||||
|
|
||||||
result = rpc.rewardsaddfunding("STUFF", txid, "555")
|
# add funding amount must be positive
|
||||||
assert_success(result)
|
result = rpc.rewardsaddfunding("STUFF", fundingtxid, "-1")
|
||||||
fundingtxid = result['hex']
|
|
||||||
assert fundingtxid, "got funding txid"
|
|
||||||
|
|
||||||
result = rpc.rewardslock("STUFF", fundingtxid, "7")
|
|
||||||
assert_error(result)
|
assert_error(result)
|
||||||
|
|
||||||
# the previous xtn has not been broadcasted yet
|
# add funding amount must be positive
|
||||||
result = rpc.rewardsunlock("STUFF", fundingtxid)
|
result = rpc.rewardsaddfunding("STUFF", fundingtxid, "0")
|
||||||
assert_error(result)
|
assert_error(result)
|
||||||
|
|
||||||
# wrong plan name
|
# adding valid funding
|
||||||
result = rpc.rewardsunlock("SHTUFF", fundingtxid)
|
result = rpc.rewardsaddfunding("STUFF", fundingtxid, "555")
|
||||||
assert_error(result)
|
addfundingtxid = self.send_and_mine(result['hex'])
|
||||||
|
assert addfundingtxid, 'got funding txid'
|
||||||
|
|
||||||
txid = rpc.sendrawtransaction(fundingtxid)
|
# checking if funding added to rewardsplan
|
||||||
assert txid, 'got txid from sendrawtransaction'
|
result = rpc.rewardsinfo(fundingtxid)
|
||||||
|
assert_equal(result['funding'], "8332.00000000")
|
||||||
|
|
||||||
# confirm the xtn above
|
# trying to lock funds, locking funds amount must be positive
|
||||||
rpc.generate(1)
|
|
||||||
|
|
||||||
# amount must be positive
|
|
||||||
result = rpc.rewardslock("STUFF", fundingtxid, "-5")
|
result = rpc.rewardslock("STUFF", fundingtxid, "-5")
|
||||||
assert_error(result)
|
assert_error(result)
|
||||||
|
|
||||||
# amount must be positive
|
# trying to lock funds, locking funds amount must be positive
|
||||||
result = rpc.rewardslock("STUFF", fundingtxid, "0")
|
result = rpc.rewardslock("STUFF", fundingtxid, "0")
|
||||||
assert_error(result)
|
assert_error(result)
|
||||||
|
|
||||||
@@ -416,25 +560,26 @@ class CryptoConditionsTest (BitcoinTestFramework):
|
|||||||
result = rpc.rewardslock("STUFF", fundingtxid, "7")
|
result = rpc.rewardslock("STUFF", fundingtxid, "7")
|
||||||
assert_error(result)
|
assert_error(result)
|
||||||
|
|
||||||
# not working
|
# locking funds in rewards plan
|
||||||
#result = rpc.rewardslock("STUFF", fundingtxid, "10")
|
result = rpc.rewardslock("STUFF", fundingtxid, "10")
|
||||||
#assert_success(result)
|
assert_success(result)
|
||||||
#locktxid = result['hex']
|
locktxid = result['hex']
|
||||||
#assert locktxid, "got lock txid"
|
assert locktxid, "got lock txid"
|
||||||
|
|
||||||
# locktxid has not been broadcast yet
|
# locktxid has not been broadcast yet
|
||||||
#result = rpc.rewardsunlock("STUFF", locktxid)
|
result = rpc.rewardsunlock("STUFF", fundingtxid, locktxid)
|
||||||
#assert_error(result)
|
assert_error(result)
|
||||||
|
|
||||||
# broadcast xtn
|
# broadcast xtn
|
||||||
#txid = rpc.sendrawtransaction(locktxid)
|
txid = rpc.sendrawtransaction(locktxid)
|
||||||
#assert txid, 'got txid from sendrawtransaction'
|
assert txid, 'got txid from sendrawtransaction'
|
||||||
|
|
||||||
# confirm the xtn above
|
# confirm the xtn above
|
||||||
#rpc.generate(1)
|
rpc.generate(1)
|
||||||
|
|
||||||
#result = rpc.rewardsunlock("STUFF", locktxid)
|
# will not unlock since reward amount is less than tx fee
|
||||||
#assert_error(result)
|
result = rpc.rewardsunlock("STUFF", fundingtxid, locktxid)
|
||||||
|
assert_error(result)
|
||||||
|
|
||||||
|
|
||||||
def run_test (self):
|
def run_test (self):
|
||||||
|
|||||||
@@ -272,6 +272,7 @@ libbitcoin_server_a_SOURCES = \
|
|||||||
chain.cpp \
|
chain.cpp \
|
||||||
checkpoints.cpp \
|
checkpoints.cpp \
|
||||||
crosschain.cpp \
|
crosschain.cpp \
|
||||||
|
crosschain_authority.cpp \
|
||||||
deprecation.cpp \
|
deprecation.cpp \
|
||||||
httprpc.cpp \
|
httprpc.cpp \
|
||||||
httpserver.cpp \
|
httpserver.cpp \
|
||||||
|
|||||||
@@ -140,45 +140,17 @@ int32_t Eval::GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t time
|
|||||||
return komodo_notaries(pubkeys, height, timestamp);
|
return komodo_notaries(pubkeys, height, timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Eval::CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const
|
bool Eval::CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const
|
||||||
{
|
{
|
||||||
if (tx.vin.size() < 11) return false;
|
if (tx.vin.size() < 11) return false;
|
||||||
|
|
||||||
uint8_t seenNotaries[64] = {0};
|
CrosschainAuthority auth;
|
||||||
uint8_t notaries[64][33];
|
auth.requiredSigs = 11;
|
||||||
int nNotaries = GetNotaries(notaries, height, timestamp);
|
auth.size = GetNotaries(auth.notaries, height, timestamp);
|
||||||
|
|
||||||
BOOST_FOREACH(const CTxIn &txIn, tx.vin)
|
return CheckTxAuthority(tx, auth);
|
||||||
{
|
|
||||||
// Get notary pubkey
|
|
||||||
CTransaction tx;
|
|
||||||
uint256 hashBlock;
|
|
||||||
if (!GetTxUnconfirmed(txIn.prevout.hash, tx, hashBlock)) return false;
|
|
||||||
if (tx.vout.size() < txIn.prevout.n) return false;
|
|
||||||
CScript spk = tx.vout[txIn.prevout.n].scriptPubKey;
|
|
||||||
if (spk.size() != 35) return false;
|
|
||||||
const unsigned char *pk = spk.data();
|
|
||||||
if (pk++[0] != 33) return false;
|
|
||||||
if (pk[33] != OP_CHECKSIG) return false;
|
|
||||||
|
|
||||||
// Check it's a notary
|
|
||||||
for (int i=0; i<nNotaries; i++) {
|
|
||||||
if (!seenNotaries[i]) {
|
|
||||||
if (memcmp(pk, notaries[i], 33) == 0) {
|
|
||||||
seenNotaries[i] = 1;
|
|
||||||
goto found;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
found:;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get MoM from a notarisation tx hash (on KMD)
|
* Get MoM from a notarisation tx hash (on KMD)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ bool Eval::ImportCoin(const std::vector<uint8_t> params, const CTransaction &imp
|
|||||||
|
|
||||||
// check burn amount
|
// check burn amount
|
||||||
{
|
{
|
||||||
uint64_t burnAmount = burnTx.vout[0].nValue;
|
uint64_t burnAmount = burnTx.vout.back().nValue;
|
||||||
if (burnAmount == 0)
|
if (burnAmount == 0)
|
||||||
return Invalid("invalid-burn-amount");
|
return Invalid("invalid-burn-amount");
|
||||||
uint64_t totalOut = 0;
|
uint64_t totalOut = 0;
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeigh
|
|||||||
|
|
||||||
int seenOwnNotarisations = 0;
|
int seenOwnNotarisations = 0;
|
||||||
|
|
||||||
|
int authority = GetSymbolAuthority(symbol);
|
||||||
|
|
||||||
for (int i=0; i<NOTARISATION_SCAN_LIMIT_BLOCKS; i++) {
|
for (int i=0; i<NOTARISATION_SCAN_LIMIT_BLOCKS; i++) {
|
||||||
if (i > kmdHeight) break;
|
if (i > kmdHeight) break;
|
||||||
NotarisationsInBlock notarisations;
|
NotarisationsInBlock notarisations;
|
||||||
@@ -72,8 +74,9 @@ uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeigh
|
|||||||
|
|
||||||
if (seenOwnNotarisations == 1) {
|
if (seenOwnNotarisations == 1) {
|
||||||
BOOST_FOREACH(Notarisation& nota, notarisations) {
|
BOOST_FOREACH(Notarisation& nota, notarisations) {
|
||||||
if (nota.second.ccId == targetCCid)
|
if (GetSymbolAuthority(nota.second.symbol) == authority)
|
||||||
moms.push_back(nota.second.MoM);
|
if (nota.second.ccId == targetCCid)
|
||||||
|
moms.push_back(nota.second.MoM);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,20 @@
|
|||||||
|
|
||||||
#include "cc/eval.h"
|
#include "cc/eval.h"
|
||||||
|
|
||||||
|
const int CROSSCHAIN_KOMODO = 1;
|
||||||
|
const int CROSSCHAIN_TXSCL = 2;
|
||||||
|
const int CROSSCHAIN_STAKED = 3;
|
||||||
|
|
||||||
|
typedef struct CrosschainAuthority {
|
||||||
|
uint8_t notaries[64][33];
|
||||||
|
size_t size;
|
||||||
|
size_t requiredSigs;
|
||||||
|
} CrosschainAuthority;
|
||||||
|
|
||||||
|
extern const CrosschainAuthority auth_STAKED;
|
||||||
|
|
||||||
|
int GetSymbolAuthority(const char* symbol);
|
||||||
|
bool CheckTxAuthority(const CTransaction &tx, CrosschainAuthority auth);
|
||||||
|
|
||||||
/* On assetchain */
|
/* On assetchain */
|
||||||
TxProof GetAssetchainProof(uint256 hash);
|
TxProof GetAssetchainProof(uint256 hash);
|
||||||
|
|||||||
77
src/crosschain_authority.cpp
Normal file
77
src/crosschain_authority.cpp
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#include "cc/eval.h"
|
||||||
|
#include "crosschain.h"
|
||||||
|
#include "notarisationdb.h"
|
||||||
|
|
||||||
|
|
||||||
|
int GetSymbolAuthority(const char* symbol)
|
||||||
|
{
|
||||||
|
if (strlen(symbol) >= 5 && strncmp(symbol, "TXSCL", 5) == 0)
|
||||||
|
return CROSSCHAIN_TXSCL;
|
||||||
|
if (strlen(symbol) >= 6 && strncmp(symbol, "STAKED", 6) == 0)
|
||||||
|
return CROSSCHAIN_STAKED;
|
||||||
|
return CROSSCHAIN_KOMODO;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool CheckTxAuthority(const CTransaction &tx, CrosschainAuthority auth)
|
||||||
|
{
|
||||||
|
EvalRef eval;
|
||||||
|
|
||||||
|
if (tx.vin.size() < auth.requiredSigs) return false;
|
||||||
|
|
||||||
|
uint8_t seen[64];
|
||||||
|
|
||||||
|
BOOST_FOREACH(const CTxIn &txIn, tx.vin)
|
||||||
|
{
|
||||||
|
// Get notary pubkey
|
||||||
|
CTransaction tx;
|
||||||
|
uint256 hashBlock;
|
||||||
|
if (!eval->GetTxUnconfirmed(txIn.prevout.hash, tx, hashBlock)) return false;
|
||||||
|
if (tx.vout.size() < txIn.prevout.n) return false;
|
||||||
|
CScript spk = tx.vout[txIn.prevout.n].scriptPubKey;
|
||||||
|
if (spk.size() != 35) return false;
|
||||||
|
const unsigned char *pk = spk.data();
|
||||||
|
if (pk++[0] != 33) return false;
|
||||||
|
if (pk[33] != OP_CHECKSIG) return false;
|
||||||
|
|
||||||
|
// Check it's a notary
|
||||||
|
for (int i=0; i<auth.size; i++) {
|
||||||
|
if (!seen[i]) {
|
||||||
|
if (memcmp(pk, auth.notaries[i], 33) == 0) {
|
||||||
|
seen[i] = 1;
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
found:;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char *notaries_STAKED[4][2] =
|
||||||
|
{
|
||||||
|
{"alright", "03b4f49a1c22087e0a9dfaa87aef98ef496c544f9f86038f6c9fea4550543a7679"},
|
||||||
|
{"alright", "03b4f49a1c22087e0a9dfaa87aef98ef496c544f9f86038f6c9fea4550543a7679"},
|
||||||
|
{"alright", "03b4f49a1c22087e0a9dfaa87aef98ef496c544f9f86038f6c9fea4550543a7679"},
|
||||||
|
{"alright", "03b4f49a1c22087e0a9dfaa87aef98ef496c544f9f86038f6c9fea4550543a7679"}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TO EDIT:
|
||||||
|
* 1) Edit sigs above
|
||||||
|
* 2) Update size and requiredSigs below
|
||||||
|
*/
|
||||||
|
|
||||||
|
const CrosschainAuthority auth_STAKED = [&](){
|
||||||
|
CrosschainAuthority auth;
|
||||||
|
auth.size = 4;
|
||||||
|
auth.requiredSigs = 2;
|
||||||
|
for (int n=0; n<auth.size; n++)
|
||||||
|
for (size_t i=0; i<33; i++)
|
||||||
|
sscanf(notaries_STAKED[n][1]+(i*2), "%2hhx", auth.notaries[n]+i);
|
||||||
|
return auth;
|
||||||
|
}();
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ bool UnmarshalBurnTx(const CTransaction &burnTx, std::string &targetSymbol, uint
|
|||||||
{
|
{
|
||||||
std::vector<uint8_t> burnOpret;
|
std::vector<uint8_t> burnOpret;
|
||||||
if (burnTx.vout.size() == 0) return false;
|
if (burnTx.vout.size() == 0) return false;
|
||||||
GetOpReturnData(burnTx.vout[0].scriptPubKey, burnOpret);
|
GetOpReturnData(burnTx.vout.back().scriptPubKey, burnOpret);
|
||||||
return E_UNMARSHAL(burnOpret, ss >> VARINT(*targetCCid);
|
return E_UNMARSHAL(burnOpret, ss >> VARINT(*targetCCid);
|
||||||
ss >> targetSymbol;
|
ss >> targetSymbol;
|
||||||
ss >> payoutsHash);
|
ss >> payoutsHash);
|
||||||
@@ -61,7 +61,7 @@ CAmount GetCoinImportValue(const CTransaction &tx)
|
|||||||
CTransaction burnTx;
|
CTransaction burnTx;
|
||||||
std::vector<CTxOut> payouts;
|
std::vector<CTxOut> payouts;
|
||||||
if (UnmarshalImportTx(tx, proof, burnTx, payouts)) {
|
if (UnmarshalImportTx(tx, proof, burnTx, payouts)) {
|
||||||
return burnTx.vout.size() ? burnTx.vout[0].nValue : 0;
|
return burnTx.vout.size() ? burnTx.vout.back().nValue : 0;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
17
src/main.cpp
17
src/main.cpp
@@ -1547,14 +1547,17 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||||||
KOMODO_ON_DEMAND++;
|
KOMODO_ON_DEMAND++;
|
||||||
pool.addUnchecked(hash, entry, !IsInitialBlockDownload());
|
pool.addUnchecked(hash, entry, !IsInitialBlockDownload());
|
||||||
|
|
||||||
// Add memory address index
|
if (!tx.IsCoinImport())
|
||||||
if (fAddressIndex) {
|
{
|
||||||
pool.addAddressIndex(entry, view);
|
// Add memory address index
|
||||||
}
|
if (fAddressIndex) {
|
||||||
|
pool.addAddressIndex(entry, view);
|
||||||
|
}
|
||||||
|
|
||||||
// Add memory spent index
|
// Add memory spent index
|
||||||
if (fSpentIndex) {
|
if (fSpentIndex) {
|
||||||
pool.addSpentIndex(entry, view);
|
pool.addSpentIndex(entry, view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "notarisationdb.h"
|
#include "notarisationdb.h"
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
#include "cc/eval.h"
|
#include "cc/eval.h"
|
||||||
|
#include "crosschain.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
@@ -21,27 +22,27 @@ NotarisationsInBlock ScanBlockNotarisations(const CBlock &block, int nHeight)
|
|||||||
for (unsigned int i = 0; i < block.vtx.size(); i++) {
|
for (unsigned int i = 0; i < block.vtx.size(); i++) {
|
||||||
CTransaction tx = block.vtx[i];
|
CTransaction tx = block.vtx[i];
|
||||||
|
|
||||||
// Special case for TXSCL. Should prob be removed at some point.
|
NotarisationData data;
|
||||||
bool isTxscl = 0;
|
bool parsed = ParseNotarisationOpReturn(tx, data);
|
||||||
{
|
if (!parsed) data = NotarisationData();
|
||||||
NotarisationData data;
|
int authority = GetSymbolAuthority(data.symbol);
|
||||||
if (ParseNotarisationOpReturn(tx, data))
|
|
||||||
if (strlen(data.symbol) >= 5 && strncmp(data.symbol, "TXSCL", 5) == 0)
|
if (authority == CROSSCHAIN_KOMODO) {
|
||||||
isTxscl = 1;
|
if (!eval->CheckNotaryInputs(tx, nHeight, block.nTime))
|
||||||
|
continue;
|
||||||
|
} else if (authority == CROSSCHAIN_STAKED) {
|
||||||
|
if (!CheckTxAuthority(tx, auth_STAKED))
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isTxscl || eval->CheckNotaryInputs(tx, nHeight, block.nTime)) {
|
if (parsed) {
|
||||||
NotarisationData data;
|
vNotarisations.push_back(std::make_pair(tx.GetHash(), data));
|
||||||
if (ParseNotarisationOpReturn(tx, data)) {
|
//printf("Parsed a notarisation for: %s, txid:%s, ccid:%i, momdepth:%i\n",
|
||||||
vNotarisations.push_back(std::make_pair(tx.GetHash(), data));
|
// data.symbol, tx.GetHash().GetHex().data(), data.ccId, data.MoMDepth);
|
||||||
//printf("Parsed a notarisation for: %s, txid:%s, ccid:%i, momdepth:%i\n",
|
//if (!data.MoMoM.IsNull()) printf("MoMoM:%s\n", data.MoMoM.GetHex().data());
|
||||||
// data.symbol, tx.GetHash().GetHex().data(), data.ccId, data.MoMDepth);
|
} else
|
||||||
//if (!data.MoMoM.IsNull()) printf("MoMoM:%s\n", data.MoMoM.GetHex().data());
|
LogPrintf("WARNING: Couldn't parse notarisation for tx: %s at height %i\n",
|
||||||
}
|
tx.GetHash().GetHex().data(), nHeight);
|
||||||
else
|
|
||||||
LogPrintf("WARNING: Couldn't parse notarisation for tx: %s at height %i\n",
|
|
||||||
tx.GetHash().GetHex().data(), nHeight);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return vNotarisations;
|
return vNotarisations;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,5 +24,6 @@ bool GetBackNotarisation(uint256 notarisationHash, Notarisation &n);
|
|||||||
void WriteBackNotarisations(const NotarisationsInBlock notarisations, CLevelDBBatch &batch);
|
void WriteBackNotarisations(const NotarisationsInBlock notarisations, CLevelDBBatch &batch);
|
||||||
void EraseBackNotarisations(const NotarisationsInBlock notarisations, CLevelDBBatch &batch);
|
void EraseBackNotarisations(const NotarisationsInBlock notarisations, CLevelDBBatch &batch);
|
||||||
int ScanNotarisationsDB(int height, std::string symbol, int scanLimitBlocks, Notarisation& out);
|
int ScanNotarisationsDB(int height, std::string symbol, int scanLimitBlocks, Notarisation& out);
|
||||||
|
bool IsTXSCL(const char* symbol);
|
||||||
|
|
||||||
#endif /* NOTARISATIONDB_H */
|
#endif /* NOTARISATIONDB_H */
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ TEST_F(TestCoinImport, testInvalidBurnOutputs)
|
|||||||
|
|
||||||
TEST_F(TestCoinImport, testInvalidBurnParams)
|
TEST_F(TestCoinImport, testInvalidBurnParams)
|
||||||
{
|
{
|
||||||
burnTx.vout[0].scriptPubKey = CScript() << OP_RETURN << E_MARSHAL(ss << VARINT(testCcid));
|
burnTx.vout.back().scriptPubKey = CScript() << OP_RETURN << E_MARSHAL(ss << VARINT(testCcid));
|
||||||
MoMoM = burnTx.GetHash(); // TODO: an actual branch
|
MoMoM = burnTx.GetHash(); // TODO: an actual branch
|
||||||
CTransaction tx = MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts);
|
CTransaction tx = MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts);
|
||||||
TestRunCCEval(tx);
|
TestRunCCEval(tx);
|
||||||
@@ -198,7 +198,7 @@ TEST_F(TestCoinImport, testWrongChainId)
|
|||||||
|
|
||||||
TEST_F(TestCoinImport, testInvalidBurnAmount)
|
TEST_F(TestCoinImport, testInvalidBurnAmount)
|
||||||
{
|
{
|
||||||
burnTx.vout[0].nValue = 0;
|
burnTx.vout.back().nValue = 0;
|
||||||
MoMoM = burnTx.GetHash(); // TODO: an actual branch
|
MoMoM = burnTx.GetHash(); // TODO: an actual branch
|
||||||
CTransaction tx = MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts);
|
CTransaction tx = MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts);
|
||||||
TestRunCCEval(tx);
|
TestRunCCEval(tx);
|
||||||
|
|||||||
@@ -4985,6 +4985,11 @@ UniValue rewardscreatefunding(const UniValue& params, bool fHelp)
|
|||||||
name = (char *)params[0].get_str().c_str();
|
name = (char *)params[0].get_str().c_str();
|
||||||
funds = atof(params[1].get_str().c_str()) * COIN;
|
funds = atof(params[1].get_str().c_str()) * COIN;
|
||||||
|
|
||||||
|
if (!VALID_PLAN_NAME(name)) {
|
||||||
|
ERR_RESULT(strprintf("Plan name can be at most %d ASCII characters",PLAN_NAME_MAX));
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
if ( funds <= 0 ) {
|
if ( funds <= 0 ) {
|
||||||
ERR_RESULT("funds must be positive");
|
ERR_RESULT("funds must be positive");
|
||||||
return result;
|
return result;
|
||||||
@@ -5046,6 +5051,11 @@ UniValue rewardslock(const UniValue& params, bool fHelp)
|
|||||||
fundingtxid = Parseuint256((char *)params[1].get_str().c_str());
|
fundingtxid = Parseuint256((char *)params[1].get_str().c_str());
|
||||||
amount = atof(params[2].get_str().c_str()) * COIN;
|
amount = atof(params[2].get_str().c_str()) * COIN;
|
||||||
hex = RewardsLock(0,name,fundingtxid,amount);
|
hex = RewardsLock(0,name,fundingtxid,amount);
|
||||||
|
|
||||||
|
if (!VALID_PLAN_NAME(name)) {
|
||||||
|
ERR_RESULT(strprintf("Plan name can be at most %d ASCII characters",PLAN_NAME_MAX));
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
if ( CCerror != "" ){
|
if ( CCerror != "" ){
|
||||||
ERR_RESULT(CCerror);
|
ERR_RESULT(CCerror);
|
||||||
} else if ( amount > 0 ) {
|
} else if ( amount > 0 ) {
|
||||||
@@ -5071,6 +5081,11 @@ UniValue rewardsaddfunding(const UniValue& params, bool fHelp)
|
|||||||
fundingtxid = Parseuint256((char *)params[1].get_str().c_str());
|
fundingtxid = Parseuint256((char *)params[1].get_str().c_str());
|
||||||
amount = atof(params[2].get_str().c_str()) * COIN;
|
amount = atof(params[2].get_str().c_str()) * COIN;
|
||||||
hex = RewardsAddfunding(0,name,fundingtxid,amount);
|
hex = RewardsAddfunding(0,name,fundingtxid,amount);
|
||||||
|
|
||||||
|
if (!VALID_PLAN_NAME(name)) {
|
||||||
|
ERR_RESULT(strprintf("Plan name can be at most %d ASCII characters",PLAN_NAME_MAX));
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
if (CCerror != "") {
|
if (CCerror != "") {
|
||||||
ERR_RESULT(CCerror);
|
ERR_RESULT(CCerror);
|
||||||
} else if (amount > 0) {
|
} else if (amount > 0) {
|
||||||
@@ -5099,6 +5114,11 @@ UniValue rewardsunlock(const UniValue& params, bool fHelp)
|
|||||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||||
name = (char *)params[0].get_str().c_str();
|
name = (char *)params[0].get_str().c_str();
|
||||||
fundingtxid = Parseuint256((char *)params[1].get_str().c_str());
|
fundingtxid = Parseuint256((char *)params[1].get_str().c_str());
|
||||||
|
|
||||||
|
if (!VALID_PLAN_NAME(name)) {
|
||||||
|
ERR_RESULT(strprintf("Plan name can be at most %d ASCII characters",PLAN_NAME_MAX));
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
if ( params.size() > 2 )
|
if ( params.size() > 2 )
|
||||||
txid = Parseuint256((char *)params[2].get_str().c_str());
|
txid = Parseuint256((char *)params[2].get_str().c_str());
|
||||||
else memset(&txid,0,sizeof(txid));
|
else memset(&txid,0,sizeof(txid));
|
||||||
|
|||||||
Reference in New Issue
Block a user