def makeT1AndT2(self): fee = 10000 #0.1 mBTC (TODO: make configurable) if self.amountLocal <= 2*fee: #both deposit and withdraw fees raise Exception("The deposited amount needs to be more than the transaction fees") ownPubKey = self.ownKey.getPublicKey() peerPubKey = self.peerKey.getPublicKey() returnKeyHash = crypto.RIPEMD160(crypto.SHA256(ownPubKey)) #We send an extra fee here, so that T2's initial return becomes # exactly self.amountLocal assert self.hasFirstPublicKey self.T1 = sendToMultiSigPubKey(self.bitcoind, self.amountLocal + fee, ownPubKey, peerPubKey, returnKeyHash, fee) T1_ID = self.T1.getTransactionID() #opposite endianness as in Bitcoind #TODO: set lock time!!! lockTime = 0 #The amount argument is the amount in the INPUT of T2, so it's again #with an added fee, to make the OUTPUT equal to self.amountLocal. self.T2_latest = MultiSigTransaction.makeNew( self.ownKey, T1_ID, self.amountLocal + fee, fee, lockTime)
def __init__(self, bitcoind, state): channel.Channel.__init__(self, state) self.bitcoind = bitcoind self.stage = stages[state["stage"]] #name -> integer self.ownAddress = str(state["ownAddress"]) ownPrivateKey = base58.decodeBase58Check( self.bitcoind.getPrivateKey(self.ownAddress), 128 ) self.ownKey = crypto.Key() self.ownKey.setPrivateKey(ownPrivateKey) self.peerKey = None if "peerPublicKey" in state: self.peerKey = crypto.Key() self.peerKey.setPublicKey( binascii.unhexlify(state["peerPublicKey"]) ) self.escrowKey = None if "escrowPublicKey" in state: self.escrowKey = crypto.Key() self.escrowKey.setPublicKey( binascii.unhexlify(state["escrowPublicKey"]) ) self.hasFirstPublicKey = bool(state["hasFirstPublicKey"]) self.T1 = None self.T2_peerSigned = None self.T2_latest = None self.peerSignature = None if "T1" in state: self.T1 = bitcointransaction.Transaction.deserialize( binascii.unhexlify(state["T1"]) ) if "T2_peerSigned" in state: self.T2_peerSigned = MultiSigTransaction.makeFromState( state["T2_peerSigned"]) if "T2_latest" in state: self.T2_latest = MultiSigTransaction.makeFromState( state["T2_latest"]) if "peerSignature" in state: self.peerSignature = binascii.unhexlify(state["peerSignature"])
def processTransactionPayload(self, payload): peerSignature = payload[0] T2 = MultiSigTransaction.deserialize(payload[1]) pubKey1, pubKey2 = self.getPublicKeyPair() if not verifyMultiSigSignature( T2.transaction, 0, [pubKey1, pubKey2], self.peerKey, peerSignature): raise Exception("Signature failure!") #TODO: what to do now? #TODO: lots of checks on T2 (IMPORTANT!) self.peerSignature = peerSignature self.T2_latest = T2 self.T2_peerSigned = copy.deepcopy(T2)
def makeWithdrawMessage(self, message): self.checkT1() #TODO: make some code to enter the "stopping" state prior to withdrawing, #and then wait until there are no more ongoing transactions. #This is not necessary for low-volume tests in controlled environments, #but as soon as people start transferring other peoples' transactions, #you want to be able to stop other peoples' transactions from #interfering with your withdrawal. if self.stage == stages["Ready"]: self.stage = stages["Stopping"] self.checkStopped() if message == None: if self.stage != stages["Stopped"]: raise Exception("Can not withdraw: channel must be Stopped, but is " + \ stageNames[self.stage]) self.makeWithdrawT2() self.stage = stages["OwnWithdraw_SendingT2"] return messages.Withdraw( self.ID, stage=self.stage, payload=[self.T2_latest.serialize()]) elif self.stage == stages["Stopped"] and \ message.stage == stages["OwnWithdraw_SendingT2"]: #Received T2 self.T2_latest = MultiSigTransaction.deserialize(message.payload[0]) #TODO: maybe re-serialize to check consistency #TODO: lots of checks on T2 (IMPORTANT!) pubKey1, pubKey2 = self.getPublicKeyPair() signature = signMultiSigTransaction( self.T2_latest.transaction, 0, [pubKey1, pubKey2], self.ownKey) self.stage = stages["PeerWithdraw_SendingSignature"] return messages.Withdraw( self.ID, stage=self.stage, payload=[signature]) elif self.stage == stages["OwnWithdraw_SendingT2"] and \ message.stage == stages["PeerWithdraw_SendingSignature"]: peerSignature = message.payload[0] pubKey1, pubKey2 = self.getPublicKeyPair() if not verifyMultiSigSignature( self.T2_latest.transaction, 0, [pubKey1, pubKey2], self.peerKey, peerSignature): raise Exception("Signature failure!") #TODO: what to do now? ownSignature = signMultiSigTransaction( self.T2_latest.transaction, 0, [pubKey1, pubKey2], self.ownKey) if self.hasFirstPublicKey: applyMultiSigSignatures(self.T2_latest.transaction, ownSignature, peerSignature) else: applyMultiSigSignatures(self.T2_latest.transaction, peerSignature, ownSignature) self.peerSignature = peerSignature self.T2_peerSigned = copy.deepcopy(self.T2_latest) self.stage = stages["Closed"] #Publish T2 in Bitcoind T2_serialized = self.T2_latest.transaction.serialize() self.bitcoind.sendRawTransaction(T2_serialized) return messages.Withdraw( self.ID, stage=self.stage, payload=[T2_serialized]) elif self.stage == stages["PeerWithdraw_SendingSignature"] and \ message.stage == stages["Closed"]: T2 = bitcointransaction.Transaction.deserialize(message.payload[0]) #TODO: maybe re-serialize to check consistency #TODO: lots of checks on T2 (IMPORTANT!) #TODO: #self.T2_latest = T2 #self.peerSignature = TODO #self.T2_peerSigned = copy.deepcopy(T2) self.stage = stages["Closed"] #Publish T2 in Bitcoind T2_serialized = message.payload[0] self.bitcoind.sendRawTransaction(T2_serialized) print "DONE" else: raise Exception("Received illegal withdraw message") return None
def makeDepositMessage(self, message): if self.stage == stages["OwnDeposit_Initial"] and \ message == None: #Initial message: self.stage = stages["OwnDeposit_SendingPublicKey"] return messages.Deposit( self.ID, self.getType(), isInitial=True, stage=self.stage, payload=[self.ownKey.getPublicKey(), self.escrowKey.getPublicKey()]) elif self.stage == stages["PeerDeposit_Initial"] and \ message.stage == stages["OwnDeposit_SendingPublicKey"]: #Received deposit message with public key from peer self.peerKey = crypto.Key() self.peerKey.setPublicKey(message.payload[0]) self.escrowKey = crypto.Key() self.escrowKey.setPublicKey(message.payload[1]) #TODO: check whether the escrow key is accepted self.stage = stages["PeerDeposit_SendingPublicKey"] return messages.Deposit( self.ID, self.getType(), stage=self.stage, payload=[self.ownKey.getPublicKey()]) elif self.stage == stages["OwnDeposit_SendingPublicKey"] and \ message.stage == stages["PeerDeposit_SendingPublicKey"]: #Received reply on own deposit message self.peerKey = crypto.Key() self.peerKey.setPublicKey(message.payload[0]) self.makeT1AndT2() self.stage = stages["OwnDeposit_SendingT2"] return messages.Deposit( self.ID, self.getType(), stage=self.stage, payload=[self.T2_latest.serialize()]) elif self.stage == stages["PeerDeposit_SendingPublicKey"] and \ message.stage == stages["OwnDeposit_SendingT2"]: #Received T2 self.T2_latest = MultiSigTransaction.deserialize(message.payload[0]) #TODO: maybe re-serialize to check consistency self.amountRemote = sum(tx.amount for tx in self.T2_latest.transaction.tx_out) assert not self.hasFirstPublicKey signature = signMultiSigTransaction( self.T2_latest.transaction, 0, [self.peerKey.getPublicKey(), self.ownKey.getPublicKey()], self.ownKey) self.stage = stages["PeerDeposit_SendingSignature"] return messages.Deposit( self.ID, self.getType(), stage=self.stage, payload=[signature]) elif self.stage == stages["OwnDeposit_SendingT2"] and \ message.stage == stages["PeerDeposit_SendingSignature"]: signature = message.payload[0] assert self.hasFirstPublicKey if not verifyMultiSigSignature( self.T2_latest.transaction, 0, [self.ownKey.getPublicKey(), self.peerKey.getPublicKey()], self.peerKey, signature): raise Exception("Signature failure!") #TODO: what to do now? self.peerSignature = signature self.T2_peerSigned = copy.deepcopy(self.T2_latest) T1_serialized = self.T1.serialize() self.stage = stages["WaitingForT1"] #Publish T1 in Bitcoind self.bitcoind.sendRawTransaction(T1_serialized) return messages.Deposit( self.ID, self.getType(), stage=self.stage, payload=[T1_serialized]) elif self.stage == stages["PeerDeposit_SendingSignature"] and \ message.stage == stages["WaitingForT1"]: T1_serialized = message.payload[0] self.T1 = bitcointransaction.Transaction.deserialize(T1_serialized) #TODO: maybe re-serialize to check consistency #TODO: check T1 (and that it matches T2) self.stage = stages["WaitingForT1"] #Publish T1 in Bitcoind self.bitcoind.sendRawTransaction(T1_serialized) print "DONE" else: raise Exception("Received illegal deposit message") return None