From a328ed25cb5e9563b9e0e47b04e52dcb38b1add0 Mon Sep 17 00:00:00 2001 From: mike31 Date: Tue, 14 Aug 2018 17:09:18 +0300 Subject: [PATCH 001/280] Filters, initial code --- src/Makefile.am | 2 + src/chainparams/globals.h | 1 + src/chainparams/paramlist.h | 6 +- src/chainparams/params.cpp | 20 ++ src/chainparams/params.h | 1 + src/chainparams/state.h | 1 + src/core/init.cpp | 13 ++ src/core/init.h | 2 + src/core/main.cpp | 1 + src/core/main.h | 1 + src/entities/asset.cpp | 13 ++ src/entities/asset.h | 23 ++- src/protocol/filter.cpp | 42 ++++ src/protocol/filter.h | 54 ++++++ src/protocol/multichainscript.cpp | 12 +- src/rpc/rpcfilters.cpp | 305 ++++++++++++++++++++++++++++++ src/rpc/rpchelp.cpp | 28 +++ src/rpc/rpclist.cpp | 1 + src/rpc/rpcserver.h | 1 + src/rpc/rpcstreams.cpp | 5 + src/utils/utilwrapper.cpp | 1 + src/version/version.cpp | 2 +- 22 files changed, 518 insertions(+), 17 deletions(-) create mode 100644 src/protocol/filter.cpp create mode 100644 src/protocol/filter.h create mode 100644 src/rpc/rpcfilters.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 6305b149..85298cfe 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -180,6 +180,7 @@ libbitcoin_server_a_SOURCES = \ protocol/multichaintx.cpp \ protocol/multichainblock.cpp \ custom/custom_server.cpp \ + protocol/filter.cpp \ protocol/relay.cpp \ protocol/handshake.cpp \ chain/merkleblock.cpp \ @@ -225,6 +226,7 @@ libbitcoin_wallet_a_SOURCES = \ rpc/rpcassets.cpp \ rpc/rpcstreams.cpp \ rpc/rpcupgrades.cpp \ + rpc/rpcfilters.cpp \ wallet/wallet.cpp \ wallet/walletcoins.cpp \ wallet/wallettxs.cpp \ diff --git a/src/chainparams/globals.h b/src/chainparams/globals.h index 0e55f380..53662346 100644 --- a/src/chainparams/globals.h +++ b/src/chainparams/globals.h @@ -44,6 +44,7 @@ int MCP_ANYONE_CAN_CREATE=0; int MCP_ANYONE_CAN_ISSUE=0; int MCP_ANYONE_CAN_ACTIVATE=0; int64_t MCP_MINIMUM_PER_OUTPUT=0; +int MCP_ALLOW_FILTERS=0; int MCP_ALLOW_ARBITRARY_OUTPUTS=1; int MCP_ALLOW_MULTISIG_OUTPUTS=0; int MCP_ALLOW_P2SH_OUTPUTS=0; diff --git a/src/chainparams/paramlist.h b/src/chainparams/paramlist.h index 9678e79b..164ce1f4 100644 --- a/src/chainparams/paramlist.h +++ b/src/chainparams/paramlist.h @@ -93,8 +93,12 @@ static const mc_OneMultichainParam MultichainParamArray[] = "Anyone can grant or revoke all permissions."}, { "supportminerprecheck" , "support-miner-precheck" , MC_PRM_BOOLEAN | MC_PRM_USER | MC_PRM_CLONE , -1, 1, 0, 0, 0.0, 10007, 0, "-mc-supportminerprecheck", - "allowarbitraryoutputs","", + "allowfilters","", "Require special metadata output with cached scriptPubKey for input, to support advanced miner checks."}, + { "allowfilters" , "allow-filters" , + MC_PRM_BOOLEAN | MC_PRM_USER | MC_PRM_CLONE , -1, 1, 0, 0, 0.0, 20004, 0, "-mc-allowfilters", + "allowarbitraryoutputs","", + "Allow smart filters."}, { "allowarbitraryoutputs" , "allow-arbitrary-outputs" , MC_PRM_BOOLEAN | MC_PRM_USER | MC_PRM_CLONE , -1, 0, 0, 0, 0.0, 10009, 0, "-mc-allowarbitraryoutputs", "allowp2shoutputs","", diff --git a/src/chainparams/params.cpp b/src/chainparams/params.cpp index 52d5b522..caa0fd10 100644 --- a/src/chainparams/params.cpp +++ b/src/chainparams/params.cpp @@ -2037,3 +2037,23 @@ int mc_Features::FixedIn1001120003() return ret; } +int mc_Features::Filters() +{ + int ret=0; + if(mc_gState->m_NetworkParams->IsProtocolMultichain() == 0) + { + return 0; + } + int protocol=mc_gState->m_NetworkParams->ProtocolVersion(); + + if(protocol) + { + if(protocol >= 20004) + { + ret=1; + } + } + + return ret; +} + diff --git a/src/chainparams/params.h b/src/chainparams/params.h index ba1390c5..16f04d7b 100644 --- a/src/chainparams/params.h +++ b/src/chainparams/params.h @@ -77,6 +77,7 @@ extern int MCP_ANYONE_CAN_CREATE; extern int MCP_ANYONE_CAN_ISSUE; extern int MCP_ANYONE_CAN_ACTIVATE; extern int64_t MCP_MINIMUM_PER_OUTPUT; +extern int MCP_ALLOW_FILTERS; extern int MCP_ALLOW_ARBITRARY_OUTPUTS; extern int MCP_ALLOW_MULTISIG_OUTPUTS; extern int MCP_ALLOW_P2SH_OUTPUTS; diff --git a/src/chainparams/state.h b/src/chainparams/state.h index 7ba26417..b20ca517 100644 --- a/src/chainparams/state.h +++ b/src/chainparams/state.h @@ -144,6 +144,7 @@ typedef struct mc_Features int Chunks(); int FixedIn1001020003(); int FixedIn1001120003(); + int Filters(); } mc_Features; typedef struct mc_BlockHeaderInfo diff --git a/src/core/init.cpp b/src/core/init.cpp index 07ea282a..84b8f9fd 100644 --- a/src/core/init.cpp +++ b/src/core/init.cpp @@ -66,6 +66,7 @@ CWallet* pwalletMain = NULL; mc_WalletTxs* pwalletTxsMain = NULL; #endif mc_RelayManager* pRelayManager = NULL; +mc_FilterEngine* pFilterEngine = NULL; bool fFeeEstimatesInitialized = false; extern int JSON_DOUBLE_DECIMAL_DIGITS; @@ -225,6 +226,12 @@ void Shutdown() delete pRelayManager; pRelayManager=NULL; } + if(pFilterEngine) + { + delete pFilterEngine; + pFilterEngine=NULL; + } + /* MCHN END */ #endif globalVerifyHandle.reset(); @@ -1873,6 +1880,12 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) return InitError(_("Failed to listen on any port. Use -listen=0 if you want this.")); } /* MCHN START */ + pFilterEngine=new mc_FilterEngine; + if(pFilterEngine->Initialize()) + { + return InitError(_("Couldn't initialize filter engine.")); + } + pRelayManager=new mc_RelayManager; int max_ips=64; diff --git a/src/core/init.h b/src/core/init.h index f0ee4bd6..475f3ac0 100644 --- a/src/core/init.h +++ b/src/core/init.h @@ -12,6 +12,7 @@ class CWallet; struct mc_WalletTxs; struct mc_RelayManager; +struct mc_FilterEngine; namespace boost { @@ -21,6 +22,7 @@ class thread_group; extern CWallet* pwalletMain; extern mc_WalletTxs* pwalletTxsMain; extern mc_RelayManager* pRelayManager; +extern mc_FilterEngine* pFilterEngine; void StartShutdown(); diff --git a/src/core/main.cpp b/src/core/main.cpp index 175edd5f..425d3fea 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -35,6 +35,7 @@ extern mc_WalletTxs* pwalletTxsMain; extern mc_RelayManager* pRelayManager; +extern mc_FilterEngine* pFilterEngine; /* MCHN END */ diff --git a/src/core/main.h b/src/core/main.h index 2444f3f7..7628a648 100644 --- a/src/core/main.h +++ b/src/core/main.h @@ -22,6 +22,7 @@ #include "script/script.h" #include "script/sigcache.h" #include "script/standard.h" +#include "protocol/filter.h" #include "utils/sync.h" #include "utils/tinyformat.h" #include "chain/txmempool.h" diff --git a/src/entities/asset.cpp b/src/entities/asset.cpp index 1f75f5b4..ce9442a7 100644 --- a/src/entities/asset.cpp +++ b/src/entities/asset.cpp @@ -1959,9 +1959,22 @@ void mc_AssetDB::Dump() uint32_t mc_AssetDB::MaxEntityType() { + if(mc_gState->m_Features->Filters() == 0) + { + return MC_ENT_TYPE_UPGRADE; + } return MC_ENT_TYPE_MAX; } +int mc_AssetDB::MaxScriptSize() +{ + if(mc_gState->m_Features->Filters() == 0) + { + return MC_ENT_MAX_SCRIPT_SIZE_BEFORE_FILTERS; + } + return MC_ENT_MAX_SCRIPT_SIZE; +} + int mc_AssetDB::MaxStoredIssuers() { return MC_ENT_MAX_STORED_ISSUERS; diff --git a/src/entities/asset.h b/src/entities/asset.h index 0c512cc5..56dbcc0e 100644 --- a/src/entities/asset.h +++ b/src/entities/asset.h @@ -31,14 +31,15 @@ -#define MC_ENT_REF_SIZE 10 -#define MC_ENT_REF_PREFIX_SIZE 2 -#define MC_ENT_MAX_NAME_SIZE 32 -#define MC_ENT_MAX_ITEM_KEY_SIZE 256 -#define MC_ENT_MAX_SCRIPT_SIZE 4096 -#define MC_ENT_MAX_FIXED_FIELDS_SIZE 128 -#define MC_ENT_MAX_STORED_ISSUERS 128 -#define MC_ENT_SCRIPT_ALLOC_SIZE 8192 // > MC_ENT_MAX_SCRIPT_SIZE + MC_ENT_MAX_FIXED_FIELDS_SIZE + 27*MC_ENT_MAX_STORED_ISSUERS +#define MC_ENT_REF_SIZE 10 +#define MC_ENT_REF_PREFIX_SIZE 2 +#define MC_ENT_MAX_NAME_SIZE 32 +#define MC_ENT_MAX_ITEM_KEY_SIZE 256 +#define MC_ENT_MAX_SCRIPT_SIZE_BEFORE_FILTERS 4096 +#define MC_ENT_MAX_SCRIPT_SIZE 65536 +#define MC_ENT_MAX_FIXED_FIELDS_SIZE 128 +#define MC_ENT_MAX_STORED_ISSUERS 128 +#define MC_ENT_SCRIPT_ALLOC_SIZE 66000 // > MC_ENT_MAX_SCRIPT_SIZE + MC_ENT_MAX_FIXED_FIELDS_SIZE + 27*MC_ENT_MAX_STORED_ISSUERS #define MC_ENT_KEY_SIZE 32 #define MC_ENT_KEYTYPE_TXID 0x00000001 @@ -54,7 +55,8 @@ #define MC_ENT_TYPE_STREAM 0x02 #define MC_ENT_TYPE_STREAM_MAX 0x0F #define MC_ENT_TYPE_UPGRADE 0x10 -#define MC_ENT_TYPE_MAX 0x10 +#define MC_ENT_TYPE_FILTER 0x11 +#define MC_ENT_TYPE_MAX 0x11 #define MC_ENT_SPRM_NAME 0x01 #define MC_ENT_SPRM_FOLLOW_ONS 0x02 @@ -67,6 +69,8 @@ #define MC_ENT_SPRM_UPGRADE_PROTOCOL_VERSION 0x42 #define MC_ENT_SPRM_UPGRADE_START_BLOCK 0x43 #define MC_ENT_SPRM_UPGRADE_CHAIN_PARAMS 0x44 +#define MC_ENT_SPRM_FILTER_ENTITY 0x45 +#define MC_ENT_SPRM_FILTER_JS 0x46 #define MC_ENT_SPRM_TIMESTAMP 0x81 #define MC_ENT_SPRM_CHUNK_HASH 0x82 @@ -275,6 +279,7 @@ typedef struct mc_AssetDB void RemoveFiles(); uint32_t MaxEntityType(); + int MaxScriptSize(); int MaxStoredIssuers(); //Internal functions diff --git a/src/protocol/filter.cpp b/src/protocol/filter.cpp new file mode 100644 index 00000000..2b906f99 --- /dev/null +++ b/src/protocol/filter.cpp @@ -0,0 +1,42 @@ +// Copyright (c) 2014-2017 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#include "protocol/filter.h" + +int mc_Filter::Zero() +{ + return MC_ERR_NOERROR; +} + +int mc_Filter::Destroy() +{ + return MC_ERR_NOERROR; +} + + +int mc_FilterEngine::Zero() +{ + return MC_ERR_NOERROR; +} + +int mc_FilterEngine::Destroy() +{ + return MC_ERR_NOERROR; +} + +int mc_FilterEngine::Initialize() +{ + return MC_ERR_NOERROR; +} + +int mc_FilterEngine::CreateFilter(const char *script,const char* main_name,mc_Filter *filter,std::string &strResult) +{ + strResult=""; + return MC_ERR_NOERROR; +} + +int mc_FilterEngine::RunFilter(const mc_Filter& filter,std::string &strResult) +{ + strResult=""; + return MC_ERR_NOERROR; +} diff --git a/src/protocol/filter.h b/src/protocol/filter.h new file mode 100644 index 00000000..d3370160 --- /dev/null +++ b/src/protocol/filter.h @@ -0,0 +1,54 @@ +// Copyright (c) 2014-2017 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#ifndef MULTICHAIN_FILTER_H +#define MULTICHAIN_FILTER_H + +#include "utils/declare.h" +#include "utils/util.h" + + + +typedef struct mc_Filter +{ + void *m_Impl; + + mc_Filter() + { + Zero(); + } + + ~mc_Filter() + { + Destroy(); + } + + + int Zero(); + int Destroy(); +} mc_Filter; + +typedef struct mc_FilterEngine +{ + mc_FilterEngine() + { + Zero(); + } + + ~mc_FilterEngine() + { + Destroy(); + } + + + int Initialize(); + + int CreateFilter(const char *script,const char* main_name,mc_Filter *filter,std::string &strResult); + int RunFilter(const mc_Filter& filter,std::string &strResult); + + int Zero(); + int Destroy(); + +} mc_FilterEngine; + +#endif /* MULTICHAIN_FILTER_H */ \ No newline at end of file diff --git a/src/protocol/multichainscript.cpp b/src/protocol/multichainscript.cpp index 01251313..31232575 100644 --- a/src/protocol/multichainscript.cpp +++ b/src/protocol/multichainscript.cpp @@ -1287,7 +1287,7 @@ int mc_Script::GetAssetDetails(char* name,int* multiple,unsigned char* script,in return MC_ERR_WRONG_SCRIPT; } - if(m_lpCoord[m_CurrentElement*2+1] > MC_DCT_SCRIPT_IDENTIFIER_LEN+1+MC_ENT_MAX_SCRIPT_SIZE + 4 + MC_ENT_MAX_NAME_SIZE + 1) + if(m_lpCoord[m_CurrentElement*2+1] > MC_DCT_SCRIPT_IDENTIFIER_LEN+1+mc_gState->m_Assets->MaxScriptSize() + 4 + MC_ENT_MAX_NAME_SIZE + 1) { return MC_ERR_WRONG_SCRIPT; } @@ -1342,7 +1342,7 @@ int mc_Script::SetAssetDetails(const char*name,int multiple,const unsigned char* int err; unsigned char buf[MC_DCT_SCRIPT_IDENTIFIER_LEN+1+4]; - if((script_size<0) || (script_size>MC_ENT_MAX_SCRIPT_SIZE)) + if((script_size<0) || (script_size>mc_gState->m_Assets->MaxScriptSize())) { return MC_ERR_INVALID_PARAMETER_VALUE; } @@ -1402,7 +1402,7 @@ int mc_Script::GetGeneralDetails(unsigned char* script,int *script_size) return MC_ERR_WRONG_SCRIPT; } - if(m_lpCoord[m_CurrentElement*2+1] > MC_DCT_SCRIPT_IDENTIFIER_LEN+1+MC_ENT_MAX_SCRIPT_SIZE) + if(m_lpCoord[m_CurrentElement*2+1] > MC_DCT_SCRIPT_IDENTIFIER_LEN+1+mc_gState->m_Assets->MaxScriptSize()) { return MC_ERR_WRONG_SCRIPT; } @@ -1437,7 +1437,7 @@ int mc_Script::SetGeneralDetails(const unsigned char* script,int script_size) int err; unsigned char buf[MC_DCT_SCRIPT_IDENTIFIER_LEN+1+4]; - if((script_size<0) || (script_size>MC_ENT_MAX_SCRIPT_SIZE)) + if((script_size<0) || (script_size>mc_gState->m_Assets->MaxScriptSize())) { return MC_ERR_INVALID_PARAMETER_VALUE; } @@ -1540,7 +1540,7 @@ int mc_Script::GetNewEntityType(uint32_t *type,int *update,unsigned char* script return MC_ERR_WRONG_SCRIPT; } - if(m_lpCoord[m_CurrentElement*2+1] > MC_DCT_SCRIPT_IDENTIFIER_LEN+1+1+MC_ENT_MAX_SCRIPT_SIZE) + if(m_lpCoord[m_CurrentElement*2+1] > MC_DCT_SCRIPT_IDENTIFIER_LEN+1+1+mc_gState->m_Assets->MaxScriptSize()) { return MC_ERR_WRONG_SCRIPT; } @@ -1590,7 +1590,7 @@ int mc_Script::SetNewEntityType(const uint32_t type,const int update,const unsig int err; unsigned char buf[MC_DCT_SCRIPT_IDENTIFIER_LEN+1+1]; - if((script_size<0) || (script_size>MC_ENT_MAX_SCRIPT_SIZE)) + if((script_size<0) || (script_size>mc_gState->m_Assets->MaxScriptSize())) { return MC_ERR_INVALID_PARAMETER_VALUE; } diff --git a/src/rpc/rpcfilters.cpp b/src/rpc/rpcfilters.cpp new file mode 100644 index 00000000..f30d78b6 --- /dev/null +++ b/src/rpc/rpcfilters.cpp @@ -0,0 +1,305 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2014-2016 The Bitcoin Core developers +// Original code was distributed under the MIT software license. +// Copyright (c) 2014-2017 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + + +#include "rpc/rpcwallet.h" +#include "protocol/filter.h" + +Value createtxfilterfromcmd(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 5) + throw runtime_error("Help message not found\n"); + + if (strcmp(params[1].get_str().c_str(),"txfilter")) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid entity type, should be stream"); + + if(mc_gState->m_Features->Filters() == 0) + { + throw JSONRPCError(RPC_NOT_SUPPORTED, "API is not supported with this protocol version."); + } + + if(MCP_ALLOW_FILTERS == 0) + { + throw JSONRPCError(RPC_NOT_ALLOWED, "Filters are not allowed in this chain."); + } + + CWalletTx wtx; + + mc_Script *lpScript=mc_gState->m_TmpBuffers->m_RpcScript3; + lpScript->Clear(); + mc_Script *lpDetailsScript=mc_gState->m_TmpBuffers->m_RpcScript1; + lpDetailsScript->Clear(); + mc_Script *lpDetails=mc_gState->m_TmpBuffers->m_RpcScript2; + lpDetails->Clear(); + + int ret,type; + string upgrade_name=""; + string strError=""; + int errorCode=RPC_INVALID_PARAMETER; + + if (params[2].type() != str_type) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid upgrade name, should be string"); + + if(!params[2].get_str().empty()) + { + upgrade_name=params[2].get_str(); + } + + if(upgrade_name == "*") + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid upgrade name: *"); + } + + unsigned char buf_a[MC_AST_ASSET_REF_SIZE]; + if(AssetRefDecode(buf_a,upgrade_name.c_str(),upgrade_name.size())) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid upgrade name, looks like a upgrade reference"); + } + + + if(upgrade_name.size()) + { + ret=ParseAssetKey(upgrade_name.c_str(),NULL,NULL,NULL,NULL,&type,MC_ENT_TYPE_ANY); + if(ret != MC_ASSET_KEY_INVALID_NAME) + { + if(type == MC_ENT_KEYTYPE_NAME) + { + throw JSONRPCError(RPC_DUPLICATE_NAME, "Filter, upgrade, stream or asset with this name already exists"); + } + else + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid upgrade name"); + } + } + } + + vector addresses; + + vector fromaddresses; + + if(params[0].get_str() != "*") + { + fromaddresses=ParseAddresses(params[0].get_str(),false,false); + + if(fromaddresses.size() != 1) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Single from-address should be specified"); + } + + if( (IsMine(*pwalletMain, fromaddresses[0]) & ISMINE_SPENDABLE) != ISMINE_SPENDABLE ) + { + throw JSONRPCError(RPC_WALLET_ADDRESS_NOT_FOUND, "Private key for from-address is not found in this wallet"); + } + + set thisFromAddresses; + + BOOST_FOREACH(const CTxDestination& fromaddress, fromaddresses) + { + thisFromAddresses.insert(fromaddress); + } + + CPubKey pkey; + if(!pwalletMain->GetKeyFromAddressBook(pkey,MC_PTP_CREATE | MC_PTP_ADMIN,&thisFromAddresses)) + { + throw JSONRPCError(RPC_INSUFFICIENT_PERMISSIONS, "from-address doesn't have create or admin permission"); + } + } + else + { + BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) + { + const CBitcoinAddress& address = item.first; + CKeyID keyID; + + if(address.GetKeyID(keyID)) + { + if( IsMine(*pwalletMain, keyID) & ISMINE_SPENDABLE ) + { + if(mc_gState->m_Permissions->CanCreate(NULL,(unsigned char*)(&keyID))) + { + if(mc_gState->m_Permissions->CanAdmin(NULL,(unsigned char*)(&keyID))) + { + fromaddresses.push_back(keyID); + } + } + } + } + } + CPubKey pkey; + if(fromaddresses.size() == 0) + { + throw JSONRPCError(RPC_INSUFFICIENT_PERMISSIONS, "This wallet doesn't have keys with create and admin permission"); + } + } + + lpScript->Clear(); + + lpDetails->Clear(); + lpDetails->AddElement(); + if(upgrade_name.size()) + { + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_NAME,(unsigned char*)(upgrade_name.c_str()),upgrade_name.size());//+1); + } + + bool field_parsed,already_found; + size_t bytes; + string js; + const unsigned char *script; + CScript scriptOpReturn=CScript(); + + + lpDetailsScript->Clear(); + lpDetailsScript->AddElement(); + if(params[3].type() == obj_type) + { + Object objParams = params[4].get_obj(); + already_found=false; + BOOST_FOREACH(const Pair& s, objParams) + { + field_parsed=false; + if(s.name_ == "for") + { + if(already_found) + { + throw JSONRPCError(RPC_INVALID_PARAMETER,"for field can appear only once in the object"); + } + lpDetailsScript->Clear(); + lpDetailsScript->AddElement(); + vector inputStrings; + inputStrings=ParseStringList(params[0]); + for(int is=0;is<(int)inputStrings.size();is++) + { + mc_EntityDetails entity; + ParseEntityIdentifier(inputStrings[is],&entity, MC_ENT_TYPE_ANY); + if(entity.GetEntityType() > MC_ENT_TYPE_STREAM_MAX) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Filter can be created only for streams and assets"); + } + lpDetailsScript->SetData(entity.GetShortRef(),MC_AST_SHORT_TXID_SIZE); + } + field_parsed=true; + already_found=true; + } + if(s.name_ == "js") + { + field_parsed=true; + } + if(!field_parsed) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid field: %s",s.name_.c_str())); + } + } + script = lpDetailsScript->GetData(0,&bytes); + + if(bytes) + { + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_FILTER_ENTITY,script,bytes); + } + } + else + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid restrictions, should be object"); + } + + if(params[4].type() == obj_type) + { + Object objParams = params[4].get_obj(); + already_found=false; + BOOST_FOREACH(const Pair& s, objParams) + { + field_parsed=false; + if(s.name_ == "js") + { + if(s.value_.type() == str_type) + { + if(already_found) + { + throw JSONRPCError(RPC_INVALID_PARAMETER,"js field can appear only once in the object"); + } + js=s.value_.get_str(); + already_found=true; + field_parsed=true; + } + else + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid js field, should be string"); + } + } + if(!field_parsed) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid field: %s",s.name_.c_str())); + } + } + if(!already_found) + { + throw JSONRPCError(RPC_INVALID_PARAMETER,"js field is required"); + } + if(js.size() == 0) + { + throw JSONRPCError(RPC_INVALID_PARAMETER,"js cannot be empty"); + } + + mc_Filter filter; + string strError=""; + int err=pFilterEngine->CreateFilter(js.c_str(),"filtertransaction",&filter,strError); + if(err) + { + throw JSONRPCError(RPC_INTERNAL_ERROR,"Couldn't create filter"); + } + if(strError.size()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER,strprintf("Couldn't create filter: %s",strError.c_str())); + } + } + else + { + strError="Invalid details, expecting object"; + goto exitlbl; + } + + + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_FILTER_JS,(unsigned char*)js.c_str(),js.size()); + + + script=lpDetails->GetData(0,&bytes); + + + int err; + size_t elem_size; + const unsigned char *elem; + + lpDetailsScript->Clear(); + err=lpDetailsScript->SetNewEntityType(MC_ENT_TYPE_FILTER,0,script,bytes); + if(err) + { + strError="Invalid custom fields or filter name, too long"; + goto exitlbl; + } + + elem = lpDetailsScript->GetData(0,&elem_size); + scriptOpReturn << vector(elem, elem + elem_size) << OP_DROP << OP_RETURN; + + + EnsureWalletIsUnlocked(); + { + LOCK (pwalletMain->cs_wallet_send); + + SendMoneyToSeveralAddresses(addresses, 0, wtx, lpScript, scriptOpReturn,fromaddresses); + } + +exitlbl: + + if(strError.size()) + { + throw JSONRPCError(errorCode, strError); + } + return wtx.GetHash().GetHex(); +} + +Value listfilters(const Array& params, bool fHelp) +{ + return Value::null; +} diff --git a/src/rpc/rpchelp.cpp b/src/rpc/rpchelp.cpp index de09acda..ea8d9051 100644 --- a/src/rpc/rpchelp.cpp +++ b/src/rpc/rpchelp.cpp @@ -1422,6 +1422,20 @@ void mc_InitRPCHelpMap06() // " \"param-name\": \"param-value\" (strings, required) The key is the parameter name, the value is parameter value\n" " ,...\n" " }\n" + " or \n" + "1. \"entity-type\" (string, required) txfilter\n" + "2. \"txfilter-name\" (string, required) Tx filter name, if not \"\" should be unique.\n" + "3. restrictions (object, required) A json object with filter restrictions\n" + " {\n" + " \"for\": \"stream-identifier\" (string, optional) List of stream identifiers - one of the following: stream txid, stream reference, stream name.\n" + " or\n" + " \"for\": stream-identifier(s) (array, optional) A json array of stream identifiers .\n" + " }\n" + "4. details (object, required) a json object with custom fields\n" + " {\n" + " \"js\": \"stream-identifier\" (string, required) Javascript code in clear text.\n" + " }\n" + "\nResult:\n" "\"transactionid\" (string) The transaction id.\n" @@ -1470,6 +1484,20 @@ void mc_InitRPCHelpMap06() // " \"param-name\": \"param-value\" (strings, required) The key is the parameter name, the value is parameter value\n" " ,...\n" " }\n" + " or \n" + "1. \"entity-type\" (string, required) txfilter\n" + "2. \"txfilter-name\" (string, required) Tx filter name, if not \"\" should be unique.\n" + "3. restrictions (object, required) a json object with filter restrictions\n" + " {\n" + " \"for\": \"stream-identifier\" (string, optional) List of stream identifiers - one of the following: stream txid, stream reference, stream name.\n" + " or\n" + " \"for\": stream-identifier(s) (array, optional) A json array of stream identifiers .\n" + " }\n" + "4. details (object, required) A json object with custom fields\n" + " {\n" + " \"js\": \"stream-identifier\" (string, required) Javascript code in clear text.\n" + " }\n" + "\nResult:\n" "\"transactionid\" (string) The transaction id.\n" "\nExamples:\n" diff --git a/src/rpc/rpclist.cpp b/src/rpc/rpclist.cpp index 294e74df..3dbc6691 100644 --- a/src/rpc/rpclist.cpp +++ b/src/rpc/rpclist.cpp @@ -94,6 +94,7 @@ static const CRPCCommand vRPCCommands[] = { "blockchain", "listpermissions", &listpermissions, true, false, false }, { "blockchain", "liststreams", &liststreams, true, false, false }, { "blockchain", "listupgrades", &listupgrades, true, false, false }, + { "blockchain", "listfilters", &listfilters, true, false, false }, { "blockchain", "listblocks", &listblocks, true, false, false }, /* MCHN END */ diff --git a/src/rpc/rpcserver.h b/src/rpc/rpcserver.h index e03f08a1..74ce31d7 100644 --- a/src/rpc/rpcserver.h +++ b/src/rpc/rpcserver.h @@ -248,6 +248,7 @@ extern json_spirit::Value resendwallettransactions(const json_spirit::Array& par extern json_spirit::Value listaddresses(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value liststreams(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value listupgrades(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value listfilters(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value createcmd(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value createfromcmd(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value publish(const json_spirit::Array& params, bool fHelp); diff --git a/src/rpc/rpcstreams.cpp b/src/rpc/rpcstreams.cpp index 9d1fbbcd..68782876 100644 --- a/src/rpc/rpcstreams.cpp +++ b/src/rpc/rpcstreams.cpp @@ -21,6 +21,7 @@ Value createupgradefromcmd(const Array& params, bool fHelp); +Value createtxfilterfromcmd(const Array& params, bool fHelp); void parseStreamIdentifier(Value stream_identifier,mc_EntityDetails *entity) { @@ -518,6 +519,10 @@ Value createfromcmd(const Array& params, bool fHelp) { return createupgradefromcmd(params,fHelp); } + if (strcmp(params[1].get_str().c_str(),"txfilter") == 0) + { + return createtxfilterfromcmd(params,fHelp); + } throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid entity type, should be stream"); } diff --git a/src/utils/utilwrapper.cpp b/src/utils/utilwrapper.cpp index 66ff33bb..1a9e11d8 100644 --- a/src/utils/utilwrapper.cpp +++ b/src/utils/utilwrapper.cpp @@ -829,6 +829,7 @@ int mc_MultichainParams::SetGlobals() if(MCP_ANYONE_CAN_ISSUE)MCP_ANYONE_CAN_SEND=1; } MCP_MINIMUM_PER_OUTPUT=mc_gState->m_NetworkParams->GetInt64Param("minimumperoutput"); + MCP_ALLOW_FILTERS=mc_gState->m_NetworkParams->GetInt64Param("allowfilters"); MCP_ALLOW_MULTISIG_OUTPUTS=mc_gState->m_NetworkParams->GetInt64Param("allowmultisigoutputs"); MCP_ALLOW_P2SH_OUTPUTS=mc_gState->m_NetworkParams->GetInt64Param("allowp2shoutputs"); MCP_WITH_NATIVE_CURRENCY=0; diff --git a/src/version/version.cpp b/src/version/version.cpp index d1c8e5d2..615a8ad6 100644 --- a/src/version/version.cpp +++ b/src/version/version.cpp @@ -14,7 +14,7 @@ int mc_State::VersionInfo(int version) } int this_build=20000104; - int this_protocol=20003; + int this_protocol=20004; if(version < 0) { From 30eb51e230240382b3b8857a2b4b0b05cc9e50dc Mon Sep 17 00:00:00 2001 From: mike31 Date: Thu, 16 Aug 2018 16:04:02 +0300 Subject: [PATCH 002/280] filter protocol and listfilters --- src/Makefile.am | 1 + src/chainparams/paramlist.h | 6 +- src/core/init.cpp | 15 ++ src/core/init.h | 2 + src/core/main.cpp | 4 + src/core/main.h | 1 + src/entities/asset.h | 3 +- src/permissions/permission.cpp | 34 ++++- src/permissions/permission.h | 2 + src/protocol/multichaintx.cpp | 31 +++- src/rpc/rpcfilters.cpp | 266 ++++++++++++++++++++++++++++++++- src/rpc/rpcutils.cpp | 203 +++++++++++++++++++++++++ src/rpc/rpcutils.h | 2 + 13 files changed, 556 insertions(+), 14 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 85298cfe..c712fd3d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -180,6 +180,7 @@ libbitcoin_server_a_SOURCES = \ protocol/multichaintx.cpp \ protocol/multichainblock.cpp \ custom/custom_server.cpp \ + protocol/multichainfilter.cpp \ protocol/filter.cpp \ protocol/relay.cpp \ protocol/handshake.cpp \ diff --git a/src/chainparams/paramlist.h b/src/chainparams/paramlist.h index 164ce1f4..1c7b59b8 100644 --- a/src/chainparams/paramlist.h +++ b/src/chainparams/paramlist.h @@ -122,8 +122,12 @@ static const mc_OneMultichainParam MultichainParamArray[] = "Miners must wait * between blocks."}, { "adminconsensusupgrade" , "admin-consensus-upgrade" , MC_PRM_UINT32 | MC_PRM_USER | MC_PRM_CLONE | MC_PRM_DECIMAL , -1, 500000, 0, 1000000, 0.0, 10008, 0, "-mc-adminconsensusupgrade", - "adminconsensusadmin","", + "adminconsensusfilter","", "* needed to upgrade the chain."}, + { "adminconsensusfilter" , "admin-consensus-filter" , + MC_PRM_UINT32 | MC_PRM_USER | MC_PRM_CLONE | MC_PRM_DECIMAL , -1, 500000, 0, 1000000, 0.0, 20004, 0, "-mc-adminconsensusfilter", + "adminconsensusadmin","", + "* needed to approve filter in the chain."}, { "adminconsensusadmin" , "admin-consensus-admin" , MC_PRM_UINT32 | MC_PRM_USER | MC_PRM_CLONE | MC_PRM_DECIMAL , -1, 500000, 0, 1000000, 0.0, 10001, 0, "-mc-adminconsensusadmin", "adminconsensusactivate","", diff --git a/src/core/init.cpp b/src/core/init.cpp index 84b8f9fd..037fce3e 100644 --- a/src/core/init.cpp +++ b/src/core/init.cpp @@ -67,6 +67,7 @@ mc_WalletTxs* pwalletTxsMain = NULL; #endif mc_RelayManager* pRelayManager = NULL; mc_FilterEngine* pFilterEngine = NULL; +mc_MultiChainFilterEngine* pMultiChainFilterEngine = NULL; bool fFeeEstimatesInitialized = false; extern int JSON_DOUBLE_DECIMAL_DIGITS; @@ -226,6 +227,13 @@ void Shutdown() delete pRelayManager; pRelayManager=NULL; } + + if(pMultiChainFilterEngine) + { + delete pMultiChainFilterEngine; + pMultiChainFilterEngine=NULL; + } + if(pFilterEngine) { delete pFilterEngine; @@ -1886,6 +1894,13 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) return InitError(_("Couldn't initialize filter engine.")); } + pMultiChainFilterEngine=new mc_MultiChainFilterEngine; + if(pMultiChainFilterEngine->Initialize()) + { + return InitError(_("Couldn't initialize filter engine.")); + } + + pRelayManager=new mc_RelayManager; int max_ips=64; diff --git a/src/core/init.h b/src/core/init.h index 475f3ac0..7c483423 100644 --- a/src/core/init.h +++ b/src/core/init.h @@ -13,6 +13,7 @@ class CWallet; struct mc_WalletTxs; struct mc_RelayManager; struct mc_FilterEngine; +struct mc_MultiChainFilterEngine; namespace boost { @@ -23,6 +24,7 @@ extern CWallet* pwalletMain; extern mc_WalletTxs* pwalletTxsMain; extern mc_RelayManager* pRelayManager; extern mc_FilterEngine* pFilterEngine; +extern mc_MultiChainFilterEngine* pMultiChainFilterEngine; void StartShutdown(); diff --git a/src/core/main.cpp b/src/core/main.cpp index 425d3fea..42d85485 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -36,6 +36,7 @@ extern mc_WalletTxs* pwalletTxsMain; extern mc_RelayManager* pRelayManager; extern mc_FilterEngine* pFilterEngine; +extern mc_MultiChainFilterEngine* pMultiChainFilterEngine; /* MCHN END */ @@ -2834,6 +2835,7 @@ bool static DisconnectTip(CValidationState &state) { if(fDebug)LogPrint("mcblockperf","mchn-block-perf: Rolling back permission and asset databases\n"); mc_gState->m_Permissions->RollBack(old_height-1); mc_gState->m_Assets->RollBack(old_height-1); + pMultiChainFilterEngine->Reset(old_height-1); MultichainNode_ApplyUpgrades(old_height-1); if(mc_gState->m_WalletMode & MC_WMD_TXS) @@ -2909,6 +2911,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1; int64_t nTime3; if(fDebug)LogPrint("bench", " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); + pMultiChainFilterEngine->Reset(pindexNew->nHeight-1); { CCoinsViewCache view(pcoinsTip); CInv inv(MSG_BLOCK, pindexNew->GetBlockHash()); @@ -3332,6 +3335,7 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo { mc_gState->m_Permissions->ClearMemPool(); mc_gState->m_Assets->ClearMemPool(); + pMultiChainFilterEngine->Reset(chainActive.Height()); if(fDebug)LogPrint("mcblockperf","mchn-block-perf: Replaying mempool\n"); ReplayMemPool(mempool,0,true); diff --git a/src/core/main.h b/src/core/main.h index 7628a648..6a7f7126 100644 --- a/src/core/main.h +++ b/src/core/main.h @@ -23,6 +23,7 @@ #include "script/sigcache.h" #include "script/standard.h" #include "protocol/filter.h" +#include "protocol/multichainfilter.h" #include "utils/sync.h" #include "utils/tinyformat.h" #include "chain/txmempool.h" diff --git a/src/entities/asset.h b/src/entities/asset.h index 56dbcc0e..70670bb0 100644 --- a/src/entities/asset.h +++ b/src/entities/asset.h @@ -70,7 +70,8 @@ #define MC_ENT_SPRM_UPGRADE_START_BLOCK 0x43 #define MC_ENT_SPRM_UPGRADE_CHAIN_PARAMS 0x44 #define MC_ENT_SPRM_FILTER_ENTITY 0x45 -#define MC_ENT_SPRM_FILTER_JS 0x46 +#define MC_ENT_SPRM_FILTER_CODE 0x46 +#define MC_ENT_SPRM_FILTER_TYPE 0x47 #define MC_ENT_SPRM_TIMESTAMP 0x81 #define MC_ENT_SPRM_CHUNK_HASH 0x82 diff --git a/src/permissions/permission.cpp b/src/permissions/permission.cpp index a8a97b95..20e52bb6 100644 --- a/src/permissions/permission.cpp +++ b/src/permissions/permission.cpp @@ -1219,6 +1219,32 @@ int mc_Permissions::CanWrite(const void* lpEntity,const void* lpAddress) return result; } +/** Returns non-zero value if filter is approved */ + +int mc_Permissions::FilterApproved(const void* lpEntity,const void* lpAddress) +{ + int result; + mc_MempoolPermissionRow row; + + if(mc_gState->m_NetworkParams->IsProtocolMultichain() == 0) + { + return 0; + } + + Lock(0); + + result = GetPermission(lpEntity,lpAddress,MC_PTP_FILTER); + + if(result) + { + result = MC_PTP_FILTER; + } + + UnLock(); + + return result; +} + /** Returns non-zero value if (entity,address) can write */ int mc_Permissions::CanCreate(const void* lpEntity,const void* lpAddress) @@ -2178,6 +2204,7 @@ int mc_Permissions::AdminConsensus(const void* lpEntity,uint32_t type) case MC_PTP_ACTIVATE: case MC_PTP_ISSUE: case MC_PTP_CREATE: + case MC_PTP_FILTER: if(IsSetupPeriod()) { return 1; @@ -2204,6 +2231,10 @@ int mc_Permissions::AdminConsensus(const void* lpEntity,uint32_t type) { consensus=mc_gState->m_NetworkParams->GetInt64Param("adminconsensuscreate"); } + if(type == MC_PTP_CREATE) + { + consensus=mc_gState->m_NetworkParams->GetInt64Param("adminconsensusfilter"); + } if(consensus==0) { @@ -2945,7 +2976,7 @@ void mc_Permissions::FreePermissionList(mc_Buffer *permissions) int mc_Permissions::IsActivateEnough(uint32_t type) { - if(type & ( MC_PTP_ADMIN | MC_PTP_ISSUE | MC_PTP_MINE | MC_PTP_ACTIVATE | MC_PTP_CREATE)) + if(type & ( MC_PTP_ADMIN | MC_PTP_ISSUE | MC_PTP_MINE | MC_PTP_ACTIVATE | MC_PTP_CREATE | MC_PTP_FILTER)) { return 0; } @@ -3040,6 +3071,7 @@ int mc_Permissions::SetPermissionInternal(const void* lpEntity,const void* lpAdd types[num_types]=MC_PTP_ACTIVATE;num_types++; types[num_types]=MC_PTP_ADMIN;num_types++; types[num_types]=MC_PTP_UPGRADE;num_types++; + types[num_types]=MC_PTP_FILTER;num_types++; err=MC_ERR_NOERROR; diff --git a/src/permissions/permission.h b/src/permissions/permission.h index 54114203..bd6e6f62 100644 --- a/src/permissions/permission.h +++ b/src/permissions/permission.h @@ -20,6 +20,7 @@ #define MC_PTP_UPGRADE 0x00010000 #define MC_PTP_BLOCK_MINER 0x01000000 #define MC_PTP_BLOCK_INDEX 0x02000000 +#define MC_PTP_FILTER 0x04000000 #define MC_PTP_SPECIFIED 0x80000000 #define MC_PTP_ALL 0x00FFFFFF #define MC_PTP_GLOBAL_ALL 0x00003137 @@ -316,6 +317,7 @@ typedef struct mc_Permissions int CanMine(const void* lpEntity,const void* lpAddress); int CanAdmin(const void* lpEntity,const void* lpAddress); int CanActivate(const void* lpEntity,const void* lpAddress); + int FilterApproved(const void* lpEntity,const void* lpAddress); int CanMineBlock(const void* lpAddress,uint32_t block); diff --git a/src/protocol/multichaintx.cpp b/src/protocol/multichaintx.cpp index 705169e2..b7964949 100644 --- a/src/protocol/multichaintx.cpp +++ b/src/protocol/multichaintx.cpp @@ -10,6 +10,9 @@ #include "multichain/multichain.h" #include "structs/base58.h" #include "custom/custom.h" +#include "protocol/multichainfilter.h" + +extern mc_MultiChainFilterEngine* pMultiChainFilterEngine; using namespace std; @@ -894,10 +897,17 @@ bool MultiChainTransaction_CheckEntityItem(const CTransaction& tx, } else // (Pseudo)stream item { - if(!MultiChainTransaction_CheckStreamItem(&entity,vout,details,reason)) + if(entity.GetEntityType() <= MC_ENT_TYPE_STREAM_MAX) { - return false; - } + if(!MultiChainTransaction_CheckStreamItem(&entity,vout,details,reason)) + { + return false; + } + } + else + { + reason="Metadata script rejected - too many elements for this entity type"; + } } } @@ -1891,7 +1901,8 @@ bool MultiChainTransaction_ProcessEntityCreation(const CTransaction& tx, { if(mc_gState->m_Permissions->CanCreate(NULL,(unsigned char*)&(details->vInputDestinations[i]))) { - if( (details->new_entity_type != MC_ENT_TYPE_UPGRADE) || (mc_gState->m_Permissions->CanAdmin(NULL,(unsigned char*)&(details->vInputDestinations[i])) != 0) ) + if( (details->new_entity_type <= MC_ENT_TYPE_STREAM_MAX) || // Admin persmission is required for upgrades and filters + (mc_gState->m_Permissions->CanAdmin(NULL,(unsigned char*)&(details->vInputDestinations[i])) != 0) ) { openers.push_back(details->vInputDestinations[i]); flags=MC_PFL_NONE; @@ -1917,7 +1928,7 @@ bool MultiChainTransaction_ProcessEntityCreation(const CTransaction& tx, mc_gState->m_TmpScript->AddElement(); txid=tx.GetHash(); // Setting first record in the per-entity permissions list - if(details->new_entity_type != MC_ENT_TYPE_UPGRADE) + if(details->new_entity_type <= MC_ENT_TYPE_STREAM_MAX) { memset(opener_buf,0,sizeof(opener_buf)); err=mc_gState->m_Permissions->SetPermission(&txid,opener_buf,MC_PTP_CONNECT, @@ -1936,7 +1947,7 @@ bool MultiChainTransaction_ProcessEntityCreation(const CTransaction& tx, { mc_gState->m_TmpScript->SetSpecialParamValue(MC_ENT_SPRM_ISSUER,opener_buf,sizeof(opener_buf)); } - if(details->new_entity_type != MC_ENT_TYPE_UPGRADE) + if(details->new_entity_type <= MC_ENT_TYPE_STREAM_MAX) { // Granting default per-entity permissions to openers err=mc_gState->m_Permissions->SetPermission(&txid,opener_buf,MC_PTP_ADMIN | MC_PTP_ACTIVATE | MC_PTP_WRITE, @@ -1984,6 +1995,10 @@ bool MultiChainTransaction_ProcessEntityCreation(const CTransaction& tx, { entity_type_str="upgrade"; } + if(details->new_entity_type == MC_ENT_TYPE_FILTER) + { + entity_type_str="filter"; + } if(offset>=0) { LogPrintf("New %s. TxID: %s, StreamRef: %d-%d-%d, Name: %s\n", @@ -1996,6 +2011,10 @@ bool MultiChainTransaction_ProcessEntityCreation(const CTransaction& tx, LogPrintf("New %s. TxID: %s, unconfirmed, Name: %s\n", entity_type_str.c_str(),tx.GetHash().GetHex().c_str(),entity.GetName()); } + if(details->new_entity_type == MC_ENT_TYPE_FILTER) + { + pMultiChainFilterEngine->Add((unsigned char*)&txid+MC_AST_SHORT_TXID_OFFSET); + } } else { diff --git a/src/rpc/rpcfilters.cpp b/src/rpc/rpcfilters.cpp index f30d78b6..8de823a9 100644 --- a/src/rpc/rpcfilters.cpp +++ b/src/rpc/rpcfilters.cpp @@ -6,7 +6,7 @@ #include "rpc/rpcwallet.h" -#include "protocol/filter.h" +#include "protocol/multichainfilter.h" Value createtxfilterfromcmd(const Array& params, bool fHelp) { @@ -150,7 +150,10 @@ Value createtxfilterfromcmd(const Array& params, bool fHelp) const unsigned char *script; CScript scriptOpReturn=CScript(); - + uint32_t filter_type=MC_FLT_TYPE_TX; + + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_FILTER_TYPE,(unsigned char*)&filter_type,4); + lpDetailsScript->Clear(); lpDetailsScript->AddElement(); if(params[3].type() == obj_type) @@ -244,7 +247,7 @@ Value createtxfilterfromcmd(const Array& params, bool fHelp) mc_Filter filter; string strError=""; - int err=pFilterEngine->CreateFilter(js.c_str(),"filtertransaction",&filter,strError); + int err=pFilterEngine->CreateFilter(js.c_str(),MC_FLT_MAIN_NAME_TX,&filter,strError); if(err) { throw JSONRPCError(RPC_INTERNAL_ERROR,"Couldn't create filter"); @@ -261,7 +264,7 @@ Value createtxfilterfromcmd(const Array& params, bool fHelp) } - lpDetails->SetSpecialParamValue(MC_ENT_SPRM_FILTER_JS,(unsigned char*)js.c_str(),js.size()); + lpDetails->SetSpecialParamValue(MC_ENT_SPRM_FILTER_CODE,(unsigned char*)js.c_str(),js.size()); script=lpDetails->GetData(0,&bytes); @@ -301,5 +304,258 @@ Value createtxfilterfromcmd(const Array& params, bool fHelp) Value listfilters(const Array& params, bool fHelp) { - return Value::null; + Array results; + uint32_t output_level; + + vector inputStrings; + if (params.size() > 0 && params[0].type() != null_type && ((params[0].type() != str_type) || (params[0].get_str() !="*" ) ) ) + { + if(params[0].type() == str_type) + { + inputStrings.push_back(params[0].get_str()); + if(params[0].get_str() == "") + { + return results; + } + } + else + { + inputStrings=ParseStringList(params[0]); + if(inputStrings.size() == 0) + { + return results; + } + } + } + + set filter_list; + + if(inputStrings.size()) + { + { + LOCK(cs_main); + for(int is=0;is<(int)inputStrings.size();is++) + { + string param=inputStrings[is]; + + mc_EntityDetails filter_entity; + ParseEntityIdentifier(param,&filter_entity,MC_ENT_TYPE_FILTER); + filter_list.insert(*(uint256*)filter_entity.GetTxID()); + } + } + } + + + bool verbose=false; + if (params.size() > 1) + { + if(paramtobool(params[1])) + { + verbose=true; + } + } + + output_level=0x07; + + if (verbose) + { + output_level=0x27; + } + + int unconfirmed_count=0; + for(int i=0;i<(int)pMultiChainFilterEngine->m_Filters.size();i++) + { + Object entry; + + if((filter_list.size() == 0) || + (filter_list.find(*(uint256*)pMultiChainFilterEngine->m_Filters[i].m_Details.GetTxID()) != filter_list.end()) ) + { + entry=FilterEntry(pMultiChainFilterEngine->m_Filters[i].m_Details.GetTxID(),output_level); + if(entry.size()>0) + { + bool take_it=false; + BOOST_FOREACH(const Pair& p, entry) + { + if(p.name_ == "filterref") + { + if(p.value_.type() == str_type) + { + take_it=true; + } + else + { + unconfirmed_count++; + } + } + } + if(take_it) + { + bool valid=pMultiChainFilterEngine->m_Filters[i].m_CreateError.size() == 0; + entry.push_back(Pair("valid",valid)); + if(!valid) + { + entry.push_back(Pair("error",pMultiChainFilterEngine->m_Filters[i].m_CreateError)); + } + entry.push_back(Pair("approved",mc_gState->m_Permissions->FilterApproved(NULL,&(pMultiChainFilterEngine->m_Filters[i].m_FilterAddress)) !=0 )); + entry.push_back(Pair("address",pMultiChainFilterEngine->m_Filters[i].m_FilterAddress.ToString())); + results.push_back(entry); + } + } + } + } + + sort(results.begin(), results.end(), AssetCompareByRef); + + for(int i=0;i<(int)pMultiChainFilterEngine->m_Filters.size();i++) + { + if((filter_list.size() == 0) || + (filter_list.find(*(uint256*)pMultiChainFilterEngine->m_Filters[i].m_Details.GetTxID()) != filter_list.end()) ) + { + Object entry; + + entry=FilterEntry(pMultiChainFilterEngine->m_Filters[i].m_Details.GetTxID(),output_level); + if(entry.size()>0) + { + bool take_it=false; + BOOST_FOREACH(const Pair& p, entry) + { + if(p.name_ == "filterref") + { + if(p.value_.type() != str_type) + { + take_it=true; + } + } + } + if(take_it) + { + bool valid=pMultiChainFilterEngine->m_Filters[i].m_CreateError.size() == 0; + entry.push_back(Pair("valid",valid)); + if(!valid) + { + entry.push_back(Pair("error",pMultiChainFilterEngine->m_Filters[i].m_CreateError)); + } + entry.push_back(Pair("approved",mc_gState->m_Permissions->FilterApproved(NULL,&(pMultiChainFilterEngine->m_Filters[i].m_FilterAddress)) !=0 )); + entry.push_back(Pair("address",pMultiChainFilterEngine->m_Filters[i].m_FilterAddress.ToString())); + results.push_back(entry); + } + } + } + } + + for(int i=0;i<(int)results.size();i++) + { + uint160 filter_address=0; + BOOST_FOREACH(const Pair& p, results[i].get_obj()) + { + if(p.name_ == "address") + { + filter_address.SetHex(p.value_.get_str()); + } + } + results[i].get_obj().pop_back(); + + if(verbose) + { + mc_PermissionDetails *plsRow; + mc_PermissionDetails *plsDet; + mc_PermissionDetails *plsPend; + int flags,consensus,remaining; + Array admins; + Array pending; + + mc_Buffer *permissions=NULL; + permissions=mc_gState->m_Permissions->GetPermissionList(NULL,(unsigned char*)&filter_address,MC_PTP_FILTER,permissions); + + if(permissions->GetCount()) + { + plsRow=(mc_PermissionDetails *)(permissions->GetRow(0)); + + flags=plsRow->m_Flags; + consensus=plsRow->m_RequiredAdmins; + mc_Buffer *details; + + if((flags & MC_PFL_HAVE_PENDING) || (consensus>1)) + { + details=mc_gState->m_Permissions->GetPermissionDetails(plsRow); + } + else + { + details=NULL; + } + + if(details) + { + for(int j=0;jGetCount();j++) + { + plsDet=(mc_PermissionDetails *)(details->GetRow(j)); + remaining=plsDet->m_RequiredAdmins; + if(remaining > 0) + { + uint160 addr; + memcpy(&addr,plsDet->m_LastAdmin,sizeof(uint160)); + CKeyID lpKeyID=CKeyID(addr); + admins.push_back(CBitcoinAddress(lpKeyID).ToString()); + } + } + for(int j=0;jGetCount();j++) + { + plsDet=(mc_PermissionDetails *)(details->GetRow(j)); + remaining=plsDet->m_RequiredAdmins; + if(remaining == 0) + { + Object pend_obj; + Array pend_admins; + uint32_t block_from=plsDet->m_BlockFrom; + uint32_t block_to=plsDet->m_BlockTo; + for(int k=j;kGetCount();k++) + { + plsPend=(mc_PermissionDetails *)(details->GetRow(k)); + remaining=plsPend->m_RequiredAdmins; + + if(remaining == 0) + { + if(block_from == plsPend->m_BlockFrom) + { + if(block_to == plsPend->m_BlockTo) + { + uint160 addr; + memcpy(&addr,plsPend->m_LastAdmin,sizeof(uint160)); + CKeyID lpKeyID=CKeyID(addr); + // CKeyID lpKeyID=CKeyID(*(uint160*)((void*)(plsPend->m_LastAdmin))); + pend_admins.push_back(CBitcoinAddress(lpKeyID).ToString()); + plsPend->m_RequiredAdmins=0x01010101; + } + } + } + } + pend_obj.push_back(Pair("approve", block_from < block_to)); + pend_obj.push_back(Pair("admins", pend_admins)); + pend_obj.push_back(Pair("required", (int64_t)(consensus-pend_admins.size()))); + pending.push_back(pend_obj); + } + } + mc_gState->m_Permissions->FreePermissionList(details); + } + else + { + uint160 addr; + memcpy(&addr,plsRow->m_LastAdmin,sizeof(uint160)); + CKeyID lpKeyID=CKeyID(addr); + admins.push_back(CBitcoinAddress(lpKeyID).ToString()); + } + + results[i].get_obj().push_back(Pair("admins", admins)); + results[i].get_obj().push_back(Pair("pending", pending)); + } + else + { + results[i].get_obj().push_back(Pair("admins", admins)); + results[i].get_obj().push_back(Pair("pending", pending)); + } + mc_gState->m_Permissions->FreePermissionList(permissions); + } + } + + return results; } diff --git a/src/rpc/rpcutils.cpp b/src/rpc/rpcutils.cpp index a3b2e5a8..f96a7bab 100644 --- a/src/rpc/rpcutils.cpp +++ b/src/rpc/rpcutils.cpp @@ -549,7 +549,202 @@ Array PermissionEntries(const CTxOut& txout,mc_Script *lpScript,bool fLong) return results; } +Object FilterEntry(const unsigned char *txid,uint32_t output_level) +{ +// output_level constants +// 0x0001 type +// 0x0002 txid +// 0x0004 details +// 0x0020 creators + + Object entry; + mc_EntityDetails entity; + unsigned char *ptr; + size_t value_size; + + if(txid == NULL) + { + entry.push_back(Pair("filterref", "")); + return entry; + } + + uint256 hash=*(uint256*)txid; + if(mc_gState->m_Assets->FindEntityByTxID(&entity,txid)) + { + if(output_level & 0x0001) + { + uint32_t filter_type=MC_FLT_TYPE_TX; + ptr=(unsigned char *)entity.GetSpecialParam(MC_ENT_SPRM_FILTER_TYPE,&value_size); + + if(ptr) + { + if( (value_size <=0) || (value_size > 4) ) + { + filter_type=MC_FLT_TYPE_BAD; + } + else + { + filter_type=mc_GetLE(ptr,value_size); + } + } + switch(filter_type) + { + case MC_FLT_TYPE_TX: + entry.push_back(Pair("type", "txfilter")); + break; + default: + entry.push_back(Pair("type", "unsupported")); + break; + } + + } + + ptr=(unsigned char *)entity.GetName(); + + if(ptr && strlen((char*)ptr)) + { + entry.push_back(Pair("name", string((char*)ptr))); + } + if(output_level & 0x002) + { + entry.push_back(Pair("createtxid", hash.GetHex())); + } + ptr=(unsigned char *)entity.GetRef(); + string streamref=""; + if(entity.IsUnconfirmedGenesis()) + { + Value null_value; + entry.push_back(Pair("filterref",null_value)); + } + else + { + if((int)mc_GetLE(ptr,4)) + { + streamref += itostr((int)mc_GetLE(ptr,4)); + streamref += "-"; + streamref += itostr((int)mc_GetLE(ptr+4,4)); + streamref += "-"; + streamref += itostr((int)mc_GetLE(ptr+8,2)); + } + else + { + streamref="0-0-0"; + } + entry.push_back(Pair("filterref", streamref)); + } + + if(output_level & 0x0004) + { + entry.push_back(Pair("language", "javascript")); + + Array entities; + ptr=(unsigned char *)entity.GetSpecialParam(MC_ENT_SPRM_FILTER_CODE,&value_size); + + if(ptr) + { + entry.push_back(Pair("codesize", value_size)); + } + else + { + entry.push_back(Pair("codesize", 0)); + } + + ptr=(unsigned char *)entity.GetSpecialParam(MC_ENT_SPRM_FILTER_ENTITY,&value_size); + if(ptr) + { + if(value_size % MC_AST_SHORT_TXID_SIZE) + { + entry.push_back(Pair("entities","error")); + } + else + { + for(int i=0;i<(int)value_size/MC_AST_SHORT_TXID_SIZE;i++) + { + mc_EntityDetails relevant_entity; + Object asset_entry; + if(mc_gState->m_Assets->FindEntityByShortTxID(&relevant_entity,ptr+i*MC_AST_SHORT_TXID_SIZE)) + { + switch(relevant_entity.GetEntityType()) + { + case MC_ENT_TYPE_ASSET: + asset_entry=AssetEntry(relevant_entity.GetTxID(),-1,0x00); + asset_entry.push_back(Pair("type", "asset")); + entities.push_back(asset_entry); + break; + default: + entities.push_back(StreamEntry(relevant_entity.GetTxID(),0x03)); + break; + } + } + } + entry.push_back(Pair("entities",entities)); + } + } + else + { + entry.push_back(Pair("entities",entities)); + } + } + + + + if(output_level & 0x0020) + { + Array openers; + int64_t offset,new_offset; + uint32_t value_offset; + ptr=(unsigned char*)entity.GetScript(); + + offset=0; + while(offset>=0) + { + new_offset=entity.NextParam(offset,&value_offset,&value_size); + if(value_offset > 0) + { + if(ptr[offset] == 0) + { + if(ptr[offset+1] == MC_ENT_SPRM_ISSUER) + { + if(value_size == 24) + { + unsigned char tptr[4]; + memcpy(tptr,ptr+value_offset+sizeof(uint160),4); + if(mc_GetLE(tptr,4) & MC_PFL_IS_SCRIPTHASH) + { + openers.push_back(CBitcoinAddress(*(CScriptID*)(ptr+value_offset)).ToString()); + } + else + { + openers.push_back(CBitcoinAddress(*(CKeyID*)(ptr+value_offset)).ToString()); + } + } + } + } + } + offset=new_offset; + } + + entry.push_back(Pair("creators",openers)); + } + } + else + { + Value null_value; + if(output_level & 0x001) + { + entry.push_back(Pair("type", "stream")); + } + entry.push_back(Pair("name",null_value)); + if(output_level & 0x002) + { + entry.push_back(Pair("createtxid",null_value)); + } + entry.push_back(Pair("filterref", null_value)); + } + + return entry; +} Object StreamEntry(const unsigned char *txid,uint32_t output_level) { @@ -3532,6 +3727,10 @@ bool AssetCompareByRef(Value a,Value b) { assetref_a=p.value_; } + if(p.name_ == "filterref") + { + assetref_a=p.value_; + } } BOOST_FOREACH(const Pair& p, b.get_obj()) @@ -3544,6 +3743,10 @@ bool AssetCompareByRef(Value a,Value b) { assetref_b=p.value_; } + if(p.name_ == "filterref") + { + assetref_b=p.value_; + } } if(assetref_b.type() != str_type) diff --git a/src/rpc/rpcutils.h b/src/rpc/rpcutils.h index f80c34a1..56cd4307 100644 --- a/src/rpc/rpcutils.h +++ b/src/rpc/rpcutils.h @@ -20,6 +20,7 @@ #include "multichain/multichain.h" #include "utils/utilparse.h" #include "wallet/chunkdb.h" +#include "protocol/multichainfilter.h" using namespace std; using namespace json_spirit; @@ -116,6 +117,7 @@ Value OpReturnFormatEntry(const unsigned char *elem,int64_t elem_size,uint256 tx Value OpReturnFormatEntry(const unsigned char *elem,size_t elem_size,uint256 txid, int vout, uint32_t format, string *format_text_out); Value OpReturnFormatEntry(const unsigned char *elem,size_t elem_size,uint256 txid, int vout, uint32_t format); Value DataItemEntry(const CTransaction& tx,int n,set & already_seen,uint32_t stream_output_level); +Object FilterEntry(const unsigned char *txid,uint32_t output_level); Object AssetEntry(const unsigned char *txid,int64_t quantity,uint32_t output_level); string ParseRawOutputObject(Value param,CAmount& nAmount,mc_Script *lpScript,int *eErrorCode); bool FindPreparedTxOut(CTxOut& txout,COutPoint outpoint,string& reason); From 8cae30f6f7e48a6871af22c605be86211441168f Mon Sep 17 00:00:00 2001 From: mike31 Date: Sun, 19 Aug 2018 10:52:07 +0300 Subject: [PATCH 003/280] Filter processing engine --- src/protocol/multichainfilter.cpp | 238 ++++++++++++++++++++++++++++++ src/protocol/multichainfilter.h | 74 ++++++++++ 2 files changed, 312 insertions(+) create mode 100644 src/protocol/multichainfilter.cpp create mode 100644 src/protocol/multichainfilter.h diff --git a/src/protocol/multichainfilter.cpp b/src/protocol/multichainfilter.cpp new file mode 100644 index 00000000..2b0b25d5 --- /dev/null +++ b/src/protocol/multichainfilter.cpp @@ -0,0 +1,238 @@ +// Copyright (c) 2014-2017 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#include "protocol/multichainfilter.h" + +using namespace std; + +int mc_MultiChainFilter::Zero() +{ + m_RelevantEntities.clear(); + m_Filter.Zero(); + m_CreateError="Not Initialized"; + m_FilterType=MC_FLT_TYPE_TX; + m_FilterCaption="Unknown"; + m_FilterCode[0]=0x00; + m_FilterAddress=0; + + return MC_ERR_NOERROR; +} + +int mc_MultiChainFilter::Destroy() +{ + m_Filter.Destroy(); + + Zero(); + + return MC_ERR_NOERROR; +} + +int mc_MultiChainFilter::Initialize(const unsigned char* short_txid) +{ + size_t value_size; + unsigned char *ptr; + + m_FilterAddress=0; + memcpy(&m_FilterAddress,short_txid,MC_AST_SHORT_TXID_SIZE); + + if(mc_gState->m_Assets->FindEntityByShortTxID(&m_Details,short_txid) == 0) + { + return MC_ERR_NOT_FOUND; + } + + m_FilterCaption=strprintf("TxID: %s, FilterRef: %s, Name: %s", + m_Details.GetTxID(),m_Details.GetRef(),m_Details.m_Name); + + ptr=(unsigned char *)m_Details.GetSpecialParam(MC_ENT_SPRM_FILTER_TYPE,&value_size); + + if(ptr) + { + if( (value_size <=0) || (value_size > 4) ) + { + return MC_ERR_ERROR_IN_SCRIPT; + } + m_FilterType=mc_GetLE(ptr,value_size); + } + + switch(m_FilterType) + { + case MC_FLT_TYPE_TX: + m_MainName=MC_FLT_MAIN_NAME_TX; + break; + default: + m_CreateError="Unsupported filter type"; + break; + } + + ptr=(unsigned char *)m_Details.GetSpecialParam(MC_ENT_SPRM_FILTER_ENTITY,&value_size); + + if(ptr) + { + if(value_size % MC_AST_SHORT_TXID_SIZE) + { + return MC_ERR_ERROR_IN_SCRIPT; + } + + for(int i=0;i<(int)value_size/MC_AST_SHORT_TXID_SIZE;i++) + { + uint160 hash=0; + memcpy(&hash,ptr+i*MC_AST_SHORT_TXID_SIZE,MC_AST_SHORT_TXID_SIZE); + m_RelevantEntities.push_back(hash); + } + } + + ptr=(unsigned char *)m_Details.GetSpecialParam(MC_ENT_SPRM_FILTER_CODE,&value_size); + + if(ptr) + { + m_CreateError="Empty filter code"; + } + else + { + memcpy(m_FilterCode,ptr,value_size); + m_FilterCode[value_size]=0x00; + } + + return MC_ERR_NOERROR; +} + + +int mc_MultiChainFilterEngine::Zero() +{ + m_Filters.clear(); + + return MC_ERR_NOERROR; +} + +int mc_MultiChainFilterEngine::Destroy() +{ + for(int i=0;i<(int)m_Filters.size();i++) + { + m_Filters[i].Destroy(); + } + + Zero(); + + return MC_ERR_NOERROR; +} + +int mc_MultiChainFilterEngine::Add(const unsigned char* short_txid) +{ + int err; + mc_MultiChainFilter filter; + + err=filter.Initialize(short_txid); + if(err) + { + LogPrintf("Couldn't add filter with short txid %s, error: %d\n",filter.m_FilterAddress.ToString().c_str(),err); + return err; + } + + m_Filters.push_back(filter); + + err=pFilterEngine->CreateFilter(m_Filters.back().m_FilterCode,m_Filters.back().m_MainName.c_str(),&(m_Filters.back().m_Filter),m_Filters.back().m_CreateError); + if(err) + { + LogPrintf("Couldn't create filter with short txid %s, error: %d\n",filter.m_FilterAddress.ToString().c_str(),err); + m_Filters.pop_back(); + return err; + } + + return MC_ERR_NOERROR; +} + +int mc_MultiChainFilterEngine::Reset(int block) +{ + int filter_block; + int err; + filter_block=m_Filters.back().m_Details.m_LedgerRow.m_Block; + if(filter_block<0) + { + filter_block=block+1; + } + while( (m_Filters.size()>0) && (filter_block > block) ) + { + m_Filters.back().Destroy(); + m_Filters.pop_back(); + if(m_Filters.size()>0) + { + filter_block=m_Filters.back().m_Details.m_LedgerRow.m_Block; + if(filter_block<0) + { + filter_block=block+1; + } + } + } + + for(int i=0;i<(int)m_Filters.size();i++) + { + err=pFilterEngine->CreateFilter(m_Filters[i].m_FilterCode,m_Filters[i].m_MainName.c_str(),&(m_Filters[i].m_Filter),m_Filters[i].m_CreateError); + if(err) + { + LogPrintf("Couldn't prepare filter %s, error: %d\n",m_Filters[i].m_FilterCaption.c_str(),err); + return err; + } + } + + return MC_ERR_NOERROR; +} + +int mc_MultiChainFilterEngine::Run(std::string &strResult,mc_MultiChainFilter **lppFilter) +{ + int err; + strResult=""; + + for(int i=0;i<(int)m_Filters.size();i++) + { + if(m_Filters[i].m_CreateError.size()) + { + if(mc_gState->m_Permissions->FilterApproved(NULL,&(m_Filters[i].m_FilterAddress))) + { + err=pFilterEngine->RunFilter(m_Filters[i].m_Filter,strResult); + if(err) + { + LogPrintf("Error while running filter %s, error: %d\n",m_Filters[i].m_FilterCaption.c_str(),err); + return err; + } + if(strResult.c_str()) + { + if(lppFilter) + { + *lppFilter=&(m_Filters[i]); + } + return MC_ERR_NOERROR; + } + } + } + } + + return MC_ERR_NOERROR; +} + +int mc_MultiChainFilterEngine::Initialize() +{ + mc_Buffer *filters; + unsigned char *txid; + int err=MC_ERR_NOERROR; + + filters=NULL; + filters=mc_gState->m_Assets->GetEntityList(filters,NULL,MC_ENT_TYPE_FILTER); + + for(int i=0;iGetCount();i++) + { + txid=filters->GetRow(i); + err=Add(txid+MC_AST_SHORT_TXID_OFFSET); + if(err) + { + goto exitlbl; + } + } + + LogPrintf("Filter initialization completed\n"); + +exitlbl: + + mc_gState->m_Assets->FreeEntityList(filters); + + return MC_ERR_NOERROR; +} \ No newline at end of file diff --git a/src/protocol/multichainfilter.h b/src/protocol/multichainfilter.h new file mode 100644 index 00000000..964d666e --- /dev/null +++ b/src/protocol/multichainfilter.h @@ -0,0 +1,74 @@ +// Copyright (c) 2014-2017 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#ifndef MULTICHAINFILTER_H +#define MULTICHAINFILTER_H + +#include "core/init.h" +#include "core/main.h" +#include "utils/util.h" +#include "utils/utilparse.h" +#include "multichain/multichain.h" +#include "protocol/filter.h" + +#define MC_FLT_TYPE_BAD 0xFFFFFFFF +#define MC_FLT_TYPE_TX 0 + +#define MC_FLT_MAIN_NAME_TX "filtertransaction" + +typedef struct mc_MultiChainFilter +{ + std::vector m_RelevantEntities; + + mc_EntityDetails m_Details; + mc_Filter m_Filter; + std::string m_CreateError; + std::string m_MainName; + std::string m_FilterCaption; + char m_FilterCode[MC_ENT_MAX_SCRIPT_SIZE+1]; + uint32_t m_FilterType; + uint160 m_FilterAddress; + + mc_MultiChainFilter() + { + Zero(); + } + + ~mc_MultiChainFilter() + { + Destroy(); + } + + int Initialize(const unsigned char* short_txid); + + int Zero(); + int Destroy(); + +} mc_MultiChainFilter; + +typedef struct mc_MultiChainFilterEngine +{ + std::vector m_Filters; + + mc_MultiChainFilterEngine() + { + Zero(); + } + + ~mc_MultiChainFilterEngine() + { + Destroy(); + } + + int Initialize(); + int Add(const unsigned char* short_txid); + int Reset(int block); + int Run(std::string &strResult,mc_MultiChainFilter **lppFilter); + + int Zero(); + int Destroy(); + +} mc_MultiChainFilterEngine; + +#endif /* MULTICHAINFILTER_H */ + From da63da1594d52fe33b7e2e7c4ec191ba46fb01c2 Mon Sep 17 00:00:00 2001 From: mike31 Date: Sun, 19 Aug 2018 12:50:32 +0300 Subject: [PATCH 004/280] Filter approvals --- src/protocol/multichaintx.cpp | 4 +- src/rpc/rpcclient.cpp | 3 ++ src/rpc/rpcfilters.cpp | 8 ++++ src/rpc/rpchelp.cpp | 19 +++++++- src/rpc/rpcupgrades.cpp | 82 ++++++++++++++++++++++++++--------- src/rpc/rpcutils.cpp | 4 ++ 6 files changed, 97 insertions(+), 23 deletions(-) diff --git a/src/protocol/multichaintx.cpp b/src/protocol/multichaintx.cpp index b7964949..cebce7fe 100644 --- a/src/protocol/multichaintx.cpp +++ b/src/protocol/multichaintx.cpp @@ -1043,7 +1043,7 @@ bool MultiChainTransaction_ProcessPermissions(const CTransaction& tx, if(fFirstPass) { - if( type & ( MC_PTP_CREATE | MC_PTP_ISSUE | MC_PTP_ACTIVATE ) ) + if( type & ( MC_PTP_CREATE | MC_PTP_ISSUE | MC_PTP_ACTIVATE | MC_PTP_FILTER ) ) { details->vOutputScriptFlags[vout] |= MC_MTX_OUTPUT_DETAIL_FLAG_PERMISSION_CREATE; } @@ -1327,7 +1327,7 @@ bool MultiChainTransaction_CheckOutputs(const CTransaction& tx, { MultiChainTransaction_SetTmpOutputScript(tx.vout[vout].scriptPubKey); - permission_type=MC_PTP_CREATE | MC_PTP_ISSUE | MC_PTP_ACTIVATE; + permission_type=MC_PTP_CREATE | MC_PTP_ISSUE | MC_PTP_ACTIVATE | MC_PTP_FILTER; if(!MultiChainTransaction_ProcessPermissions(tx,offset,vout,permission_type,false,details,reason)) { return false; diff --git a/src/rpc/rpcclient.cpp b/src/rpc/rpcclient.cpp index b46e6148..fd6d7da2 100644 --- a/src/rpc/rpcclient.cpp +++ b/src/rpc/rpcclient.cpp @@ -76,6 +76,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "listupgrades", 1 }, { "listupgrades", 2 }, { "listupgrades", 3 }, + { "listfilters", 0 }, + { "listfilters", 1 }, { "publishfrom", 2 }, { "publishfrom", 3 }, { "publish", 1 }, @@ -346,6 +348,7 @@ static const CRPCConvertParamMayBeString vRPCConvertParamsMayBeString[] = { "listassets", 0 }, { "liststreams", 0 }, { "listupgrades", 0 }, + { "listfilters", 0 }, { "listpermissions", 1 }, { "publishfrom", 2 }, { "publishfrom", 3 }, diff --git a/src/rpc/rpcfilters.cpp b/src/rpc/rpcfilters.cpp index 8de823a9..6e3c15d9 100644 --- a/src/rpc/rpcfilters.cpp +++ b/src/rpc/rpcfilters.cpp @@ -304,9 +304,17 @@ Value createtxfilterfromcmd(const Array& params, bool fHelp) Value listfilters(const Array& params, bool fHelp) { + if (fHelp || params.size() > 2) + throw runtime_error("Help message not found\n"); + Array results; uint32_t output_level; + if(mc_gState->m_Features->Filters() == 0) + { + throw JSONRPCError(RPC_NOT_SUPPORTED, "API is not supported with this protocol version."); + } + vector inputStrings; if (params.size() > 0 && params[0].type() != null_type && ((params[0].type() != str_type) || (params[0].get_str() !="*" ) ) ) { diff --git a/src/rpc/rpchelp.cpp b/src/rpc/rpchelp.cpp index ea8d9051..7cca95d7 100644 --- a/src/rpc/rpchelp.cpp +++ b/src/rpc/rpchelp.cpp @@ -3618,12 +3618,14 @@ void mc_InitRPCHelpMap16() )); mapHelpStrings.insert(std::make_pair("approvefrom", - "approvefrom \"from-address\" \"upgrade-identifier\" ( approve )\n" + "approvefrom \"from-address\" \"upgrade-identifier\"|\"filter-identifier\" ( approve )\n" "\nApprove upgrade using specific address.\n" + HelpRequiringPassphraseWrapper() + "\nArguments:\n" "1. \"from-address\" (string, required) Address used for approval.\n" "2. \"upgrade-identifier\" (string, required) Upgrade identifier - one of the following: upgrade txid, upgrade name.\n" + " or\n" + "2. \"filter-identifier\" (string, required) Filter identifier - one of the following: filter txid, filter name.\n" "3. approve (boolean, required) Approve or disapprove\n" "\nResult:\n" "\"transactionid\" (string) The transaction id.\n" @@ -4177,7 +4179,22 @@ void mc_InitRPCHelpMap18() "" )); + mapHelpStrings.insert(std::make_pair("listfilters", + "listfilters ( filter-identifier(s) verbose )\n" + "\nReturns list of defined filters\n" + "\nArguments:\n" + "1. \"filter-identifier\" (string, optional) Filter identifier - one of the following: create txid, filter reference, filter name.\n" + " or\n" + "1. filter-identifier(s) (array, optional) A json array of filter identifiers \n" + "2. verbose (boolean, optional, default=false) If true, returns list of creators and approval details \n" + "\nResult:\n" + "An array containing list of defined filters\n" + "\nExamples:\n" + + HelpExampleCli("listfilters", "") + + HelpExampleRpc("listfilters", "") + )); + } void mc_InitRPCLogParamCountMap() diff --git a/src/rpc/rpcupgrades.cpp b/src/rpc/rpcupgrades.cpp index 69a317e2..70f0fca8 100644 --- a/src/rpc/rpcupgrades.cpp +++ b/src/rpc/rpcupgrades.cpp @@ -306,24 +306,54 @@ Value approvefrom(const json_spirit::Array& params, bool fHelp) mc_EntityDetails entity; entity.Zero(); - ParseEntityIdentifier(entity_identifier,&entity, MC_ENT_TYPE_UPGRADE); + ParseEntityIdentifier(entity_identifier,&entity, MC_ENT_TYPE_ANY); + + string entity_nameU; + string entity_nameL; + bool fIsUpgrade=true; + + switch(entity.GetEntityType()) + { + case MC_ENT_TYPE_UPGRADE: + entity_nameU="Upgrade"; + entity_nameL="upgrade"; + break; + case MC_ENT_TYPE_FILTER: + if(mc_gState->m_Features->Filters() == 0) + { + throw JSONRPCError(RPC_NOT_SUPPORTED, "API is not supported with this protocol version."); + } + entity_nameU="Filter"; + entity_nameL="filter"; + fIsUpgrade=false; + break; + default: + throw JSONRPCError(RPC_ENTITY_NOT_FOUND, "Invalid identifier, should be upgrade or filter"); + break; + } - if( mc_gState->m_NetworkParams->ProtocolVersion() >= mc_gState->MinProtocolForbiddenDowngradeVersion() ) + if(fIsUpgrade) { - if(entity.UpgradeProtocolVersion()) + if( mc_gState->m_NetworkParams->ProtocolVersion() >= mc_gState->MinProtocolForbiddenDowngradeVersion() ) { - if(entity.UpgradeProtocolVersion() < mc_gState->m_NetworkParams->ProtocolVersion()) + if(entity.UpgradeProtocolVersion()) { - throw JSONRPCError(RPC_NOT_ALLOWED, "Invalid protocol version, cannot downgrade from current version"); - } + if(entity.UpgradeProtocolVersion() < mc_gState->m_NetworkParams->ProtocolVersion()) + { + throw JSONRPCError(RPC_NOT_ALLOWED, "Invalid protocol version, cannot downgrade from current version"); + } + } } } - if(mc_gState->m_Permissions->IsApproved(entity.GetTxID()+MC_AST_SHORT_TXID_OFFSET,0)) + if(fIsUpgrade) { - throw JSONRPCError(RPC_NOT_ALLOWED, "Upgrade already approved"); + if(mc_gState->m_Permissions->IsApproved(entity.GetTxID()+MC_AST_SHORT_TXID_OFFSET,0)) + { + throw JSONRPCError(RPC_NOT_ALLOWED, "Upgrade already approved"); + } } - + vector fromaddresses; fromaddresses=ParseAddresses(params[0].get_str(),false,false); @@ -352,21 +382,33 @@ Value approvefrom(const json_spirit::Array& params, bool fHelp) mc_Script *lpScript=mc_gState->m_TmpBuffers->m_RpcScript3; lpScript->Clear(); - lpScript->SetEntity(entity.GetTxID()+MC_AST_SHORT_TXID_OFFSET); - lpScript->SetApproval(approval, timestamp); - - size_t elem_size; - const unsigned char *elem; CScript scriptOpReturn=CScript(); - for(int e=0;eGetNumElements();e++) + if(fIsUpgrade) { - elem = lpScript->GetData(e,&elem_size); - scriptOpReturn << vector(elem, elem + elem_size) << OP_DROP; - } - scriptOpReturn << OP_RETURN; + lpScript->SetEntity(entity.GetTxID()+MC_AST_SHORT_TXID_OFFSET); + lpScript->SetApproval(approval, timestamp); + size_t elem_size; + const unsigned char *elem; + + for(int e=0;eGetNumElements();e++) + { + elem = lpScript->GetData(e,&elem_size); + scriptOpReturn << vector(elem, elem + elem_size) << OP_DROP; + } + scriptOpReturn << OP_RETURN; + } + else + { + lpScript->SetPermission(MC_PTP_FILTER,0,approval ? 4294967295U : 0,timestamp); + uint160 filter_address; + filter_address=0; + memcpy(&filter_address,entity.GetTxID()+MC_AST_SHORT_TXID_OFFSET,MC_AST_SHORT_TXID_SIZE); + addresses.push_back(CKeyID(filter_address)); + } + - LogPrintf("mchn: %s upgrade %s (%s) from address %s\n",(approval != 0) ? "Approving" : "Disapproving", + LogPrintf("mchn: %s %s %s (%s) from address %s\n",(approval != 0) ? "Approving" : "Disapproving",entity_nameL.c_str(), ((uint256*)entity.GetTxID())->ToString().c_str(),entity.GetName(),CBitcoinAddress(fromaddresses[0]).ToString().c_str()); diff --git a/src/rpc/rpcutils.cpp b/src/rpc/rpcutils.cpp index f96a7bab..512eeebc 100644 --- a/src/rpc/rpcutils.cpp +++ b/src/rpc/rpcutils.cpp @@ -3636,6 +3636,10 @@ void ParseEntityIdentifier(Value entity_identifier,mc_EntityDetails *entity,uint entity_nameU="Upgrade"; entity_nameL="upgrade"; break; + case MC_ENT_TYPE_FILTER: + entity_nameU="Filter"; + entity_nameL="filter"; + break; default: entity_nameU="Entity"; entity_nameL="entity"; From 4b2c2d85bf625e279d970a93c5c8e8f2fc646f78 Mon Sep 17 00:00:00 2001 From: mike31 Date: Sun, 19 Aug 2018 13:22:27 +0300 Subject: [PATCH 005/280] Applying global filters --- src/protocol/multichainfilter.cpp | 2 +- src/protocol/multichainfilter.h | 2 +- src/protocol/multichaintx.cpp | 37 ++++++++++++++++++++++++++++++- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/protocol/multichainfilter.cpp b/src/protocol/multichainfilter.cpp index 2b0b25d5..671a1403 100644 --- a/src/protocol/multichainfilter.cpp +++ b/src/protocol/multichainfilter.cpp @@ -177,7 +177,7 @@ int mc_MultiChainFilterEngine::Reset(int block) return MC_ERR_NOERROR; } -int mc_MultiChainFilterEngine::Run(std::string &strResult,mc_MultiChainFilter **lppFilter) +int mc_MultiChainFilterEngine::Run(uint256 txid,std::string &strResult,mc_MultiChainFilter **lppFilter) { int err; strResult=""; diff --git a/src/protocol/multichainfilter.h b/src/protocol/multichainfilter.h index 964d666e..a3a8fb6c 100644 --- a/src/protocol/multichainfilter.h +++ b/src/protocol/multichainfilter.h @@ -63,7 +63,7 @@ typedef struct mc_MultiChainFilterEngine int Initialize(); int Add(const unsigned char* short_txid); int Reset(int block); - int Run(std::string &strResult,mc_MultiChainFilter **lppFilter); + int Run(uint256 txid, std::string &strResult,mc_MultiChainFilter **lppFilter); int Zero(); int Destroy(); diff --git a/src/protocol/multichaintx.cpp b/src/protocol/multichaintx.cpp index cebce7fe..9eb698cc 100644 --- a/src/protocol/multichaintx.cpp +++ b/src/protocol/multichaintx.cpp @@ -36,6 +36,7 @@ typedef struct CMultiChainTxDetails bool fSeedNodeInvolved; // Connect permission of seed node changed bool fFullReplayCheckRequired; // Tx should be rechecked when mempool is replayed bool fAdminMinerGrant; // Admin/miner grant in this transaction + bool fNoFiltering; // Filters are not applied in this transaction vector vInputScriptTypes; // Input script types vector vInputDestinations; // Addresses used in input scripts @@ -98,6 +99,7 @@ void CMultiChainTxDetails::Zero() fSeedNodeInvolved=false; fFullReplayCheckRequired=false; fAdminMinerGrant=false; + fNoFiltering=false; details_script_size=0; details_script_type=-1; @@ -1046,6 +1048,10 @@ bool MultiChainTransaction_ProcessPermissions(const CTransaction& tx, if( type & ( MC_PTP_CREATE | MC_PTP_ISSUE | MC_PTP_ACTIVATE | MC_PTP_FILTER ) ) { details->vOutputScriptFlags[vout] |= MC_MTX_OUTPUT_DETAIL_FLAG_PERMISSION_CREATE; + if(type & MC_PTP_FILTER) + { + details->fNoFiltering=true; + } } if( type & ( MC_PTP_MINE | MC_PTP_ADMIN ) ) { @@ -1928,13 +1934,18 @@ bool MultiChainTransaction_ProcessEntityCreation(const CTransaction& tx, mc_gState->m_TmpScript->AddElement(); txid=tx.GetHash(); // Setting first record in the per-entity permissions list + if(details->new_entity_type > MC_ENT_TYPE_STREAM_MAX) + { + details->fNoFiltering=true; + } + if(details->new_entity_type <= MC_ENT_TYPE_STREAM_MAX) { memset(opener_buf,0,sizeof(opener_buf)); err=mc_gState->m_Permissions->SetPermission(&txid,opener_buf,MC_PTP_CONNECT, (unsigned char*)openers[0].begin(),0,(uint32_t)(-1),timestamp, MC_PFL_ENTITY_GENESIS ,update_mempool,offset); } - + for (unsigned int i = 0; i < openers.size(); i++) { if(err == MC_ERR_NOERROR) @@ -2082,6 +2093,30 @@ bool AcceptMultiChainTransaction (const CTransaction& tx, goto exitlbl; } + if(!details.fNoFiltering) + { + if(pMultiChainFilterEngine) + { + string filter_error; + mc_MultiChainFilter* lpFilter; + + if(pMultiChainFilterEngine->Run(tx.GetHash(),reason,&lpFilter) != MC_ERR_NOERROR) + { + reason="Error while running filters"; + fReject=true; + goto exitlbl; + } + else + { + if(reason.size()) + { + if(fDebug)LogPrint("mchn","mchn: Rejecting filter: %s\n",lpFilter->m_FilterCaption.c_str()); + fReject=true; + goto exitlbl; + } + } + } + } // Custom filters fReject=!custom_accept_transacton(tx,inputs,offset,accept,reason,replay); From 5550e787a9d2240fac1a2807f508d182c9bc326c Mon Sep 17 00:00:00 2001 From: mike31 Date: Sun, 19 Aug 2018 18:12:55 +0300 Subject: [PATCH 006/280] Per-entity filters --- src/protocol/multichainfilter.cpp | 68 ++++++++++++++++++++++++------- src/protocol/multichainfilter.h | 7 +++- src/protocol/multichaintx.cpp | 39 +++++++++++++++--- src/rpc/rpcfilters.cpp | 39 ++++++++++++++++++ src/rpc/rpchelp.cpp | 21 ++++++++-- src/rpc/rpclist.cpp | 1 + src/rpc/rpcserver.h | 2 + 7 files changed, 151 insertions(+), 26 deletions(-) diff --git a/src/protocol/multichainfilter.cpp b/src/protocol/multichainfilter.cpp index 671a1403..e4c4622c 100644 --- a/src/protocol/multichainfilter.cpp +++ b/src/protocol/multichainfilter.cpp @@ -40,8 +40,10 @@ int mc_MultiChainFilter::Initialize(const unsigned char* short_txid) return MC_ERR_NOT_FOUND; } - m_FilterCaption=strprintf("TxID: %s, FilterRef: %s, Name: %s", - m_Details.GetTxID(),m_Details.GetRef(),m_Details.m_Name); + uint256 txid; + txid=*(uint256*)m_Details.GetTxID(); + m_FilterCaption=strprintf("TxID: %s, Name: %s", + txid.ToString().c_str(),m_Details.m_Name); ptr=(unsigned char *)m_Details.GetSpecialParam(MC_ENT_SPRM_FILTER_TYPE,&value_size); @@ -96,10 +98,26 @@ int mc_MultiChainFilter::Initialize(const unsigned char* short_txid) return MC_ERR_NOERROR; } +bool mc_MultiChainFilter::HasRelevantEntity(set & sRelevantEntities) +{ + if(m_RelevantEntities.size() == 0) + { + return true; + } + for(int i=0;i<(int)m_RelevantEntities.size();i++) + { + if(sRelevantEntities.find(m_RelevantEntities[i]) != sRelevantEntities.end()) + { + return true; + } + } + return false; +} int mc_MultiChainFilterEngine::Zero() { m_Filters.clear(); + m_TxID=0; return MC_ERR_NOERROR; } @@ -138,6 +156,8 @@ int mc_MultiChainFilterEngine::Add(const unsigned char* short_txid) return err; } + LogPrint("filter","Filter added: %s\n",m_Filters.back().m_FilterCaption.c_str()); + return MC_ERR_NOERROR; } @@ -145,6 +165,12 @@ int mc_MultiChainFilterEngine::Reset(int block) { int filter_block; int err; + + if(m_Filters.size() == 0) + { + return MC_ERR_NOERROR; + } + filter_block=m_Filters.back().m_Details.m_LedgerRow.m_Block; if(filter_block<0) { @@ -152,6 +178,7 @@ int mc_MultiChainFilterEngine::Reset(int block) } while( (m_Filters.size()>0) && (filter_block > block) ) { + LogPrint("filter","Filter rolled back: %s\n",m_Filters.back().m_FilterCaption.c_str()); m_Filters.back().Destroy(); m_Filters.pop_back(); if(m_Filters.size()>0) @@ -174,38 +201,51 @@ int mc_MultiChainFilterEngine::Reset(int block) } } + LogPrint("filter","Filter engine reset\n"); return MC_ERR_NOERROR; } -int mc_MultiChainFilterEngine::Run(uint256 txid,std::string &strResult,mc_MultiChainFilter **lppFilter) +int mc_MultiChainFilterEngine::Run(uint256 txid,std::set & sRelevantEntities,std::string &strResult,mc_MultiChainFilter **lppFilter) { int err; strResult=""; + m_TxID=txid; for(int i=0;i<(int)m_Filters.size();i++) { - if(m_Filters[i].m_CreateError.size()) + if(m_Filters[i].m_CreateError.size() == 0) { if(mc_gState->m_Permissions->FilterApproved(NULL,&(m_Filters[i].m_FilterAddress))) { - err=pFilterEngine->RunFilter(m_Filters[i].m_Filter,strResult); - if(err) - { - LogPrintf("Error while running filter %s, error: %d\n",m_Filters[i].m_FilterCaption.c_str(),err); - return err; - } - if(strResult.c_str()) + if(m_Filters[i].HasRelevantEntity(sRelevantEntities)) { - if(lppFilter) + err=pFilterEngine->RunFilter(m_Filters[i].m_Filter,strResult); + if(err) { - *lppFilter=&(m_Filters[i]); + LogPrintf("Error while running filter %s, error: %d\n",m_Filters[i].m_FilterCaption.c_str(),err); + return err; } - return MC_ERR_NOERROR; + if(strResult.size()) + { + if(lppFilter) + { + *lppFilter=&(m_Filters[i]); + } + LogPrint("filter","Tx rejected: %s, filter: %s\n",strResult.c_str(),m_Filters[i].m_FilterCaption.c_str()); + + return MC_ERR_NOERROR; + } + LogPrint("filter","Tx %s accepted, filter: %s\n",txid.ToString().c_str(),m_Filters[i].m_FilterCaption.c_str()); + } + else + { + LogPrint("filter","Irrelevant, filter: %s\n",m_Filters[i].m_FilterCaption.c_str()); } } } } + m_TxID=0; return MC_ERR_NOERROR; } diff --git a/src/protocol/multichainfilter.h b/src/protocol/multichainfilter.h index a3a8fb6c..3cb9ba6c 100644 --- a/src/protocol/multichainfilter.h +++ b/src/protocol/multichainfilter.h @@ -43,12 +43,15 @@ typedef struct mc_MultiChainFilter int Zero(); int Destroy(); - + + bool HasRelevantEntity(std::set & sRelevantEntities); + } mc_MultiChainFilter; typedef struct mc_MultiChainFilterEngine { std::vector m_Filters; + uint256 m_TxID; mc_MultiChainFilterEngine() { @@ -63,7 +66,7 @@ typedef struct mc_MultiChainFilterEngine int Initialize(); int Add(const unsigned char* short_txid); int Reset(int block); - int Run(uint256 txid, std::string &strResult,mc_MultiChainFilter **lppFilter); + int Run(uint256 txid,std::set & sRelevantEntities,std::string &strResult,mc_MultiChainFilter **lppFilter); int Zero(); int Destroy(); diff --git a/src/protocol/multichaintx.cpp b/src/protocol/multichaintx.cpp index 9eb698cc..66d918ec 100644 --- a/src/protocol/multichaintx.cpp +++ b/src/protocol/multichaintx.cpp @@ -46,6 +46,7 @@ typedef struct CMultiChainTxDetails set vAllowedAdmins; // Admin permissions before this tx - for grants set vAllowedActivators; // Activate permissions before this tx - for grants + set vRelevantEntities; // Set of entities involved in this transaction vector vOutputScriptFlags; // Output script flags, filled when script is processed for the first time vector vOutputPermissionRequired; // Number of required receive permissions @@ -74,6 +75,8 @@ typedef struct CMultiChainTxDetails void Zero(); bool IsRelevantInput(int vin,int vout); + void SetRelevantEntity(void *entity); + bool IsRelevantEntity(uint160 hash); } CMultiChainTxDetails; @@ -119,6 +122,25 @@ bool CMultiChainTxDetails::IsRelevantInput(int vin, int vout) return false; } +bool CMultiChainTxDetails::IsRelevantEntity(uint160 hash) +{ + if(vRelevantEntities.find(hash) != vRelevantEntities.end()) + { + return true; + } + return false; +} + +void CMultiChainTxDetails::SetRelevantEntity(void *entity) +{ + uint160 hash; + memcpy(&hash,entity,MC_AST_SHORT_TXID_SIZE); + if(!IsRelevantEntity(hash)) + { + vRelevantEntities.insert(hash); + } +} + uint160 mc_GenesisAdmin(const CTransaction& tx) { uint32_t type,from,to,timestamp; @@ -212,7 +234,7 @@ bool mc_ExtractInputAssetQuantities(mc_Buffer *assets, const CScript& script1, u return true; } -bool mc_CompareAssetQuantities(string& reason) +bool mc_CompareAssetQuantities(CMultiChainTxDetails *details,string& reason) { unsigned char *ptrIn; unsigned char *ptrOut; @@ -255,6 +277,8 @@ bool mc_CompareAssetQuantities(string& reason) return false; } + details->SetRelevantEntity((unsigned char*)entity.GetTxID()+MC_AST_SHORT_TXID_OFFSET); + if(quantity>0) { if(row>=0) @@ -881,6 +905,8 @@ bool MultiChainTransaction_CheckEntityItem(const CTransaction& tx, return false; } + details->SetRelevantEntity(short_txid); + if(entity.GetEntityType() == MC_ENT_TYPE_ASSET) // Asset update { if(!MultiChainTransaction_CheckAssetUpdateDetails(&entity,vout,details,reason)) @@ -1031,7 +1057,9 @@ bool MultiChainTransaction_ProcessPermissions(const CTransaction& tx, { reason="Script rejected - entity not found"; return false; - } + } + + details->SetRelevantEntity(short_txid); } else // Not entity element { @@ -1381,7 +1409,7 @@ bool MultiChainTransaction_CheckOutputs(const CTransaction& tx, } } - if(!mc_CompareAssetQuantities(reason)) // Comparing input/output asset quantities + if(!mc_CompareAssetQuantities(details,reason)) // Comparing input/output asset quantities { return false; } @@ -1498,7 +1526,6 @@ bool MultiChainTransaction_ProcessAssetIssuance(const CTransaction& tx, reason="Metadata script rejected - wrong element, should be entityref"; return false; } - } if(details->vOutputScriptFlags[vout] & MC_MTX_OUTPUT_DETAIL_FLAG_NOT_OP_RETURN) @@ -1643,6 +1670,7 @@ bool MultiChainTransaction_ProcessAssetIssuance(const CTransaction& tx, reason="Details script rejected - entity not found"; return false; } + details->SetRelevantEntity(short_txid); } else { @@ -2097,10 +2125,9 @@ bool AcceptMultiChainTransaction (const CTransaction& tx, { if(pMultiChainFilterEngine) { - string filter_error; mc_MultiChainFilter* lpFilter; - if(pMultiChainFilterEngine->Run(tx.GetHash(),reason,&lpFilter) != MC_ERR_NOERROR) + if(pMultiChainFilterEngine->Run(tx.GetHash(),details.vRelevantEntities,reason,&lpFilter) != MC_ERR_NOERROR) { reason="Error while running filters"; fReject=true; diff --git a/src/rpc/rpcfilters.cpp b/src/rpc/rpcfilters.cpp index 6e3c15d9..96721e40 100644 --- a/src/rpc/rpcfilters.cpp +++ b/src/rpc/rpcfilters.cpp @@ -567,3 +567,42 @@ Value listfilters(const Array& params, bool fHelp) return results; } + +Value getfiltercode(const Array& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error("Help message not found\n"); + + if(mc_gState->m_Features->Filters() == 0) + { + throw JSONRPCError(RPC_NOT_SUPPORTED, "API is not supported with this protocol version."); + } + + mc_EntityDetails filter_entity; + ParseEntityIdentifier(params[0],&filter_entity,MC_ENT_TYPE_FILTER); + + char *ptr; + size_t value_size; + char filter_code[MC_ENT_MAX_SCRIPT_SIZE+1]; + + ptr=(char *)filter_entity.GetSpecialParam(MC_ENT_SPRM_FILTER_CODE,&value_size); + + if(ptr == NULL) + { + return Value::null; + } + + if(value_size) + { + memcpy(filter_code,ptr,value_size); + + } + filter_code[value_size]=0x00; + + return string(filter_code); +} + +Value getfiltertxid(const Array& params, bool fHelp) +{ + return pMultiChainFilterEngine->m_TxID.ToString(); +} \ No newline at end of file diff --git a/src/rpc/rpchelp.cpp b/src/rpc/rpchelp.cpp index 7cca95d7..548ba0fb 100644 --- a/src/rpc/rpchelp.cpp +++ b/src/rpc/rpchelp.cpp @@ -4175,10 +4175,6 @@ void mc_InitRPCHelpMap18() + HelpExampleRpc("liststreamqueryitems", "\"test-stream\", \"{\\\"keys\\\":[\\\"key01\\\",\"key02\"],\\\"publisher\\\":\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\"}\", false") )); - mapHelpStrings.insert(std::make_pair("AAAAAAA", - "" - )); - mapHelpStrings.insert(std::make_pair("listfilters", "listfilters ( filter-identifier(s) verbose )\n" "\nReturns list of defined filters\n" @@ -4193,6 +4189,23 @@ void mc_InitRPCHelpMap18() + HelpExampleCli("listfilters", "") + HelpExampleRpc("listfilters", "") )); + + mapHelpStrings.insert(std::make_pair("getfiltercode", + "getfiltercode \"filter-identifier\"\n" + "\nReturns code for specified filter\n" + "\nArguments:\n" + "1. \"filter-identifier\" (string, optional) Filter identifier - one of the following: create txid, filter reference, filter name.\n" + "\nResult:\n" + "Filter code in plain text\n" + "\nExamples:\n" + + HelpExampleCli("getfiltercode", "filter1") + + HelpExampleRpc("getfiltercode", "filter1") + )); + + mapHelpStrings.insert(std::make_pair("AAAAAAA", + "" + )); + } diff --git a/src/rpc/rpclist.cpp b/src/rpc/rpclist.cpp index 3dbc6691..e622b500 100644 --- a/src/rpc/rpclist.cpp +++ b/src/rpc/rpclist.cpp @@ -95,6 +95,7 @@ static const CRPCCommand vRPCCommands[] = { "blockchain", "liststreams", &liststreams, true, false, false }, { "blockchain", "listupgrades", &listupgrades, true, false, false }, { "blockchain", "listfilters", &listfilters, true, false, false }, + { "blockchain", "getfiltercode", &getfiltercode, true, false, false }, { "blockchain", "listblocks", &listblocks, true, false, false }, /* MCHN END */ diff --git a/src/rpc/rpcserver.h b/src/rpc/rpcserver.h index 74ce31d7..f81b077d 100644 --- a/src/rpc/rpcserver.h +++ b/src/rpc/rpcserver.h @@ -249,6 +249,8 @@ extern json_spirit::Value listaddresses(const json_spirit::Array& params, bool f extern json_spirit::Value liststreams(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value listupgrades(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value listfilters(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getfiltercode(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value getfiltertxid(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value createcmd(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value createfromcmd(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value publish(const json_spirit::Array& params, bool fHelp); From 9f8dae44b535e59fada1f1247db46ee47f16a297 Mon Sep 17 00:00:00 2001 From: mike31 Date: Mon, 20 Aug 2018 08:17:19 +0300 Subject: [PATCH 007/280] Fixed filter code initialization --- src/protocol/multichainfilter.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/protocol/multichainfilter.cpp b/src/protocol/multichainfilter.cpp index e4c4622c..3057bb76 100644 --- a/src/protocol/multichainfilter.cpp +++ b/src/protocol/multichainfilter.cpp @@ -87,14 +87,13 @@ int mc_MultiChainFilter::Initialize(const unsigned char* short_txid) if(ptr) { - m_CreateError="Empty filter code"; + memcpy(m_FilterCode,ptr,value_size); + m_FilterCode[value_size]=0x00; } else { - memcpy(m_FilterCode,ptr,value_size); - m_FilterCode[value_size]=0x00; + m_CreateError="Empty filter code"; } - return MC_ERR_NOERROR; } @@ -147,7 +146,7 @@ int mc_MultiChainFilterEngine::Add(const unsigned char* short_txid) } m_Filters.push_back(filter); - + err=pFilterEngine->CreateFilter(m_Filters.back().m_FilterCode,m_Filters.back().m_MainName.c_str(),&(m_Filters.back().m_Filter),m_Filters.back().m_CreateError); if(err) { From 23217be04529ffd62a2cc7a3b589b9e8968a0e52 Mon Sep 17 00:00:00 2001 From: Zvi Tarem Date: Sun, 19 Aug 2018 07:12:34 +0300 Subject: [PATCH 008/280] Implement V8 filter engine --- .gitignore | 33 +++--- src/Makefile.am | 27 ++++- src/core/init.cpp | 7 +- src/protocol/filter.cpp | 72 +++++++++--- src/protocol/filter.h | 99 +++++++++++----- src/v8/callbacks.cpp | 83 +++++++++++++ src/v8/callbacks.h | 21 ++++ src/v8/fixture.js | 33 ++++++ src/v8/v8engine.cpp | 61 ++++++++++ src/v8/v8engine.h | 60 ++++++++++ src/v8/v8filter.cpp | 253 ++++++++++++++++++++++++++++++++++++++++ src/v8/v8filter.h | 94 +++++++++++++++ src/v8/v8utils.h | 39 +++++++ 13 files changed, 822 insertions(+), 60 deletions(-) create mode 100644 src/v8/callbacks.cpp create mode 100644 src/v8/callbacks.h create mode 100644 src/v8/fixture.js create mode 100644 src/v8/v8engine.cpp create mode 100644 src/v8/v8engine.h create mode 100644 src/v8/v8filter.cpp create mode 100644 src/v8/v8filter.h create mode 100644 src/v8/v8utils.h diff --git a/.gitignore b/.gitignore index 880a46ac..2b95c432 100644 --- a/.gitignore +++ b/.gitignore @@ -16,19 +16,19 @@ src/qt/test/test_bitcoin-qt Makefile.in aclocal.m4 autom4te.cache/ -build-aux/config.guess -build-aux/config.sub -build-aux/depcomp -build-aux/install-sh -build-aux/ltmain.sh -build-aux/m4/libtool.m4 -build-aux/m4/lt~obsolete.m4 -build-aux/m4/ltoptions.m4 -build-aux/m4/ltsugar.m4 -build-aux/m4/ltversion.m4 -build-aux/missing -build-aux/compile -build-aux/test-driver +**/build-aux/config.guess +**/build-aux/config.sub +**/build-aux/depcomp +**/build-aux/install-sh +**/build-aux/ltmain.sh +**/build-aux/m4/libtool.m4 +**/build-aux/m4/lt~obsolete.m4 +**/build-aux/m4/ltoptions.m4 +**/build-aux/m4/ltsugar.m4 +**/build-aux/m4/ltversion.m4 +**/build-aux/missing +**/build-aux/compile +**/build-aux/test-driver config.log config.status configure @@ -115,7 +115,14 @@ qa/pull-tester/run-bitcoind-for-test.sh qa/pull-tester/tests-config.sh qa/pull-tester/cache/* qa/pull-tester/test.*/* +test_filters.sh !src/leveldb*/Makefile /doc/doxygen/ + +# Eclipse CDT +.autotools +.cproject +.project +.settings/ diff --git a/src/Makefile.am b/src/Makefile.am index c712fd3d..a10ffaf6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,10 +1,12 @@ walDIST_SUBDIRS = secp256k1 AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) +CXXFLAGS += -Wno-implicit-fallthrough -std=c++0x if EMBEDDED_LEVELDB LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/include LEVELDB_CPPFLAGS += -I$(srcdir)/leveldb/helpers/memenv +LEVELDB_CPPFLAGS += -Wno-deprecated LIBLEVELDB += $(builddir)/leveldb/libleveldb.a LIBMEMENV += $(builddir)/leveldb/libmemenv.a @@ -167,7 +169,7 @@ obj/build.h: FORCE libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h # server: shared between bitcoind and bitcoin-qt -libbitcoin_server_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) +libbitcoin_server_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(V8_INCLUDE) libbitcoin_server_a_SOURCES = \ storage/addrman.cpp \ structs/alert.cpp \ @@ -182,6 +184,9 @@ libbitcoin_server_a_SOURCES = \ custom/custom_server.cpp \ protocol/multichainfilter.cpp \ protocol/filter.cpp \ + v8/v8engine.cpp \ + v8/v8filter.cpp \ + v8/callbacks.cpp \ protocol/relay.cpp \ protocol/handshake.cpp \ chain/merkleblock.cpp \ @@ -408,6 +413,22 @@ endif # MCHN START +V8_RELEASE = $(HOME)/local/v8 +V8_INCLUDE = -I$(V8_RELEASE)/include +V8_LIB_DIR = $(V8_RELEASE)/lib +V8_LIBS = \ + -L$(V8_RELEASE)/lib \ + -Wl,--start-group \ + -lv8_libbase \ + -lv8_libplatform \ + -lv8_base \ + -lv8_external_snapshot \ + -lv8_libsampler \ + -licuuc \ + -licui18n \ + -lv8pp \ + -lrt -ldl + # multichaind binary # multichaind_LDADD = \ $(LIBBITCOIN_SERVER) \ @@ -433,7 +454,7 @@ endif multichaind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) multichaind_CPPFLAGS = $(BITCOIN_INCLUDES) -multichaind_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +multichaind_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(V8_LIBS) -pthread # multichaind-cold binary # multichaind_cold_LDADD = \ @@ -460,7 +481,7 @@ endif multichaind_cold_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) multichaind_cold_CPPFLAGS = $(BITCOIN_INCLUDES) -multichaind_cold_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +multichaind_cold_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(V8_LIBS) -pthread # multichain-cli binary # multichain_cli_LDADD = \ diff --git a/src/core/init.cpp b/src/core/init.cpp index 037fce3e..853f536e 100644 --- a/src/core/init.cpp +++ b/src/core/init.cpp @@ -1888,10 +1888,11 @@ bool AppInit2(boost::thread_group& threadGroup,int OutputPipe) return InitError(_("Failed to listen on any port. Use -listen=0 if you want this.")); } /* MCHN START */ - pFilterEngine=new mc_FilterEngine; - if(pFilterEngine->Initialize()) + std::string strResult; + pFilterEngine=new mc_FilterEngine(); + if (pFilterEngine->Initialize(strResult) != MC_ERR_NOERROR) { - return InitError(_("Couldn't initialize filter engine.")); + return InitError(strprintf(_("Couldn't initialize filter engine: '%s'"), strResult)); } pMultiChainFilterEngine=new mc_MultiChainFilterEngine; diff --git a/src/protocol/filter.cpp b/src/protocol/filter.cpp index 2b906f99..04bcb9ec 100644 --- a/src/protocol/filter.cpp +++ b/src/protocol/filter.cpp @@ -1,42 +1,88 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2018 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. +#include "v8/v8engine.h" +#include "v8/v8filter.h" #include "protocol/filter.h" +#include "utils/util.h" +#include "utils/define.h" -int mc_Filter::Zero() +namespace mc_v8 { - return MC_ERR_NOERROR; +class V8Filter; +} + +void mc_Filter::Zero() +{ +// LogPrintf("mc_Filter: Zero\n"); + m_Impl = nullptr; } int mc_Filter::Destroy() { +// LogPrintf("mc_Filter: Destroy\n"); + if (m_Impl != nullptr) + { + auto v8filter = static_cast(m_Impl); + delete v8filter; + } + + this->Zero(); return MC_ERR_NOERROR; } - -int mc_FilterEngine::Zero() +int mc_Filter::Initialize(std::string &strResult) { + LogPrintf("mc_Filter: Initialize\n"); + m_Impl = new mc_v8::V8Filter(); return MC_ERR_NOERROR; } +void mc_FilterEngine::Zero() +{ +// LogPrintf("mc_FilterEngine: Zero\n"); + m_Impl = nullptr; +} + int mc_FilterEngine::Destroy() { +// LogPrintf("mc_FilterEngine: Destroy\n"); + if (m_Impl != nullptr) + { + auto v8engine = static_cast(m_Impl); + delete v8engine; + } + + this->Zero(); return MC_ERR_NOERROR; } -int mc_FilterEngine::Initialize() +int mc_FilterEngine::Initialize(std::string& strResult) { - return MC_ERR_NOERROR; + LogPrintf("mc_FilterEngine: Initialize\n"); + auto v8engine = new mc_v8::V8Engine(); + m_Impl = v8engine; + return v8engine->Initialize(strResult); } -int mc_FilterEngine::CreateFilter(const char *script,const char* main_name,mc_Filter *filter,std::string &strResult) +int mc_FilterEngine::CreateFilter(std::string script, std::string main_name, mc_Filter* filter, std::string& strResult) { - strResult=""; - return MC_ERR_NOERROR; + LogPrintf("mc_FilterEngine: CreateFilter\n"); + auto v8engine = static_cast(m_Impl); + filter->Destroy(); + int result = filter->Initialize(strResult); + if (result != MC_ERR_NOERROR) + { + return result; + } + auto v8filter = static_cast(filter->m_Impl); + return v8engine->CreateFilter(script, main_name, v8filter, strResult); } -int mc_FilterEngine::RunFilter(const mc_Filter& filter,std::string &strResult) +int mc_FilterEngine::RunFilter(const mc_Filter& filter, std::string& strResult) { - strResult=""; - return MC_ERR_NOERROR; + LogPrintf("mc_FilterEngine: RunFilter\n"); + strResult.clear(); + auto v8filter = static_cast(filter.m_Impl); + return v8filter->Run(strResult); } diff --git a/src/protocol/filter.h b/src/protocol/filter.h index d3370160..e8d0f302 100644 --- a/src/protocol/filter.h +++ b/src/protocol/filter.h @@ -1,54 +1,97 @@ -// Copyright (c) 2014-2017 Coin Sciences Ltd +// Copyright (c) 2014-2018 Coin Sciences Ltd // MultiChain code distributed under the GPLv3 license, see COPYING file. #ifndef MULTICHAIN_FILTER_H #define MULTICHAIN_FILTER_H -#include "utils/declare.h" -#include "utils/util.h" +#include +class mc_FilterEngine; +/** + * A user-defined transaction filter. + */ +class mc_Filter +{ + friend class mc_FilterEngine; -typedef struct mc_Filter -{ - void *m_Impl; - +public: mc_Filter() { Zero(); } - + ~mc_Filter() { Destroy(); } - - - int Zero(); - int Destroy(); -} mc_Filter; - -typedef struct mc_FilterEngine -{ + + void Zero(); + int Destroy(); + + /** + * Initialize the transaction filter. + * + * @param strResult Reason for failure if unsuccessful. + * @return MC_ERR_NOERROR if successful, MC_ERR_INTERNAL_ERROR if not. + */ + int Initialize(std::string &strResult); + +private: + void *m_Impl; + +}; // class mc_Filter + +/** + * An environment for creating and defining transaction filters. + */ +class mc_FilterEngine +{ +public: mc_FilterEngine() { Zero(); } - + ~mc_FilterEngine() { Destroy(); } - - - int Initialize(); - - int CreateFilter(const char *script,const char* main_name,mc_Filter *filter,std::string &strResult); - int RunFilter(const mc_Filter& filter,std::string &strResult); - - int Zero(); + + void Zero(); int Destroy(); - -} mc_FilterEngine; -#endif /* MULTICHAIN_FILTER_H */ \ No newline at end of file + /** + * Initialize the environment. + * + * @param strResult Reason for failure if unsuccessful. + * @return MC_ERR_NOERROR if successful, MC_ERR_INTERNAL_ERROR if not. + */ + int Initialize(std::string &strResult); + + /** + * Initialize a user-defined transaction filter. + * + * @param script The filter JS code. + * @param main_name The expected name of the filtering function in the script. + * @param filter The user-defined transaction filter to initialize. + * @param strResult Reason for failure if unsuccessful. + * @return MC_ERR_NOERROR if successful, MC_ERR_INTERNAL_ERROR if not. + */ + int CreateFilter(std::string script, std::string main_name, mc_Filter* filter, std::string& strResult); + + /** + * Run the filter function in the JS script. + * + * @param filter The user-defined transaction filter to use. + * @param strResult Reason for script failure or transaction rejection. + * @return MC_ERR_NOERROR if the transaction was accepted, MC_ERR_INTERNAL_ERROR if not. + */ + int RunFilter(const mc_Filter& filter, std::string& strResult); + +private: + void *m_Impl; + +}; // class mc_FilterEngine + +#endif /* MULTICHAIN_FILTER_H */ diff --git a/src/v8/callbacks.cpp b/src/v8/callbacks.cpp new file mode 100644 index 00000000..3171617b --- /dev/null +++ b/src/v8/callbacks.cpp @@ -0,0 +1,83 @@ +// Copyright (c) 2014-2018 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#include "callbacks.h" +#include "v8engine.h" +#include "v8utils.h" +#include "utils/util.h" +#include "rpc/rpcserver.h" +#include + +namespace mc_v8 +{ + +/** + * Signature of a function to remove non-deterministic or sensitive elements from RPC function output. + * + * @param value The RPC function output value. Transform this value in-place. + */ +typedef void (*fnSanitize)(json_spirit::Value& value); + +/** + * Call an RPC function from a V8 JS callback. + * + * Marshal the arguments and the return value between V8 and json_spirit using intermediate JSON strings. + * Optionally filter the result before returning it to JS. + * + * @param rpcFunction The RPC function to call. + * @param args The V8 arguments/return value. + * @param sanitize An optional function to transform the RPC function result before returning it to JS. + */ +void CallRpcFunction(rpcfn_type rpcFunction, const v8::FunctionCallbackInfo& args, + fnSanitize sanitize = nullptr) +{ + v8::Isolate* isolate = args.GetIsolate(); + v8::Locker locker(isolate); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + v8::Local context(isolate->GetCurrentContext()); + v8::Context::Scope contextScope(context); + + auto args_array = v8::Array::New(isolate, args.Length()); + for (int i = 0; i < args.Length(); ++i) + { + args_array->Set(i, args[i]); + } + v8::Local argsJson = v8::JSON::Stringify(context, args_array).ToLocalChecked(); + std::string argsString = V82String(isolate, argsJson); + json_spirit::Value params; + json_spirit::read_string(argsString, params); + + json_spirit::Value result = rpcFunction(params.get_array(), false); + + if (sanitize != nullptr) + { + sanitize(result); + } + + std::string resultString = json_spirit::write_string(result, false); + v8::Local resultJson = String2V8(isolate, resultString); + args.GetReturnValue().Set(v8::JSON::Parse(context, resultJson).ToLocalChecked()); +} + +#define FILTER_FUNCTION(name) \ + void filter_##name(const v8::FunctionCallbackInfo& args) \ + { \ + CallRpcFunction(name, args); \ + } + +#define FILTER_FUNCTION_SANITIZE(name, sanitize) \ + void filter_##name(const v8::FunctionCallbackInfo& args) \ + { \ + CallRpcFunction(name, args, sanitize); \ + } + +FILTER_FUNCTION(getfiltertxid) +FILTER_FUNCTION(getrawtransaction) +FILTER_FUNCTION(gettxout) +FILTER_FUNCTION(listassets) +FILTER_FUNCTION(liststreams) +FILTER_FUNCTION(listpermissions) + +} // namespace mc_v8 + diff --git a/src/v8/callbacks.h b/src/v8/callbacks.h new file mode 100644 index 00000000..5bca155c --- /dev/null +++ b/src/v8/callbacks.h @@ -0,0 +1,21 @@ +// Copyright (c) 2014-2018 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#ifndef MULTICHAIN_CALLBACKS_H_ +#define MULTICHAIN_CALLBACKS_H_ + +#include + +namespace mc_v8 +{ + +void filter_getfiltertxid(const v8::FunctionCallbackInfo& args); +void filter_getrawtransaction(const v8::FunctionCallbackInfo& args); +void filter_gettxout(const v8::FunctionCallbackInfo& args); +void filter_listassets(const v8::FunctionCallbackInfo& args); +void filter_liststreams(const v8::FunctionCallbackInfo& args); +void filter_listpermissions(const v8::FunctionCallbackInfo& args); + +} // namespace mc_v8 + +#endif /* MULTICHAIN_CALLBACKS_H_ */ diff --git a/src/v8/fixture.js b/src/v8/fixture.js new file mode 100644 index 00000000..c1aef7e5 --- /dev/null +++ b/src/v8/fixture.js @@ -0,0 +1,33 @@ +Math.random = function(){ + return 0; +}; + +Date.now = function() { + return 0; +}; + +var bind = Function.bind; +var unbind = bind.bind(bind); + +function instantiate(constructor, args) { + return new (unbind(constructor, null).apply(null, args)); +} + +Date = function (Date) { + var names = Object.getOwnPropertyNames(Date); + for (var i = 0; i < names.length; i++) { + // Skip props already in the MyDate object + if (names[i] in MyDate) continue; + var desc = Object.getOwnPropertyDescriptor(Date, names[i]); + Object.defineProperty(MyDate, names[i], desc); + } + + return MyDate; + + function MyDate() { + if (arguments.length == 0) { + arguments = [0]; + } + return instantiate(Date, arguments); + } +}(Date); diff --git a/src/v8/v8engine.cpp b/src/v8/v8engine.cpp new file mode 100644 index 00000000..5ae943fe --- /dev/null +++ b/src/v8/v8engine.cpp @@ -0,0 +1,61 @@ +// Copyright (c) 2014-2018 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#include "v8engine.h" +#include "v8filter.h" +#include "utils/define.h" +#include "utils/util.h" +#include +#include + +namespace fs = boost::filesystem; + +namespace mc_v8 +{ + +void V8Engine::Zero() +{ +// LogPrintf("V8Engine: Zero\n"); +} + +int V8Engine::Destroy() +{ +// LogPrintf("V8Engine: Destroy\n"); + v8::V8::Dispose(); + v8::V8::ShutdownPlatform(); + this->Zero(); + return MC_ERR_NOERROR; +} + +int V8Engine::Initialize(std::string& strResult) +{ + LogPrintf("V8Engine: Initialize\n"); + std::string argv0 = (fs::path(std::getenv("HOME")) / "local" / "v8" / "data" / "foo").string(); + + strResult.clear(); + v8::V8::InitializeICUDefaultLocation(argv0.c_str()); + v8::V8::InitializeExternalStartupData(argv0.c_str()); + m_platform = v8::platform::NewDefaultPlatform(); + v8::V8::InitializePlatform(m_platform.get()); + if (!v8::V8::Initialize()) + { + strResult = "Error initializing V8"; + return MC_ERR_INTERNAL_ERROR; + } + + return MC_ERR_NOERROR; +} + +int V8Engine::CreateFilter(std::string script, std::string main_name, V8Filter* filter, std::string& strResult) +{ + LogPrintf("V8Engine: CreateFilter\n"); + int status = filter->Initialize(script, main_name, strResult); + if (status != MC_ERR_NOERROR) + { + delete filter; + } + + return status; +} + +} // namespace mc_v8 diff --git a/src/v8/v8engine.h b/src/v8/v8engine.h new file mode 100644 index 00000000..d6c84da2 --- /dev/null +++ b/src/v8/v8engine.h @@ -0,0 +1,60 @@ +// Copyright (c) 2014-2018 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#ifndef MULTICHAIN_V8ENGINE_H_ +#define MULTICHAIN_V8ENGINE_H_ + +#include +#include + +namespace mc_v8 +{ + +class V8Filter; + +/** + * Interface to the Google V8 engine to create and run transaction filters. + */ +class V8Engine +{ +public: + V8Engine() + { + Zero(); + } + + ~V8Engine() + { + Destroy(); + } + + void Zero(); + int Destroy(); + + /** + * Initialize the V8 engine. + * + * @param strResult Reason for failure if unsuccessful. + * @return MC_ERR_NOERROR if successful, MC_ERR_INTERNAL_ERROR if not. + */ + int Initialize(std::string& strResult); + + /** + * Create a new transaction filter. + * + * @param script The filter JS code. + * @param main_name The expected name of the filtering function in the script. + * @param filter The filter object to initialize. + * @param strResult Reason for failure if unsuccessful. + * @return MC_ERR_NOERROR if successful, MC_ERR_INTERNAL_ERROR if not. + */ + int CreateFilter(std::string script, std::string main_name, V8Filter* filter, std::string& strResult); + +private: + + std::unique_ptr m_platform; +}; + +} // namespace mc_v8 + +#endif /* MULTICHAIN_V8ENGINE_H_ */ diff --git a/src/v8/v8filter.cpp b/src/v8/v8filter.cpp new file mode 100644 index 00000000..ed6e5f26 --- /dev/null +++ b/src/v8/v8filter.cpp @@ -0,0 +1,253 @@ +// Copyright (c) 2014-2018 Coin Sciences Ltd +// MultiChain code distributed under the GPLv3 license, see COPYING file. + +#include "v8filter.h" +#include "v8utils.h" +#include "callbacks.h" +#include "utils/define.h" +#include "utils/util.h" +#include + +namespace mc_v8 +{ + +V8IsolateManager* V8IsolateManager::m_instance = nullptr; + +V8IsolateManager* V8IsolateManager::Instance() +{ + if (m_instance == nullptr) + { + m_instance = new V8IsolateManager(); + } + return m_instance; +} + +v8::Isolate* V8IsolateManager::GetIsolate() +{ + if (m_isolate == nullptr) + { + m_createParams.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); + m_isolate = v8::Isolate::New(m_createParams); + } + return m_isolate; +} + +V8IsolateManager::~V8IsolateManager() +{ + if (m_isolate != nullptr) + { + m_isolate->Dispose(); + m_isolate = nullptr; + delete m_createParams.array_buffer_allocator; + } +} + +static std::string jsFixture = + R"( +Math.random = function() { + return 0; +}; + +Date.now = function() { + return 0; +}; + +var bind = Function.bind; +var unbind = bind.bind(bind); + +function instantiate(constructor, args) { + return new (unbind(constructor, null).apply(null, args)); +} + +Date = function (Date) { + var names = Object.getOwnPropertyNames(Date); + // Loop through them + for (var i = 0; i < names.length; i++) { + // Skip props already in the MyDate object + if (names[i] in MyDate) continue; + // Get property description from o + var desc = Object.getOwnPropertyDescriptor(Date, names[i]); + // Use it to create property on MyDate + Object.defineProperty(MyDate, names[i], desc); + } + + return MyDate; + + function MyDate() { + if (arguments.length == 0) { + arguments = [0]; + } + return instantiate(Date, arguments); + } +}(Date); + +console.log(`Math.randon()=${Math.random()}`); +console.log(`Date.now()=${Date.now()}`); +console.log(`Date()=${Date()}`); +console.log("Finished loading fixture.js"); +)"; + +void V8Filter::Zero() +{ +// LogPrintf("V8Filter: Zero\n"); + m_isolate = nullptr; +} + +int V8Filter::Destroy() +{ +// LogPrintf("V8Filter: Destroy\n"); + m_filterFunction.Reset(); + m_context.Reset(); + this->Zero(); + return MC_ERR_NOERROR; +} + +#define REGISTER_RPC(name) global->Set(String2V8(m_isolate, #name), v8::FunctionTemplate::New(m_isolate, filter_##name)) + +int V8Filter::Initialize(std::string script, std::string functionName, std::string& strResult) +{ + LogPrintf("V8Filter: Initialize\n"); + strResult.clear(); + this->MaybeCreateIsolate(); + v8::Locker locker(m_isolate); + v8::Isolate::Scope isolateScope(m_isolate); + v8::HandleScope handleScope(m_isolate); + auto global = v8::ObjectTemplate::New(m_isolate); + REGISTER_RPC(getfiltertxid); + REGISTER_RPC(getrawtransaction); + REGISTER_RPC(gettxout); + REGISTER_RPC(listassets); + REGISTER_RPC(liststreams); + REGISTER_RPC(listpermissions); + auto context = v8::Context::New(m_isolate, nullptr, global); + m_context.Reset(m_isolate, context); + + int status = this->CompileAndLoadScript(jsFixture, "", "fixture", strResult); + if (status == MC_ERR_NOERROR && !script.empty()) + { + status = this->CompileAndLoadScript(script, functionName, "