def signElement(elem: Element) -> Signature: if isinstance(elem, Verification): verif: SignedVerification = SignedVerification(elem.hash, elem.holder) verif.sign(elem.holder, PrivateKey(elem.holder)) return verif.signature if isinstance(elem, SignedMeritRemovalVerificationPacket): return elem.signature if isinstance(elem, SendDifficulty): sendDiff: SignedSendDifficulty = SignedSendDifficulty( elem.difficulty, elem.nonce) sendDiff.sign(elem.holder, PrivateKey(elem.holder)) return sendDiff.signature if isinstance(elem, DataDifficulty): dataDiff: SignedDataDifficulty = SignedDataDifficulty( elem.difficulty, elem.nonce) dataDiff.sign(elem.holder, PrivateKey(elem.holder)) return dataDiff.signature if isinstance(elem, MeritRemoval): result: Signature = signElement(elem.e2) if not elem.partial: result = Signature.aggregate([result, signElement(elem.e1)]) return result raise GenerationError( "Tried to sign an Element in a Block we didn't recognize the type of.")
def voteAndVerify() -> None: #Check the difficulties for a holder who has yet to vote. try: rpc.call("consensus", "getSendDifficulty", {"holder": 0}, False) raise TestError("") except TestError as e: if str(e) != "-2 Holder doesn't have a SendDifficulty.": raise TestError("getSendDifficulty didn't raise when asked about a holder who has yet to vote.") try: rpc.call("consensus", "getDataDifficulty", {"holder": 0}, False) raise TestError("") except TestError as e: if str(e) != "-2 Holder doesn't have a DataDifficulty.": raise TestError("getDataDifficulty didn't raise when asked about a holder who has yet to vote.") #Create the votes. sendDiff: SignedSendDifficulty = SignedSendDifficulty(6, 0) sendDiff.sign(0, PrivateKey(0)) dataDiff: SignedDataDifficulty = SignedDataDifficulty(10, 1) dataDiff.sign(0, PrivateKey(0)) #Send them. rpc.meros.signedElement(sendDiff) rpc.meros.signedElement(dataDiff) rpc.meros.live.recv() rpc.meros.live.recv() #Check them. if rpc.call("consensus", "getSendDifficulty", {"holder": 0}, False) != 6: raise TestError("getSendDifficulty didn't reply with the holder's current difficulty.") if rpc.call("consensus", "getDataDifficulty", {"holder": 0}, False) != 10: raise TestError("getDataDifficulty didn't reply with the holder's current difficulty.")
def mineBlock(rpc: RPC, nick: int = 0) -> None: privKey: PrivateKey = PrivateKey(nick) template: Dict[str, Any] = rpc.call( "merit", "getBlockTemplate", {"miner": privKey.toPublicKey().serialize().hex()}) header: bytes = bytes.fromhex(template["header"])[:-4] header += (rpc.call( "merit", "getBlock", {"block": rpc.call("merit", "getHeight") - 1})["header"]["time"] + 1200).to_bytes(4, "little") proof: int = -1 tempHash: bytes = bytes() signature: bytes = bytes() while ((proof == -1) or ((int.from_bytes(tempHash, "little") * template["difficulty"]) > int.from_bytes(bytes.fromhex("FF" * 32), "little"))): proof += 1 tempHash = RandomX(header + proof.to_bytes(4, "little")) signature = privKey.sign(tempHash).serialize() tempHash = RandomX(tempHash + signature) rpc.call( "merit", "publishBlock", { "id": template["id"], "header": header.hex() + proof.to_bytes(4, "little").hex() + signature.hex() }) if rpc.meros.live.recv() != (MessageType.BlockHeader.toByte() + header + proof.to_bytes(4, "little") + signature): raise TestError("Meros didn't broadcast back the BlockHeader.")
def sign( self, holder: int, privKey: PrivateKey ) -> None: self.holder = holder self.signature = privKey.sign(self.signatureSerialize())
def HundredSixSignedElementsTest(rpc: RPC) -> None: #Solely used to get the genesis Block hash. blockchain: Blockchain = Blockchain() blsPrivKey: PrivateKey = PrivateKey(0) sig: Signature = blsPrivKey.sign(bytes()) #Create a Data. #This is required so the Verification isn't terminated early for having an unknown hash. data: bytes = bytes.fromhex(rpc.call("personal", "data", ["AA"])) #Create a signed Verification, SendDifficulty, and DataDifficulty. elements: List[SignedElement] = [ SignedVerification(data, 1, sig), SignedSendDifficulty(0, 0, 1, sig), SignedDataDifficulty(0, 0, 1, sig) ] for elem in elements: #Handshake with the node. rpc.meros.liveConnect(blockchain.blocks[0].header.hash) #Send the Element. rpc.meros.signedElement(elem) #Sleep for thirty seconds to make sure Meros realizes our connection is dead. sleep(30) #Verify the node didn't crash. try: if rpc.call("merit", "getHeight") != 1: raise Exception() except Exception: raise TestError( "Node crashed after being sent a malformed Element.")
def add(self, nick: int = 0, packets: List[VerificationPacket] = [], elements: List[Element] = []) -> None: #Determine if this is a new miner or not. miner: Union[PrivateKey, int] self.miners.append(self.miners[-1]) if nick > self.miners[-1]: raise GenerationError( "Told to mine a Block with a miner nick which doesn't exist.") if nick == self.miners[-1]: miner = PrivateKey(nick) self.miners[-1] += 1 else: miner = nick timeBase: int if len(self.blocks) == 0: timeBase = Blockchain().blocks[0].header.time else: timeBase = self.blocks[-1].time #Create and add the PrototypeBlock. self.blocks.append( PrototypeBlock( timeBase + self.timeOffset, #Create copies of the lists used as arguments to ensure we don't mutate the arguments. list(packets), list(elements), miner))
def createSend(rpc: RPC, last: Union[Claim, Send], toAddress: str) -> Send: funded: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32) if isinstance(last, Claim): send: Send = Send([(last.hash, 0)], [(decodeAddress(toAddress), 1), (funded.get_verifying_key(), last.amount - 1)]) else: send: Send = Send( [(last.hash, 1)], [(decodeAddress(toAddress), 1), (funded.get_verifying_key(), last.outputs[1][1] - 1)]) send.sign(funded) send.beat(SpamFilter(3)) sleep(65) rpc.meros.liveConnect(Blockchain().blocks[0].header.hash) if rpc.meros.liveTransaction(send) != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast back a Send.") sv: SignedVerification = SignedVerification(send.hash) sv.sign(0, PrivateKey(0)) if rpc.meros.signedElement(sv) != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast back a Verification.") return send
def VUnknownSignedTest(rpc: RPC) -> None: file: IO[Any] = open("e2e/Vectors/Merit/BlankBlocks.json", "r") chain: Blockchain = Blockchain.fromJSON(json.loads(file.read())) file.close() #Send a single block so we have a miner. rpc.meros.liveConnect(chain.blocks[0].header.hash) rpc.meros.syncConnect(chain.blocks[0].header.hash) header: bytes = rpc.meros.liveBlockHeader(chain.blocks[1].header) if MessageType(rpc.meros.sync.recv()[0]) != MessageType.BlockBodyRequest: raise TestError("Meros didn't ask for the body.") rpc.meros.blockBody(chain.blocks[1]) if rpc.meros.live.recv() != header: raise TestError("Meros didn't broadcast the header.") #Create a valid Data. #Uneccessary at this time, but good preparation for the future. privKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32) data: Data = Data(bytes(32), privKey.get_verifying_key().to_bytes()) data.sign(privKey) data.beat(SpamFilter(5)) #Sign the Data. verif: SignedVerification = SignedVerification(data.hash) verif.sign(0, PrivateKey(0)) #Run twice. The first shouldn't send the Transaction. The second should. for i in range(2): rpc.meros.signedElement(verif) if MessageType( rpc.meros.sync.recv()[0]) != MessageType.TransactionRequest: raise TestError("Meros didn't request the transaction.") if i == 0: #When we send DataMissing, we should be disconnected within a few seconds. rpc.meros.dataMissing() start: int = int(time()) try: rpc.meros.sync.recv() except Exception: #More than a few seconds is allowed as Meros's own SyncRequest must timeout. if int(time()) - start > 10: raise TestError( "Meros didn't disconnect us for sending a Verification of a non-existent Transaction." ) #Clear our invalid connections. rpc.meros.live.connection.close() rpc.meros.sync.connection.close() sleep(65) #Init new ones. rpc.meros.liveConnect(chain.blocks[0].header.hash) rpc.meros.syncConnect(chain.blocks[0].header.hash) else: rpc.meros.syncTransaction(data) sleep(2) if not rpc.call("consensus", "getStatus", [data.hash.hex()])["verifiers"]: raise TestError("Meros didn't add the Verification.")
def BLSTest() -> None: key: PrivateKey = PrivateKey(0) if not key.sign(b"abc").verify(AggregationInfo(key.toPublicKey(), b"abc")): raise TestError( "Couldn't sign and verify a BLS signature from Python.") if key.sign(b"abc").verify(AggregationInfo(key.toPublicKey(), b"def")): raise TestError( "Could verify a BLS signature with anything in Python.")
def signElement(elem: Element) -> Signature: if isinstance(elem, Verification): verif: SignedVerification = SignedVerification(elem.hash, elem.holder) verif.sign(elem.holder, PrivateKey(elem.holder)) return verif.signature if isinstance(elem, SendDifficulty): sendDiff: SignedSendDifficulty = SignedSendDifficulty( elem.difficulty, elem.nonce) sendDiff.sign(elem.holder, PrivateKey(elem.holder)) return sendDiff.signature if isinstance(elem, DataDifficulty): dataDiff: SignedDataDifficulty = SignedDataDifficulty( elem.difficulty, elem.nonce) dataDiff.sign(elem.holder, PrivateKey(elem.holder)) return dataDiff.signature raise GenerationError( "Tried to sign an Element in a Block we didn't recognize the type of.")
def mine(self, privKey: PrivateKey, difficulty: int) -> None: self.proof = -1 while ((self.proof == -1) or ((int.from_bytes(self.hash, "little") * difficulty) > int.from_bytes(bytes.fromhex("FF" * 32), "little"))): self.proof += 1 self.hash = RandomX(self.serializeHash()) self.signature = privKey.sign(self.hash).serialize() self.hash = RandomX(self.hash + self.signature)
def verify(rpc: RPC, txHash: bytes, nick: int = 0, mr: bool = False) -> None: sv: SignedVerification = SignedVerification(txHash) sv.sign(nick, PrivateKey(nick)) temp: bytes = rpc.meros.signedElement(sv) if mr: if MessageType( rpc.meros.live.recv()[0]) != MessageType.SignedMeritRemoval: raise TestError("Meros didn't create a MeritRemoval.") elif temp != rpc.meros.live.recv(): raise TestError("Meros didn't broadcast back a Verification.")
def HundredSixSignedElementsTest( rpc: RPC ) -> None: #Solely used to get the genesis Block hash. blockchain: Blockchain = Blockchain() edPrivKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32) blsPrivKey: PrivateKey = PrivateKey(0) sig: Signature = blsPrivKey.sign(bytes()) #Create a Data for the Verification. data: Data = Data(bytes(32), edPrivKey.get_verifying_key()) data.sign(edPrivKey) data.beat(SpamFilter(5)) #Create a signed Verification, SendDifficulty, and DataDifficulty. elements: List[SignedElement] = [ SignedVerification(data.hash, 1, sig), SignedSendDifficulty(0, 0, 1, sig), SignedDataDifficulty(0, 0, 1, sig) ] dataSent: bool = False for elem in elements: #Handshake with the node. rpc.meros.liveConnect(blockchain.blocks[0].header.hash) #Send the Data if we have yet to. if not dataSent: if rpc.meros.liveTransaction(data) != rpc.meros.live.recv(): raise TestError("Data wasn't rebroadcasted.") dataSent = True #Send the Element. rpc.meros.signedElement(elem) #Sleep for thirty seconds to make sure Meros realizes our connection is dead. sleep(30) #Verify the node didn't crash. try: if rpc.call("merit", "getHeight") != 1: raise Exception() except Exception: raise TestError("Node crashed after being sent a malformed Element.")
def syncUnknown() -> None: claim: Claim = Claim([(merit.mints[0], 0)], pubKey.to_bytes()) claim.sign(PrivateKey(0)) #Create a series of Sends, forming a diamond. #Cross sendB and sendC to actually force this to work in an ordered fashion to pass. sendA: Send = Send([(claim.hash, 0)], [(pubKey.to_bytes(), claim.amount // 2), (pubKey.to_bytes(), claim.amount // 2)]) sendB: Send = Send([(sendA.hash, 0)], [(pubKey.to_bytes(), sendA.outputs[0][1] // 2), (pubKey.to_bytes(), sendA.outputs[0][1] // 2)]) sendC: Send = Send( [(sendA.hash, 1), (sendB.hash, 1)], [(pubKey.to_bytes(), sendA.outputs[1][1] + sendB.outputs[1][1])]) sendD: Send = Send([(sendB.hash, 0), (sendC.hash, 0)], [(pubKey.to_bytes(), claim.amount)]) for send in [sendA, sendB, sendC, sendD]: send.sign(privKey) send.beat(sendFilter) #Send the tail of the diamond, which should cause an ordered top-down sync. sent: bytes = rpc.meros.liveTransaction(sendD) for tx in [ sendC, sendB, sendA, claim, sendA, claim, sendB, sendA, claim ]: if rpc.meros.sync.recv() != ( MessageType.TransactionRequest.toByte() + tx.hash): raise TestError("Meros didn't request one of the inputs.") rpc.meros.syncTransaction(tx) if rpc.meros.live.recv() != sent: raise TestError("Meros didn't broadcast the Send.") #Do the same for a few Data Transactions. datas: List[Data] = [Data(bytes(32), pubKey.to_bytes())] datas.append(Data(datas[-1].hash, bytes(1))) datas.append(Data(datas[-1].hash, bytes(1))) for data in datas: data.sign(privKey) data.beat(dataFilter)
import json from e2e.Libs.BLS import PrivateKey from e2e.Classes.Merit.Merit import Merit from e2e.Vectors.Generation.PrototypeChain import PrototypeBlock, PrototypeChain proto: PrototypeChain = PrototypeChain(1, False) merit: Merit = Merit.fromJSON(proto.finish().toJSON()) #Use up all of our Blocks with Merit, except the last one. for i in range(98): merit.add( PrototypeBlock(merit.blockchain.blocks[-1].header.time + 1200, minerID=(PrivateKey(1) if i == 0 else 1)).finish( 0, merit)) #Right before the end, move to pending. merit.add( PrototypeBlock(merit.blockchain.blocks[-1].header.time + 1200, minerID=1).finish(1, merit)) #Have our Merit die. merit.add( PrototypeBlock(merit.blockchain.blocks[-1].header.time + 1200, minerID=1).finish(0, merit)) #Regain Merit. merit.add( PrototypeBlock(merit.blockchain.blocks[-1].header.time + 1200,
from e2e.Vectors.Generation.PrototypeChain import PrototypeBlock, PrototypeChain edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32) edPubKey: bytes = edPrivKey.get_verifying_key().to_bytes() transactions: Transactions = Transactions() sendFilter: SpamFilter = SpamFilter(3) proto: PrototypeChain = PrototypeChain(40, keepUnlocked=True) proto.add(1) merit: Merit = Merit.fromJSON(proto.toJSON()) #Create a Claim. claim: Claim = Claim([(merit.mints[-1], 0)], edPubKey) claim.sign(PrivateKey(0)) transactions.add(claim) merit.add( PrototypeBlock(merit.blockchain.blocks[-1].header.time + 1200, packets=[VerificationPacket(claim.hash, list(range(2))) ]).finish(0, merit)) sends: List[Send] = [ #Transaction which will win. Send([(claim.hash, 0)], [(bytes(32), claim.amount)]), #Transaction which will be beaten. Send([(claim.hash, 0)], [(edPubKey, claim.amount // 2), (edPubKey, claim.amount // 2)]) ] #Children. One which will have a Verification, one which won't.
def PartialArchiveTest(rpc: RPC) -> None: vectors: Dict[str, Any] with open("e2e/Vectors/Consensus/Verification/PartialArchive.json", "r") as file: vectors = json.loads(file.read()) data: Data = Data.fromJSON(vectors["data"]) svs: List[SignedVerification] = [ SignedVerification.fromSignedJSON(vectors["verifs"][0]), SignedVerification.fromSignedJSON(vectors["verifs"][1]) ] key: PrivateKey = PrivateKey( bytes.fromhex(rpc.call("personal", "getMiner"))) def sendDataAndVerifications() -> None: if rpc.meros.liveTransaction(data) != rpc.meros.live.recv(): raise TestError( "Meros didn't rebroadcast a Transaction we sent it.") for sv in svs: if rpc.meros.signedElement(sv) != rpc.meros.live.recv(): raise TestError( "Meros didn't rebroadcast a SignedVerification we sent it." ) #As we don't have a quality RPC route for this, we need to use getTemplate. if bytes.fromhex( rpc.call("merit", "getBlockTemplate", [key.toPublicKey().serialize().hex() ])["header"])[36:68] != BlockHeader.createContents( [VerificationPacket(data.hash, [0, 1])]): raise TestError( "New Block template doesn't have a properly created packet.") def verifyRecreation() -> None: template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate", [key.toPublicKey().serialize().hex()]) if bytes.fromhex( template["header"])[36:68] != BlockHeader.createContents( [VerificationPacket(data.hash, [1])]): raise TestError( "New Block template doesn't have a properly recreated packet.") #Mining it further verifies the internal state. header: bytes = bytes.fromhex(template["header"]) proof: int = 0 sig: bytes while True: initial: bytes = RandomX(header + proof.to_bytes(4, byteorder="little")) sig = key.sign(initial).serialize() final: bytes = RandomX(initial + sig) if (int.from_bytes(final, "little") * template["difficulty"]) < int.from_bytes( bytes.fromhex("FF" * 32), "little"): break proof += 1 rpc.call("merit", "publishBlock", [ template["id"], (header + proof.to_bytes(4, byteorder="little") + sig).hex() ]) raise SuccessError( "Stop Liver from trying to verify the vector chain which doesn't have this Block." ) #We may not want to use Liver here. #There's a very small Block count and we can't let it terminate (hence the SE). with raises(SuccessError): Liver(rpc, vectors["blockchain"], callbacks={ 2: sendDataAndVerifications, 3: verifyRecreation }).live()
def finish(self, keepUnlocked: int, existing: Merit) -> Block: genesis: bytes = existing.blockchain.genesis prev: BlockHeader = existing.blockchain.blocks[-1].header diff: int = existing.blockchain.difficulty() #Create the signatures for every packet/element. signatures: List[Signature] = [] for packet in self.packets: for holder in packet.holders: verif: SignedVerification = SignedVerification( packet.hash, holder) verif.sign(holder, PrivateKey(holder)) signatures.append(verif.signature) for element in self.elements: signatures.append(signElement(element)) #Only add the Data Verification if: #1) We're supposed to make sure Merit Holders are always Unlocked #2) The last Block created a Data #3) The Merit Holder has Merit. if (keepUnlocked != 0) and (prev.last != genesis): #Create the Data from the last Block. blockData: Data = Data(genesis, prev.hash) #Create Verifications for said Data with every Private Key. #Ensures no one has their Merit locked. #pylint: disable=unnecessary-comprehension self.packets.append(VerificationPacket(blockData.hash, [])) for i in range(keepUnlocked): if ( #Miners who are just being created don't have Merit. ((i == (keepUnlocked - 1)) and (isinstance(self.minerID, PrivateKey))) or (existing.state.balances[i] == 0)): continue self.packets[-1].holders.append(i) verif: SignedVerification = SignedVerification( blockData.hash, i) verif.sign(i, PrivateKey(i)) signatures.append(verif.signature) #Remove this packet if there's no holders. if not self.packets[-1].holders: del self.packets[-1] #Set the aggregate. aggregate = Signature.aggregate(signatures) #Create the actual Block. minerID: Union[bytes, int] = 0 if isinstance(self.minerID, int): minerID = self.minerID else: minerID = self.minerID.toPublicKey().serialize() result: Block = Block( BlockHeader( 0, prev.hash, BlockHeader.createContents(self.packets, self.elements), len(self.packets), bytes(4), BlockHeader.createSketchCheck(bytes(4), self.packets), minerID, self.time), BlockBody(self.packets, self.elements, aggregate)) if isinstance(self.minerID, int): result.mine(PrivateKey(self.minerID), diff) else: result.mine(self.minerID, diff) return result
def TwoHundredThirtyFiveTest(rpc: RPC) -> None: blockchain: Blockchain = Blockchain() dataFilter: SpamFilter = SpamFilter(5) edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32) edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key() #Mine one Block to the node. blsPrivKey: PrivateKey = PrivateKey( bytes.fromhex(rpc.call("personal", "getMiner"))) blsPubKey: bytes = blsPrivKey.toPublicKey().serialize() #Call getBlockTemplate just to get an ID. #Skips the need to write a sync loop for the BlockBody. template: Dict[str, Any] = rpc.call("merit", "getBlockTemplate", [blsPubKey.hex()]) #Mine a Block. block = Block( BlockHeader(0, blockchain.blocks[0].header.hash, bytes(32), 1, bytes(4), bytes(32), blsPubKey, blockchain.blocks[0].header.time + 1200, 0), BlockBody()) block.mine(blsPrivKey, blockchain.difficulty()) blockchain.add(block) rpc.call("merit", "publishBlock", [template["id"], block.serialize().hex()]) #Send Meros a Data and receive its Verification to make sure it's verifying Transactions in the first place. data: Data = Data(bytes(32), edPubKey.to_bytes()) data.sign(edPrivKey) data.beat(dataFilter) rpc.meros.liveConnect(blockchain.blocks[0].header.hash) if rpc.meros.liveTransaction(data) != rpc.meros.live.recv(): raise TestError("Meros didn't send back the Data.") if MessageType(rpc.meros.live.recv()[0]) != MessageType.SignedVerification: raise TestError("Meros didn't send us its SignedVerification.") #Close our connection and mine 8 Blocks so its Merit is locked. rpc.meros.live.connection.close() for _ in range(8): block = Block( BlockHeader(0, blockchain.blocks[-1].header.hash, bytes(32), 1, bytes(4), bytes(32), 0, blockchain.blocks[-1].header.time + 1200, 0), BlockBody()) #Reusing its key is fine as mining doesn't count as participation. block.mine(blsPrivKey, blockchain.difficulty()) blockchain.add(block) #Sleep 30 seconds to make sure Meros noted we disconnected, and then reconnect. sleep(30) rpc.meros.liveConnect(blockchain.blocks[0].header.hash) rpc.meros.syncConnect(blockchain.blocks[0].header.hash) #Sync the Blocks. for b in range(8): header: bytes = rpc.meros.liveBlockHeader(blockchain.blocks[b + 2].header) if rpc.meros.sync.recv() != (MessageType.BlockBodyRequest.toByte() + blockchain.blocks[b + 2].header.hash): raise TestError("Meros didn't request the BlockBody.") rpc.meros.blockBody(blockchain.blocks[b + 2]) if rpc.meros.live.recv() != header: raise TestError("Meros didn't send back the header.") if MessageType( rpc.meros.live.recv()[0]) != MessageType.SignedVerification: raise TestError("Meros didn't verify this Block's data.") #Verify its Merit is locked. #Theoretically, all code after this check is unecessary. #Meros verifies a Block's Data after updating its State. #Therefore, if the above last Block had its Data verified, this issue should be closed. #That said, the timing is a bit too tight for comfort. #Better safe than sorry. Hence why the code after this check exists. if rpc.call("merit", "getMerit", [0])["status"] != "Locked": raise TestError("Merit wasn't locked when it was supposed to be.") #Send it a Transaction and make sure Meros verifies it, despite having its Merit locked. data = Data(data.hash, edPubKey.to_bytes()) data.sign(edPrivKey) data.beat(dataFilter) if rpc.meros.liveTransaction(data) != rpc.meros.live.recv(): raise TestError("Meros didn't send back the Data.") if MessageType(rpc.meros.live.recv()[0]) != MessageType.SignedVerification: raise TestError("Meros didn't send us its SignedVerification.")
#Transactions. transactions: Transactions = Transactions() #Merit. merit: Merit = Merit() #SpamFilter. dataFilter: SpamFilter = SpamFilter(5) #Ed25519 keys. edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32) edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key() #BLS keys. blsPrivKeys: List[PrivateKey] = [ PrivateKey(blake2b(b'\0', digest_size=32).digest()), PrivateKey(blake2b(b'\1', digest_size=32).digest()) ] blsPubKeys: List[PublicKey] = [ blsPrivKeys[0].toPublicKey(), blsPrivKeys[1].toPublicKey() ] #Add 4 Blank Blocks. for i in range(4): merit.add(Block.fromJSON(blankBlocks[i])) #Add a 5th Block to another verifier. block: Block = Block( BlockHeader(0, merit.blockchain.last(), bytes(32), 1, bytes(4), bytes(32), blsPubKeys[1].serialize(), merit.blockchain.blocks[-1].header.time + 1200), BlockBody())
from typing import Dict, List, IO, Any import json from e2e.Libs.BLS import PrivateKey, PublicKey from e2e.Classes.Consensus.DataDifficulty import SignedDataDifficulty from e2e.Classes.Consensus.MeritRemoval import SignedMeritRemoval from e2e.Vectors.Generation.PrototypeChain import PrototypeChain blsPrivKey: PrivateKey = PrivateKey(0) blsPubKey: PublicKey = blsPrivKey.toPublicKey() proto: PrototypeChain = PrototypeChain(1, False) #Create conflicting Data Difficulties. dataDiffs: List[SignedDataDifficulty] = [ SignedDataDifficulty(3, 0), SignedDataDifficulty(4, 0) ] dataDiffs[0].sign(0, blsPrivKey) dataDiffs[1].sign(0, blsPrivKey) #Create a MeritRemoval out of the conflicting Data Difficulties. mr: SignedMeritRemoval = SignedMeritRemoval(dataDiffs[0], dataDiffs[1]) proto.add(elements=[mr]) #Create a MeritRemoval with the Elements swapped. swapped: SignedMeritRemoval = SignedMeritRemoval(dataDiffs[1], dataDiffs[0]) proto.add(elements=[swapped])
def verify(rpc: RPC, tx: bytes) -> None: sv: SignedVerification = SignedVerification(tx) sv.sign(0, PrivateKey(0)) if rpc.meros.signedElement(sv) != rpc.meros.live.recv(): raise TestError("Meros didn't send back a Verification.")
def test() -> None: #Send to the first address from outside the Wallet. First address is now funded. sendHash: bytes = createSend( rpc, claims[0], decodeAddress(rpc.call("personal", "getAddress"))) #Send to the second address with all of the funds. Second address is now funded. #Tests send's minimal case (single input, no change). nextAddr: str = rpc.call("personal", "getAddress") sends: List[str] = [ rpc.call( "personal", "send", { "outputs": [{ "address": nextAddr, "amount": str(claims[0].amount) }] }) ] checkSend( rpc, sends[-1], { "inputs": [{ "hash": sendHash.hex().upper(), "nonce": 0 }], "outputs": [{ "key": decodeAddress(nextAddr).hex().upper(), "amount": str(claims[0].amount) }] }) verify(rpc, bytes.fromhex(sends[-1])) #Send to the third address with some of the funds. Third and change addresses are now funded. #Tests send's capability to create a change output. mnemonic: str = rpc.call("personal", "getMnemonic") nextAddr = rpc.call("personal", "getAddress") sends.append( rpc.call( "personal", "send", { "outputs": [{ "address": nextAddr, "amount": str(claims[0].amount - 1) }] })) checkSend( rpc, sends[-1], { "inputs": [{ "hash": sends[-2], "nonce": 0 }], "outputs": [{ "key": decodeAddress(nextAddr).hex().upper(), "amount": str(claims[0].amount - 1) }, { "key": getChangePublicKey(mnemonic, "", 0).hex().upper(), "amount": "1" }] }) verify(rpc, bytes.fromhex(sends[-1])) #Send all funds out of Wallet. #Tests MuSig signing and change UTXO detection. privKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32) pubKey: bytes = privKey.get_verifying_key() sends.append( rpc.call( "personal", "send", { "outputs": [{ "address": bech32_encode("mr", convertbits(bytes([0]) + pubKey, 8, 5)), "amount": str(claims[0].amount) }] })) checkSend( rpc, sends[-1], { "inputs": [{ "hash": sends[-2], "nonce": 0 }, { "hash": sends[-2], "nonce": 1 }], "outputs": [{ "key": pubKey.hex().upper(), "amount": str(claims[0].amount) }] }) verify(rpc, bytes.fromhex(sends[-1])) #Clear Wallet. Set a password this time around to make sure the password is properly carried. #Send two instances of funds to the first address. rpc.call("personal", "setWallet", {"password": "******"}) mnemonic = rpc.call("personal", "getMnemonic") nodeKey: bytes = decodeAddress(rpc.call("personal", "getAddress")) send: Send = Send([(bytes.fromhex(sends[-1]), 0)], [(nodeKey, claims[0].amount // 2), (nodeKey, claims[0].amount // 2)]) send.sign(Ristretto.SigningKey(b'\0' * 32)) send.beat(SpamFilter(3)) if rpc.meros.liveTransaction(send) != rpc.meros.live.recv(): raise TestError("Meros didn't send back a Send.") verify(rpc, send.hash) sends = [send.hash.hex().upper()] #Send to self. #Tests send's capability to handle multiple UTXOs per key/lack of aggregation when all keys are the same/multiple output Sends. nextAddr = rpc.call("personal", "getAddress") changeKey: bytes = getChangePublicKey(mnemonic, "test", 0) sends.append( rpc.call( "personal", "send", { "outputs": [{ "address": nextAddr, "amount": str(claims[0].amount - 1) }], "password": "******" })) checkSend( rpc, sends[-1], { "inputs": [{ "hash": sends[-2], "nonce": 0 }, { "hash": sends[-2], "nonce": 1 }], "outputs": [{ "key": decodeAddress(nextAddr).hex().upper(), "amount": str(claims[0].amount - 1) }, { "key": changeKey.hex().upper(), "amount": "1" }] }) verify(rpc, bytes.fromhex(sends[-1])) #Externally send to the second/change address. #Enables entering multiple instances of each key into MuSig, which is significant as we originally only used the unique keys. sends.append( createSend(rpc, claims[1], decodeAddress(nextAddr)).hex().upper()) sends.append(createSend(rpc, claims[2], changeKey).hex().upper()) #Check personal_getUTXOs. utxos: List[Dict[str, Any]] = [{ "hash": sends[-3], "nonce": 0, "address": nextAddr }, { "hash": sends[-3], "nonce": 1, "address": bech32_encode("mr", convertbits(bytes([0]) + changeKey, 8, 5)) }, { "hash": sends[-2], "nonce": 0, "address": nextAddr }, { "hash": sends[-1], "nonce": 0, "address": bech32_encode("mr", convertbits(bytes([0]) + changeKey, 8, 5)) }] if sortUTXOs(rpc.call("personal", "getUTXOs")) != sortUTXOs(utxos): raise TestError("personal_getUTXOs was incorrect.") for utxo in utxos: del utxo["address"] #Send to any address with all funds minus one. #Test MuSig signing, multiple inputs per key on account chains, change output creation to the next change key... sends.append( rpc.call( "personal", "send", { "outputs": [{ "address": nextAddr, "amount": str(claims[0].amount + claims[1].amount + claims[2].amount - 1) }], "password": "******" })) checkSend( rpc, sends[-1], { "inputs": utxos, "outputs": [{ "key": decodeAddress(nextAddr).hex().upper(), "amount": str(claims[0].amount + claims[1].amount + claims[2].amount - 1) }, { "key": getChangePublicKey(mnemonic, "test", 1).hex().upper(), "amount": "1" }] }) verify(rpc, bytes.fromhex(sends[-1])) #Mine a Block so we can reboot the node without losing data. blsPrivKey: PrivateKey = PrivateKey( bytes.fromhex(rpc.call("personal", "getMeritHolderKey"))) for _ in range(6): template: Dict[str, Any] = rpc.call( "merit", "getBlockTemplate", {"miner": blsPrivKey.toPublicKey().serialize().hex()}) proof: int = -1 tempHash: bytes = bytes() tempSignature: bytes = bytes() while ((proof == -1) or ( (int.from_bytes(tempHash, "little") * template["difficulty"]) > int.from_bytes(bytes.fromhex("FF" * 32), "little"))): proof += 1 tempHash = RandomX( bytes.fromhex(template["header"]) + proof.to_bytes(4, "little")) tempSignature = blsPrivKey.sign(tempHash).serialize() tempHash = RandomX(tempHash + tempSignature) rpc.call( "merit", "publishBlock", { "id": template["id"], "header": template["header"] + proof.to_bytes(4, "little").hex() + tempSignature.hex() }) #Reboot the node and verify it still tracks the same change address. #Also reload the Wallet and verify it still tracks the same change address. #Really should be part of address discovery; we just have the opportunity right here. #Due to the timing of how the codebase was developed, and a personal frustration for how long this has taken... rpc.quit() sleep(3) rpc.meros = Meros(rpc.meros.db, rpc.meros.tcp, rpc.meros.rpc) if rpc.call("personal", "getTransactionTemplate", {"outputs": [{ "address": nextAddr, "amount": "1" }]})["outputs"][1]["key"] != getChangePublicKey( mnemonic, "test", 2).hex().upper(): raise TestError( "Rebooting the node caused the WalletDB to stop tracking the next change address." ) rpc.call("personal", "setAccount", rpc.call("personal", "getAccount")) if rpc.call("personal", "getTransactionTemplate", {"outputs": [{ "address": nextAddr, "amount": "1" }]})["outputs"][1]["key"] != getChangePublicKey( mnemonic, "test", 2).hex().upper(): raise TestError( "Reloading the Wallet caused the WalletDB to stop tracking the next change address." ) raise SuccessError()
merit: Merit = PrototypeChain.withMint() transactions: Transactions = Transactions() sendFilter: SpamFilter = SpamFilter(3) edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32) edPubKeys: List[ed25519.VerifyingKey] = [ edPrivKey.get_verifying_key(), ed25519.SigningKey(b'\1' * 32).get_verifying_key() ] #Create the Claim. claim: Claim = Claim([(merit.mints[-1].hash, 0)], edPubKeys[0].to_bytes()) claim.amount = merit.mints[-1].outputs[0][1] claim.sign(PrivateKey(0)) transactions.add(claim) merit.add( PrototypeBlock(merit.blockchain.blocks[-1].header.time + 1200, packets=[VerificationPacket(claim.hash, [0])]).finish(0, merit)) #Give the second key pair Merit. merit.add( PrototypeBlock(merit.blockchain.blocks[-1].header.time + 1200, minerID=PrivateKey(1)).finish(0, merit)) #Create two competing Sends. packets: List[VerificationPacket] = [] for i in range(2): send: Send = Send(
edPrivKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32) edPubKey: bytes = edPrivKey.get_verifying_key() proto: PrototypeChain = PrototypeChain(40, keepUnlocked=True) proto.add(1) datas: List[Data] = [Data(bytes(32), edPubKey)] for d in range(2): datas.append(Data(datas[0].hash, d.to_bytes(1, "little"))) for data in datas: data.sign(edPrivKey) data.beat(dataFilter) verif: SignedVerification = SignedVerification(datas[1].hash) verif.sign(0, PrivateKey(0)) proto.add( packets=[ VerificationPacket(datas[0].hash, [0]), VerificationPacket(datas[2].hash, [1]) ] ) for _ in range(5): proto.add() with open("e2e/Vectors/Consensus/Families/UnmentionedBeatMentioned.json", "w") as vectors: vectors.write(json.dumps({ "blockchain": proto.toJSON(), "datas": [data.toJSON() for data in datas], "verification": verif.toSignedJSON()
edPrivKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32) edPubKey: bytes = edPrivKey.get_verifying_key() transactions: Transactions = Transactions() spamFilter: SpamFilter = SpamFilter(5) proto = PrototypeChain(1, False) proto.add(1) data: Data = Data(bytes(32), edPubKey) data.sign(edPrivKey) data.beat(spamFilter) transactions.add(data) verif: SignedVerification = SignedVerification(data.hash) verif.sign(1, PrivateKey(1)) proto.add(1, packets=[VerificationPacket(data.hash, [0])]) for _ in range(5): proto.add(1) with open("e2e/Vectors/Consensus/Verification/HundredFortyTwo.json", "w") as vectors: vectors.write( json.dumps({ "blockchain": proto.toJSON(), "transactions": transactions.toJSON(), "verification": verif.toSignedJSON(), "transaction": data.hash.hex().upper() }))
def sign(self, privKey: PrivateKey) -> None: self.signature = privKey.sign(self.hash).serialize()
def StringBasedTypesTest( rpc: RPC ) -> None: edPrivKey: Ristretto.SigningKey = Ristretto.SigningKey(b'\0' * 32) edPubKey: bytes = edPrivKey.get_verifying_key() blsPubKey: PublicKey = PrivateKey(0).toPublicKey() #hex. #Test 0x-prefixed and no-0x both work without issue. data: Data = Data(bytes(32), edPubKey) data.sign(edPrivKey) rpc.call("transactions", "publishTransactionWithoutWork", {"type": "Data", "transaction": "0x" + data.serialize()[:-4].hex()}) data = Data(data.hash, b"abc") data.sign(edPrivKey) rpc.call("transactions", "publishTransactionWithoutWork", {"type": "Data", "transaction": data.serialize()[:-4].hex()}) #Test non-hex data is properly handled. try: rpc.call("transactions", "publishTransactionWithoutWork", {"type": "Data", "transaction": "az"}) raise TestError() except Exception as e: if str(e) != "-32602 Invalid params.": raise TestError("Meros accepted non-hex data for a hex argument.") #Hash. rpc.call("transactions", "getTransaction", {"hash": data.hash.hex()}) rpc.call("transactions", "getTransaction", {"hash": "0x" + data.hash.hex()}) #Also call the upper form, supplementing the above hex tests (as Hash routes through hex). rpc.call("transactions", "getTransaction", {"hash": data.hash.hex().upper()}) rpc.call("transactions", "getTransaction", {"hash": "0x" + data.hash.hex().upper()}) #Improper length. try: rpc.call("transactions", "getTransaction", {"hash": data.hash.hex().upper()[:-2]}) raise TestError() except Exception as e: if str(e) != "-32602 Invalid params.": raise TestError("Meros accepted a hex string with an improper length as a Hash.") #BLSPublicKey. try: rpc.call("merit", "getNickname", {"key": blsPubKey.serialize().hex()}) raise TestError() except Exception as e: if str(e) != "-2 Key doesn't have a nickname assigned.": raise TestError("Meros didn't accept a valid BLSPublicKey.") try: rpc.call("merit", "getNickname", {"key": blsPubKey.serialize().hex()[:-2]}) raise TestError() except Exception as e: if str(e) != "-32602 Invalid params.": raise TestError("Meros accepted a hex string with an improper length as a BLSPublicKey.") #Missing flags. try: rpc.call("merit", "getNickname", {"key": "0" + blsPubKey.serialize().hex()[1]}) raise TestError() except Exception as e: if str(e) != "-32602 Invalid params.": raise TestError("Meros accepted an invalid BLSPublicKey as a BLSPublicKey.") #EdPublicKey. rpc.call("personal", "setAccount", {"key": edPubKey.hex(), "chainCode": bytes(32).hex()}) try: rpc.call("personal", "setAccount", {"key": edPubKey[:-2].hex(), "chainCode": bytes(32).hex()}) raise TestError() except Exception as e: if str(e) != "-32602 Invalid params.": raise TestError("Meros accepted a hex string with an improper length as an EdPublicKey.")
bbFile.close() #Transactions. transactions: Transactions = Transactions() #Merit. merit: Merit = Merit() #SpamFilter. dataFilter: SpamFilter = SpamFilter(5) #Ed25519 keys. edPrivKey: ed25519.SigningKey = ed25519.SigningKey(b'\0' * 32) edPubKey: ed25519.VerifyingKey = edPrivKey.get_verifying_key() #BLS keys. blsPrivKey: PrivateKey = PrivateKey(blake2b(b'\0', digest_size=32).digest()) blsPubKey: PublicKey = blsPrivKey.toPublicKey() #Add 1 Blank Block. for i in range(1): merit.add(Block.fromJSON(blankBlocks[i])) #Create the Data and a successor. first: Data = Data(bytes(32), edPubKey.to_bytes()) first.sign(edPrivKey) first.beat(dataFilter) transactions.add(first) second: Data = Data(first.hash, bytes(1)) second.sign(edPrivKey) second.beat(dataFilter)
from e2e.Classes.Merit.Merit import Merit from e2e.Vectors.Generation.PrototypeChain import PrototypeBlock, PrototypeChain edPubKey: bytes = ed25519.SigningKey(b'\0' * 32).get_verifying_key().to_bytes() proto: PrototypeChain = PrototypeChain(49, keepUnlocked=True) merit: Merit = Merit.fromJSON(proto.toJSON()) transactions: Transactions = Transactions() #Create the Claims. for m in range(3): claim: Claim = Claim([(merit.mints[m], 0)], edPubKey) claim.sign(PrivateKey(0)) transactions.add(claim) merit.add( PrototypeBlock(merit.blockchain.blocks[-1].header.time + 1200, packets=[ VerificationPacket(tx.hash, [0]) for tx in transactions.txs.values() ]).finish(0, merit)) with open("e2e/Vectors/RPC/Personal/WatchWallet.json", "w") as vectors: vectors.write( json.dumps({ "blockchain": merit.toJSON(), "transactions": transactions.toJSON() }))