From 1fb87a3f254be0f7c9de4b614e1a20dfe86778ee Mon Sep 17 00:00:00 2001 From: infografx Date: Sun, 16 Jun 2019 21:43:56 +0300 Subject: [PATCH 01/24] fix fee on edges --- lnutil/dlclib.go | 49 +++++++++++++++++++++-------- qln/dlc.go | 82 +++++++++++++++++++++++++++++------------------- 2 files changed, 85 insertions(+), 46 deletions(-) diff --git a/lnutil/dlclib.go b/lnutil/dlclib.go index a45c6724a..9f2f0c936 100644 --- a/lnutil/dlclib.go +++ b/lnutil/dlclib.go @@ -452,26 +452,49 @@ func SettlementTx(c *DlcContract, d DlcContractDivision, feeOurs := feeEach feeTheirs := feeEach valueOurs := d.ValueOurs - // We don't have enough to pay for a fee. We get 0, our contract partner - // pays the rest of the fee - if valueOurs < feeEach { - feeOurs = valueOurs - valueOurs = 0 - } else { - valueOurs = d.ValueOurs - feeOurs - } + + // This code produces bugs so I disable it + + // // We don't have enough to pay for a fee. We get 0, our contract partner + // // pays the rest of the fee + // if valueOurs < feeEach { + // feeOurs = valueOurs + // valueOurs = 0 + // } else { + // valueOurs = d.ValueOurs - feeOurs + // } totalContractValue := c.TheirFundingAmount + c.OurFundingAmount valueTheirs := totalContractValue - d.ValueOurs - if valueTheirs < feeEach { - feeTheirs = valueTheirs - valueTheirs = 0 - feeOurs = totalFee - feeTheirs + + // This code produces bugs so I disable it + + // if valueTheirs < feeEach { + // feeTheirs = valueTheirs + // valueTheirs = 0 + // feeOurs = totalFee - feeTheirs + // valueOurs = d.ValueOurs - feeOurs + // } else { + // valueTheirs -= feeTheirs + // } + + + // New code to handle edges of a contract interval + + if valueOurs == 0 { + valueTheirs = valueTheirs - totalFee + }else{ valueOurs = d.ValueOurs - feeOurs - } else { + } + + if valueTheirs == 0 { + valueOurs = valueOurs - totalFee + }else{ valueTheirs -= feeTheirs } + + var buf bytes.Buffer binary.Write(&buf, binary.BigEndian, uint64(0)) binary.Write(&buf, binary.BigEndian, uint64(0)) diff --git a/qln/dlc.go b/qln/dlc.go index ab0cbd45e..bcec34451 100644 --- a/qln/dlc.go +++ b/qln/dlc.go @@ -609,46 +609,62 @@ func (nd *LitNode) SettleContract(cIdx uint64, oracleValue int64, oracleSig [32] return [32]byte{}, [32]byte{}, err } - // TODO: Claim the contract settlement output back to our wallet - otherwise the peer can claim it after locktime. - txClaim := wire.NewMsgTx() - txClaim.Version = 2 - settleOutpoint := wire.OutPoint{Hash: settleTx.TxHash(), Index: 0} - txClaim.AddTxIn(wire.NewTxIn(&settleOutpoint, nil, nil)) + if ( d.ValueOurs != 0){ - addr, err := wal.NewAdr() - txClaim.AddTxOut(wire.NewTxOut(d.ValueOurs-1000, lnutil.DirectWPKHScriptFromPKH(addr))) // todo calc fee - fee is double here because the contract output already had the fee deducted in the settlement TX + fee := int64(1000) - kg.Step[2] = UseContractPayoutBase - privSpend, _ := wal.GetPriv(kg) + if(c.OurFundingAmount + c.TheirFundingAmount == d.ValueOurs){ + fee += int64(1000) + } - pubSpend := wal.GetPub(kg) - privOracle, pubOracle := koblitz.PrivKeyFromBytes(koblitz.S256(), oracleSig[:]) - privContractOutput := lnutil.CombinePrivateKeys(privSpend, privOracle) + // TODO: Claim the contract settlement output back to our wallet - otherwise the peer can claim it after locktime. + txClaim := wire.NewMsgTx() + txClaim.Version = 2 - var pubOracleBytes [33]byte - copy(pubOracleBytes[:], pubOracle.SerializeCompressed()) - var pubSpendBytes [33]byte - copy(pubSpendBytes[:], pubSpend.SerializeCompressed()) + settleOutpoint := wire.OutPoint{Hash: settleTx.TxHash(), Index: 0} + txClaim.AddTxIn(wire.NewTxIn(&settleOutpoint, nil, nil)) - settleScript := lnutil.DlcCommitScript(c.OurPayoutBase, pubOracleBytes, c.TheirPayoutBase, 5) - err = nd.SignClaimTx(txClaim, settleTx.TxOut[0].Value, settleScript, privContractOutput, false) - if err != nil { - logging.Errorf("SettleContract SignClaimTx err %s", err.Error()) - return [32]byte{}, [32]byte{}, err - } + addr, err := wal.NewAdr() + txClaim.AddTxOut(wire.NewTxOut(d.ValueOurs-1000, lnutil.DirectWPKHScriptFromPKH(addr))) // todo calc fee - fee is double here because the contract output already had the fee deducted in the settlement TX - // Claim TX should be valid here, so publish it. - err = wal.DirectSendTx(txClaim) - if err != nil { - logging.Errorf("SettleContract DirectSendTx (claim) err %s", err.Error()) - return [32]byte{}, [32]byte{}, err - } + kg.Step[2] = UseContractPayoutBase + privSpend, _ := wal.GetPriv(kg) + + pubSpend := wal.GetPub(kg) + privOracle, pubOracle := koblitz.PrivKeyFromBytes(koblitz.S256(), oracleSig[:]) + privContractOutput := lnutil.CombinePrivateKeys(privSpend, privOracle) + + var pubOracleBytes [33]byte + copy(pubOracleBytes[:], pubOracle.SerializeCompressed()) + var pubSpendBytes [33]byte + copy(pubSpendBytes[:], pubSpend.SerializeCompressed()) + + settleScript := lnutil.DlcCommitScript(c.OurPayoutBase, pubOracleBytes, c.TheirPayoutBase, 5) + err = nd.SignClaimTx(txClaim, settleTx.TxOut[0].Value, settleScript, privContractOutput, false) + if err != nil { + logging.Errorf("SettleContract SignClaimTx err %s", err.Error()) + return [32]byte{}, [32]byte{}, err + } + + // Claim TX should be valid here, so publish it. + err = wal.DirectSendTx(txClaim) + if err != nil { + logging.Errorf("SettleContract DirectSendTx (claim) err %s", err.Error()) + return [32]byte{}, [32]byte{}, err + } + + c.Status = lnutil.ContractStatusClosed + err = nd.DlcManager.SaveContract(c) + if err != nil { + return [32]byte{}, [32]byte{}, err + } + return settleTx.TxHash(), txClaim.TxHash(), nil + + }else{ + + return settleTx.TxHash(), [32]byte{}, nil - c.Status = lnutil.ContractStatusClosed - err = nd.DlcManager.SaveContract(c) - if err != nil { - return [32]byte{}, [32]byte{}, err } - return settleTx.TxHash(), txClaim.TxHash(), nil + } From 584cb8b2224570f1612b16eff6f95fbbfbd3600b Mon Sep 17 00:00:00 2001 From: infografx Date: Fri, 19 Jul 2019 14:59:57 +0300 Subject: [PATCH 02/24] Merge branch 'arbitrary_message_size' into master-mit-lit-arb_mess_size --- litrpc/lndcrpcclient.go | 42 ++++++++++++-- lndc/conn.go | 46 +++++++++++++-- lnp2p/msgproc.go | 41 ++++++++++++++ lnp2p/peermgr.go | 20 +++++++ lnutil/msglib.go | 123 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 263 insertions(+), 9 deletions(-) diff --git a/litrpc/lndcrpcclient.go b/litrpc/lndcrpcclient.go index 7a508ee0b..b5372615d 100644 --- a/litrpc/lndcrpcclient.go +++ b/litrpc/lndcrpcclient.go @@ -28,9 +28,11 @@ type LndcRpcClient struct { requestNonce uint64 requestNonceMtx sync.Mutex responseChannelMtx sync.Mutex - responseChannels map[uint64]chan lnutil.RemoteControlRpcResponseMsg + responseChannels map[uint64]chan *lnutil.RemoteControlRpcResponseMsg key *koblitz.PrivateKey conMtx sync.Mutex + + chunksOfMsg map[int64]*lnutil.ChunkMsg } // LndcRpcCanConnectLocally checks if we can connect to lit using the normal @@ -116,9 +118,12 @@ func NewLndcRpcClient(address string, key *koblitz.PrivateKey) (*LndcRpcClient, var err error cli := new(LndcRpcClient) + + cli.chunksOfMsg = make(map[int64]*lnutil.ChunkMsg) + // Create a map of chan objects to receive returned responses on. These channels // are sent to from the ReceiveLoop, and awaited in the Call method. - cli.responseChannels = make(map[uint64]chan lnutil.RemoteControlRpcResponseMsg) + cli.responseChannels = make(map[uint64]chan *lnutil.RemoteControlRpcResponseMsg) //Parse the address we're connecting to who, where := lnutil.ParseAdrString(address) @@ -158,7 +163,7 @@ func (cli *LndcRpcClient) Call(serviceMethod string, args interface{}, reply int // Create the channel to receive the reply on cli.responseChannelMtx.Lock() - cli.responseChannels[nonce] = make(chan lnutil.RemoteControlRpcResponseMsg) + cli.responseChannels[nonce] = make(chan *lnutil.RemoteControlRpcResponseMsg) cli.responseChannelMtx.Unlock() // Send the message in a goroutine @@ -221,6 +226,35 @@ func (cli *LndcRpcClient) ReceiveLoop() { return } msg = msg[:n] + + if msg[0] == lnutil.MSGID_CHUNKS_BEGIN { + + beginChunksMsg, _ := lnutil.NewChunksBeginMsgFromBytes(msg, 0) + + msg_tmp := new(lnutil.ChunkMsg) + msg_tmp.TimeStamp = beginChunksMsg.TimeStamp + cli.chunksOfMsg[beginChunksMsg.TimeStamp] = msg_tmp + + continue + } + + if msg[0] == lnutil.MSGID_CHUNK_BODY { + + chunkMsg, _ := lnutil.NewChunkMsgFromBytes(msg, 0) + cli.chunksOfMsg[chunkMsg.TimeStamp].Data = append(cli.chunksOfMsg[chunkMsg.TimeStamp].Data, chunkMsg.Data...) + + continue + } + + if msg[0] == lnutil.MSGID_CHUNKS_END { + + endChunksMsg, _ := lnutil.NewChunksBeginMsgFromBytes(msg, 0) + msg = cli.chunksOfMsg[endChunksMsg.TimeStamp].Data + + } + + + // We only care about RPC responses (for now) if msg[0] == lnutil.MSGID_REMOTE_RPCRESPONSE { // Parse the received message @@ -239,7 +273,7 @@ func (cli *LndcRpcClient) ReceiveLoop() { // reply and therefore, it could have not blocked and just // ignore the return value. select { - case responseChan <- response: + case responseChan <- &response: default: } diff --git a/lndc/conn.go b/lndc/conn.go index 01c1f189c..afc786162 100644 --- a/lndc/conn.go +++ b/lndc/conn.go @@ -7,6 +7,7 @@ import ( "math" "net" "time" + "encoding/binary" "github.com/mit-dci/lit/crypto/koblitz" "github.com/mit-dci/lit/lnutil" @@ -151,27 +152,62 @@ func (c *Conn) Write(b []byte) (n int, err error) { // If we need to split the message into fragments, then we'll write // chunks which maximize usage of the available payload. - chunkSize := math.MaxUint16 + chunkSize := math.MaxUint16 - 1000 + + lenb := int(len(b)) + curts := (time.Now().UnixNano()) + + beginChunksMsgBodyBytes := new(bytes.Buffer) + beginChunksMsgBodyBytes.WriteByte(lnutil.MSGID_CHUNKS_BEGIN) + binary.Write(beginChunksMsgBodyBytes, binary.BigEndian, curts) + + // Start saving chunks + chunk_err := c.noise.WriteMessage(c.conn, beginChunksMsgBodyBytes.Bytes()) + if chunk_err != nil { + return 0, chunk_err + } - bytesToWrite := len(b) bytesWritten := 0 + bytesToWrite := lenb + for bytesWritten < bytesToWrite { // If we're on the last chunk, then truncate the chunk size as // necessary to avoid an out-of-bounds array memory access. if bytesWritten+chunkSize > len(b) { - chunkSize = len(b) - bytesWritten + chunkSize = lenb - bytesWritten } // Slice off the next chunk to be written based on our running // counter and next chunk size. chunk := b[bytesWritten : bytesWritten+chunkSize] - if err := c.noise.WriteMessage(c.conn, chunk); err != nil { - return bytesWritten, err + + // Wrap chunk in a MSGID_CHUNK_BODY message + chunkMsgBodyBytes := new(bytes.Buffer) + chunkMsgBodyBytes.WriteByte(lnutil.MSGID_CHUNK_BODY) + binary.Write(chunkMsgBodyBytes, binary.BigEndian, curts) + binary.Write(chunkMsgBodyBytes, binary.BigEndian, int32(chunkSize)) + chunkMsgBodyBytes.Write(chunk) + + + chunk_err = c.noise.WriteMessage(c.conn, chunkMsgBodyBytes.Bytes()) + if chunk_err != nil { + return 0, chunk_err } bytesWritten += len(chunk) } + // Actually send a message (unwrap and send) + endChunksMsgBodyBytes := new(bytes.Buffer) + endChunksMsgBodyBytes.WriteByte(lnutil.MSGID_CHUNKS_END) + binary.Write(endChunksMsgBodyBytes, binary.BigEndian, curts) + + chunk_err = c.noise.WriteMessage(c.conn, endChunksMsgBodyBytes.Bytes()) + if chunk_err != nil { + return 0, chunk_err + } + + return bytesWritten, nil } diff --git a/lnp2p/msgproc.go b/lnp2p/msgproc.go index 3897a9580..3df578fe8 100644 --- a/lnp2p/msgproc.go +++ b/lnp2p/msgproc.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/mit-dci/lit/logging" "sync" + "github.com/mit-dci/lit/lnutil" ) // ParseFuncType is the type of a Message parser function. @@ -28,6 +29,8 @@ type MessageProcessor struct { // TODO Evaluate if this mutex is even necessary? active bool actmtx *sync.Mutex + + ChunksOfMsg map[int64]*lnutil.ChunkMsg } // NewMessageProcessor processes messages coming in from over the network. @@ -36,6 +39,7 @@ func NewMessageProcessor() MessageProcessor { handlers: [256]*messagehandler{}, active: false, actmtx: &sync.Mutex{}, + ChunksOfMsg: make(map[int64]*lnutil.ChunkMsg), } } @@ -77,6 +81,43 @@ func (mp *MessageProcessor) HandleMessage(peer *Peer, buf []byte) error { // First see if we have handlers defined for this message type. mtype := buf[0] + + if mtype == 0xB2{ + + msg, _ := lnutil.NewChunksBeginMsgFromBytes(buf, peer.GetIdx()) + + chunk_msg := new(lnutil.ChunkMsg) + chunk_msg.TimeStamp = msg.TimeStamp + + mp.ChunksOfMsg[msg.TimeStamp] = chunk_msg + + return nil + + } + + if mtype == 0xB3{ + + msg, _ := lnutil.NewChunkMsgFromBytes(buf, peer.GetIdx()) + mp.ChunksOfMsg[msg.TimeStamp].Data = append(mp.ChunksOfMsg[msg.TimeStamp].Data, msg.Data...) + + return nil + + } + + if mtype == 0xB4{ + + msg, _ := lnutil.NewChunksEndMsgFromBytes(buf, peer.GetIdx()) + + buf = mp.ChunksOfMsg[msg.TimeStamp].Data + mtype = buf[0] + + delete(mp.ChunksOfMsg, msg.TimeStamp) + + } + + + + h := mp.handlers[mtype] if h == nil { return fmt.Errorf("no handler found for messasge of type %x", mtype) diff --git a/lnp2p/peermgr.go b/lnp2p/peermgr.go index f367ddd83..068d44fd6 100644 --- a/lnp2p/peermgr.go +++ b/lnp2p/peermgr.go @@ -7,6 +7,7 @@ import ( "net" "sync" "time" + "math" "github.com/mit-dci/lit/btcutil/hdkeychain" "github.com/mit-dci/lit/crypto/koblitz" @@ -87,6 +88,25 @@ func NewPeerManager(rootkey *hdkeychain.ExtendedKey, pdb lncore.LitPeerStorage, mtx: &sync.Mutex{}, } + + // Clear ChunksOfMsg in case of incomplete chunks transmittion. + // Try to clean the map every 5 minutes. Therefore message have to + // be transmitted within a 5 minutes. + go func(){ + + for { + + time.Sleep(5 * time.Minute) + + for k := range pm.mproc.ChunksOfMsg { + tdelta := time.Now().UnixNano() - k + if tdelta > 3*int64(math.Pow10(11)) { + delete(pm.mproc.ChunksOfMsg, k) + } + } + } + }() + return pm, nil } diff --git a/lnutil/msglib.go b/lnutil/msglib.go index ae4599aa2..f7d1b9fd3 100644 --- a/lnutil/msglib.go +++ b/lnutil/msglib.go @@ -73,6 +73,11 @@ const ( MSGID_REMOTE_RPCREQUEST = 0xB0 // Contains an RPC request from a remote peer MSGID_REMOTE_RPCRESPONSE = 0xB1 // Contains an RPC response to send to a remote peer + MSGID_CHUNKS_BEGIN uint8 = 0xB2 + MSGID_CHUNK_BODY uint8 = 0xB3 + MSGID_CHUNKS_END uint8 = 0xB4 + + DIGEST_TYPE_SHA256 = 0x00 DIGEST_TYPE_RIPEMD160 = 0x01 ) @@ -2412,3 +2417,121 @@ func (msg RemoteControlRpcResponseMsg) Peer() uint32 { func (msg RemoteControlRpcResponseMsg) MsgType() uint8 { return MSGID_REMOTE_RPCRESPONSE } + + +// For chunked messages + +type BeginChunksMsg struct { + PeerIdx uint32 + TimeStamp int64 +} + +func NewChunksBeginMsgFromBytes(b []byte, peerIdx uint32) (BeginChunksMsg, error) { + + msg := new(BeginChunksMsg) + buf := bytes.NewBuffer(b[1:]) // get rid of messageType + + msg.PeerIdx = peerIdx + binary.Read(buf, binary.BigEndian, &msg.TimeStamp) + + return *msg, nil +} + +func (msg BeginChunksMsg) Bytes() []byte { + var buf bytes.Buffer + + buf.WriteByte(msg.MsgType()) + binary.Write(&buf, binary.BigEndian, msg.TimeStamp) + + return buf.Bytes() +} + +func (msg BeginChunksMsg) Peer() uint32 { + return msg.PeerIdx +} + +func (msg BeginChunksMsg) MsgType() uint8 { + return MSGID_CHUNKS_BEGIN +} + + +type ChunkMsg struct { + PeerIdx uint32 + TimeStamp int64 + ChunkSize int32 + Data []byte +} + + +func NewChunkMsgFromBytes(b []byte, peerIdx uint32) (ChunkMsg, error) { + + msg := new(ChunkMsg) + + buf := bytes.NewBuffer(b[1:]) // get rid of messageType + + msg.PeerIdx = peerIdx + binary.Read(buf, binary.BigEndian, &msg.TimeStamp) + binary.Read(buf, binary.BigEndian, &msg.ChunkSize) + + msg.Data = make([]byte, msg.ChunkSize) + binary.Read(buf, binary.BigEndian, msg.Data) + + return *msg, nil + +} + + +func (msg ChunkMsg) Bytes() []byte { + var buf bytes.Buffer + + buf.WriteByte(msg.MsgType()) + binary.Write(&buf, binary.BigEndian, msg.TimeStamp) + binary.Write(&buf, binary.BigEndian, msg.ChunkSize) + buf.Write(msg.Data) + + return buf.Bytes() +} + +func (msg ChunkMsg) Peer() uint32 { + return msg.PeerIdx +} + +func (msg ChunkMsg) MsgType() uint8 { + return MSGID_CHUNK_BODY +} + + + +type EndChunksMsg struct { + PeerIdx uint32 + TimeStamp int64 +} + +func NewChunksEndMsgFromBytes(b []byte, peerIdx uint32) (EndChunksMsg, error) { + + + msg := new(EndChunksMsg) + buf := bytes.NewBuffer(b[1:]) // get rid of messageType + + msg.PeerIdx = peerIdx + binary.Read(buf, binary.BigEndian, &msg.TimeStamp) + + return *msg, nil +} + +func (msg EndChunksMsg) Bytes() []byte { + var buf bytes.Buffer + + buf.WriteByte(msg.MsgType()) + binary.Write(&buf, binary.BigEndian, msg.TimeStamp) + + return buf.Bytes() +} + +func (msg EndChunksMsg) Peer() uint32 { + return msg.PeerIdx +} + +func (msg EndChunksMsg) MsgType() uint8 { + return MSGID_CHUNKS_END +} \ No newline at end of file From 4b2b9da88e1f752c9ab111d1f69237b41bb0a821 Mon Sep 17 00:00:00 2001 From: infografx Date: Fri, 19 Jul 2019 15:23:15 +0300 Subject: [PATCH 03/24] Fix --- qln/dlc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qln/dlc.go b/qln/dlc.go index bcec34451..f2dfffb78 100644 --- a/qln/dlc.go +++ b/qln/dlc.go @@ -626,7 +626,7 @@ func (nd *LitNode) SettleContract(cIdx uint64, oracleValue int64, oracleSig [32] txClaim.AddTxIn(wire.NewTxIn(&settleOutpoint, nil, nil)) addr, err := wal.NewAdr() - txClaim.AddTxOut(wire.NewTxOut(d.ValueOurs-1000, lnutil.DirectWPKHScriptFromPKH(addr))) // todo calc fee - fee is double here because the contract output already had the fee deducted in the settlement TX + txClaim.AddTxOut(wire.NewTxOut(d.ValueOurs-fee, lnutil.DirectWPKHScriptFromPKH(addr))) // todo calc fee - fee is double here because the contract output already had the fee deducted in the settlement TX kg.Step[2] = UseContractPayoutBase privSpend, _ := wal.GetPriv(kg) From c518693048e8ce9d8ee10cc74d530cd1daaa2195 Mon Sep 17 00:00:00 2001 From: infografx Date: Fri, 19 Jul 2019 16:11:13 +0300 Subject: [PATCH 04/24] fix --- qln/dlc.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/qln/dlc.go b/qln/dlc.go index f2dfffb78..0fde07be1 100644 --- a/qln/dlc.go +++ b/qln/dlc.go @@ -612,12 +612,8 @@ func (nd *LitNode) SettleContract(cIdx uint64, oracleValue int64, oracleSig [32] if ( d.ValueOurs != 0){ - fee := int64(1000) - - if(c.OurFundingAmount + c.TheirFundingAmount == d.ValueOurs){ - fee += int64(1000) - } - + fee := int64(500) + // TODO: Claim the contract settlement output back to our wallet - otherwise the peer can claim it after locktime. txClaim := wire.NewMsgTx() txClaim.Version = 2 @@ -626,7 +622,7 @@ func (nd *LitNode) SettleContract(cIdx uint64, oracleValue int64, oracleSig [32] txClaim.AddTxIn(wire.NewTxIn(&settleOutpoint, nil, nil)) addr, err := wal.NewAdr() - txClaim.AddTxOut(wire.NewTxOut(d.ValueOurs-fee, lnutil.DirectWPKHScriptFromPKH(addr))) // todo calc fee - fee is double here because the contract output already had the fee deducted in the settlement TX + txClaim.AddTxOut(wire.NewTxOut(settleTx.TxOut[0].Value-fee, lnutil.DirectWPKHScriptFromPKH(addr))) // todo calc fee - fee is double here because the contract output already had the fee deducted in the settlement TX kg.Step[2] = UseContractPayoutBase privSpend, _ := wal.GetPriv(kg) From 034353303f2791f6dc979edf590d54eac27c54db Mon Sep 17 00:00:00 2001 From: infografx Date: Fri, 19 Jul 2019 16:29:16 +0300 Subject: [PATCH 05/24] fix --- lnutil/dlclib.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lnutil/dlclib.go b/lnutil/dlclib.go index 9f2f0c936..ba29c9959 100644 --- a/lnutil/dlclib.go +++ b/lnutil/dlclib.go @@ -453,7 +453,7 @@ func SettlementTx(c *DlcContract, d DlcContractDivision, feeTheirs := feeEach valueOurs := d.ValueOurs - // This code produces bugs so I disable it + // This code leads to errors at edges of the interval so I disable it // // We don't have enough to pay for a fee. We get 0, our contract partner // // pays the rest of the fee @@ -467,7 +467,7 @@ func SettlementTx(c *DlcContract, d DlcContractDivision, valueTheirs := totalContractValue - d.ValueOurs - // This code produces bugs so I disable it + // This code leads to errors at edges of the interval so I disable it // if valueTheirs < feeEach { // feeTheirs = valueTheirs @@ -479,7 +479,7 @@ func SettlementTx(c *DlcContract, d DlcContractDivision, // } - // New code to handle edges of a contract interval + // There is a simplified code but it works at edges of the interval. if valueOurs == 0 { valueTheirs = valueTheirs - totalFee From 9683cf8f8b3d208a55a13feb17e0be0fb69a28ff Mon Sep 17 00:00:00 2001 From: infografx Date: Sat, 20 Jul 2019 19:34:08 +0300 Subject: [PATCH 06/24] Fees calculations. --- dlc/contract.go | 21 ++++++++ litrpc/dlccmds.go | 28 +++++++++++ lnutil/dlclib.go | 122 ++++++++++++++++++++++++++++++++++------------ qln/dlc.go | 89 ++++++++++++++++++++++++++++++--- qln/msghandler.go | 20 +++++++- 5 files changed, 239 insertions(+), 41 deletions(-) diff --git a/dlc/contract.go b/dlc/contract.go index cc786cbdf..411391a2f 100644 --- a/dlc/contract.go +++ b/dlc/contract.go @@ -7,6 +7,7 @@ import ( ) const COINTYPE_NOT_SET = ^uint32(0) // Max Uint +const FEEPERBYTE_NOT_SET = ^uint32(0) // Max Uint // AddContract starts a new draft contract func (mgr *DlcManager) AddContract() (*lnutil.DlcContract, error) { @@ -244,3 +245,23 @@ func (mgr *DlcManager) SetContractCoinType(cIdx uint64, cointype uint32) error { return nil } + + +//SetContractFeePerByte sets the fee per byte for a particular contract +func (mgr *DlcManager) SetContractFeePerByte(cIdx uint64, feeperbyte uint32) error { + c, err := mgr.LoadContract(cIdx) + if err != nil { + return err + } + + if c.Status != lnutil.ContractStatusDraft { + return fmt.Errorf("You cannot change or set the coin type unless" + + " the contract is in Draft state") + } + + c.FeePerByte = feeperbyte + + mgr.SaveContract(c) + + return nil +} diff --git a/litrpc/dlccmds.go b/litrpc/dlccmds.go index bb6d84cb2..f54d0e625 100644 --- a/litrpc/dlccmds.go +++ b/litrpc/dlccmds.go @@ -314,6 +314,34 @@ func (r *LitRPC) SetContractCoinType(args SetContractCoinTypeArgs, return nil } + +type SetContractFeePerByteArgs struct { + CIdx uint64 + FeePerByte uint32 +} + +type SetContractFeePerByteReply struct { + Success bool +} + +// SetContractFeePerByte sets the coin type the contract will be in. Note that a +// peer that doesn't have a wallet of that type will automatically decline the +// contract. +func (r *LitRPC) SetContractFeePerByte(args SetContractFeePerByteArgs, + reply *SetContractFeePerByteReply) error { + var err error + + err = r.Node.DlcManager.SetContractFeePerByte(args.CIdx, args.FeePerByte) + if err != nil { + return err + } + + reply.Success = true + return nil +} + + + type OfferContractArgs struct { CIdx uint64 PeerIdx uint32 diff --git a/lnutil/dlclib.go b/lnutil/dlclib.go index ba29c9959..2f3bc1d92 100644 --- a/lnutil/dlclib.go +++ b/lnutil/dlclib.go @@ -48,6 +48,8 @@ type DlcContract struct { PeerIdx uint32 // Coin type CoinType uint32 + // Fee per byte + FeePerByte uint32 // Pub keys of the oracle and the R point used in the contract OracleA, OracleR [33]byte // The time we expect the oracle to publish @@ -125,6 +127,14 @@ func DlcContractFromBytes(b []byte) (*DlcContract, error) { return nil, err } c.CoinType = uint32(coinType) + + feePerByte, err := wire.ReadVarInt(buf, 0) + if err != nil { + logging.Errorf("Error while deserializing varint for coinType: %s", err.Error()) + return nil, err + } + c.FeePerByte = uint32(feePerByte) + c.OracleTimestamp, err = wire.ReadVarInt(buf, 0) if err != nil { return nil, err @@ -245,6 +255,7 @@ func (self *DlcContract) Bytes() []byte { buf.Write(self.OracleR[:]) wire.WriteVarInt(&buf, 0, uint64(self.PeerIdx)) wire.WriteVarInt(&buf, 0, uint64(self.CoinType)) + wire.WriteVarInt(&buf, 0, uint64(self.FeePerByte)) wire.WriteVarInt(&buf, 0, uint64(self.OracleTimestamp)) wire.WriteVarInt(&buf, 0, uint64(self.OurFundingAmount)) wire.WriteVarInt(&buf, 0, uint64(self.TheirFundingAmount)) @@ -441,58 +452,107 @@ func computePubKey(pubA, pubR [33]byte, msg []byte) ([33]byte, error) { func SettlementTx(c *DlcContract, d DlcContractDivision, ours bool) (*wire.MsgTx, error) { + + + // Maximum possible size of transaction here is + // Version 4 bytes + LockTime 4 bytes + Serialized varint size for the + // number of transaction inputs and outputs. + // n := 8 + VarIntSerializeSize(uint64(len(msg.TxIn))) + + // VarIntSerializeSize(uint64(len(msg.TxOut))) + + // Plus Witness Data 218 + // Plus Single input 41 + // Plus Their output 43 + // Plus Our output 31 + // Plus 2 for all wittness transactions + // Total max size of tx here is: 4 + 4 + 1 + 1 + 2 + 218 + 41 + 43 + 31 = 345 + // Vsize: ( (345 - 218 - 2) * 3 + 345 ) / 4 = 180 + + maxVsize := 180 + tx := wire.NewMsgTx() // set version 2, for op_csv tx.Version = 2 tx.AddTxIn(wire.NewTxIn(&c.FundingOutpoint, nil, nil)) - totalFee := int64(consts.DlcSettlementTxFee) // TODO: Calculate - feeEach := int64(float64(totalFee) / float64(2)) + + totalFee := uint32(maxVsize * int(c.FeePerByte)) + + feeEach := uint32(totalFee / uint32(2)) feeOurs := feeEach feeTheirs := feeEach + + totalContractValue := c.TheirFundingAmount + c.OurFundingAmount + valueOurs := d.ValueOurs + valueTheirs := totalContractValue - d.ValueOurs + - // This code leads to errors at edges of the interval so I disable it + if totalContractValue < int64(totalFee) { + return nil, errors.New("totalContractValue < totalFee") + } - // // We don't have enough to pay for a fee. We get 0, our contract partner - // // pays the rest of the fee - // if valueOurs < feeEach { - // feeOurs = valueOurs - // valueOurs = 0 - // } else { - // valueOurs = d.ValueOurs - feeOurs - // } - totalContractValue := c.TheirFundingAmount + c.OurFundingAmount - valueTheirs := totalContractValue - d.ValueOurs + vsize :=uint32(0) + + // We don't have enough to pay for a fee. We get 0, our contract partner + // pays the rest of the fee + if valueOurs < int64(feeOurs) { - // This code leads to errors at edges of the interval so I disable it + // Just recalculate totalFee, feeOurs, feeTheirs to exclude one of the output. + if ours { - // if valueTheirs < feeEach { - // feeTheirs = valueTheirs - // valueTheirs = 0 - // feeOurs = totalFee - feeTheirs - // valueOurs = d.ValueOurs - feeOurs - // } else { - // valueTheirs -= feeTheirs - // } + // exclude wire.NewTxOut from size (i.e 31) + vsize = uint32(149) + totalFee = vsize * uint32(c.FeePerByte) + }else{ + + // exclude DlcOutput from size (i.e 43) + vsize = uint32(137) + totalFee = vsize * uint32(c.FeePerByte) + + } - // There is a simplified code but it works at edges of the interval. + feeEach = uint32(float64(totalFee) / float64(2)) + feeOurs = feeEach + feeTheirs = feeEach - if valueOurs == 0 { - valueTheirs = valueTheirs - totalFee - }else{ - valueOurs = d.ValueOurs - feeOurs + if valueOurs == 0 { // Also if we win 0, our contract partner pays the totalFee + feeTheirs = totalFee + }else{ + + feeTheirs += uint32(valueOurs) + valueOurs = 0 + + } } - if valueTheirs == 0 { - valueOurs = valueOurs - totalFee - }else{ - valueTheirs -= feeTheirs + // Due to check above it is impossible (valueTheirs < feeTheirs) and + // (valueOurs < feeOurs) are satisfied at the same time. + if valueTheirs < int64(feeTheirs) { + if ours { + vsize = uint32(137) + }else{ + vsize = uint32(149) + } + totalFee = vsize * c.FeePerByte + feeEach = int64(float64(totalFee) / float64(2)) + feeOurs = feeEach + feeTheirs = feeEach + + if valueTheirs == 0 { + feeOurs = totalFee + }else{ + feeOurs += valueTheirs + valueTheirs = 0 + + } } + valueOurs -= int64(feeOurs) + valueTheirs -= int64(feeTheirs) var buf bytes.Buffer diff --git a/qln/dlc.go b/qln/dlc.go index 5646baa46..7cd2802f3 100644 --- a/qln/dlc.go +++ b/qln/dlc.go @@ -57,6 +57,10 @@ func (nd *LitNode) OfferDlc(peerIdx uint32, cIdx uint64) error { return fmt.Errorf("You need to set a coin type for the contract before offering it") } + if c.FeePerByte == dlc.FEEPERBYTE_NOT_SET { + return fmt.Errorf("You need to set a fee per byte for the contract before offering it") + } + if c.Division == nil { return fmt.Errorf("You need to set a payout division for the contract before offering it") } @@ -236,6 +240,7 @@ func (nd *LitNode) DlcOfferHandler(msg lnutil.DlcOfferMsg, peer *RemotePeer) { // Copy c.CoinType = msg.Contract.CoinType + c.FeePerByte = msg.Contract.FeePerByte c.OracleA = msg.Contract.OracleA c.OracleR = msg.Contract.OracleR c.OracleTimestamp = msg.Contract.OracleTimestamp @@ -474,18 +479,67 @@ func (nd *LitNode) BuildDlcFundingTransaction(c *lnutil.DlcContract) (wire.MsgTx var ourInputTotal int64 var theirInputTotal int64 - for _, u := range c.OurFundingInputs { - tx.AddTxIn(wire.NewTxIn(&u.Outpoint, nil, nil)) + our_in_size := int(0) + their_in_size := int(0) + + for i, u := range c.OurFundingInputs { + txin := wire.NewTxIn(&u.Outpoint, nil, nil) + + our_in_size += txin.SerializeSize() + + tx.AddTxIn(txin) ourInputTotal += u.Value + } - for _, u := range c.TheirFundingInputs { - tx.AddTxIn(wire.NewTxIn(&u.Outpoint, nil, nil)) + + + their_txin_num := 0 + for i, u := range c.TheirFundingInputs { + txin := wire.NewTxIn(&u.Outpoint, nil, nil) + + their_in_size += txin.SerializeSize() + + tx.AddTxIn(txin) theirInputTotal += u.Value + } + + + //==================================================== + + // Here can be a situation when peers have different number of inputs. + // Therefore we have to calculate fees for each peer separately. + + // This transaction always will have 3 outputs ( 43 + 31 + 31) + tx_basesize := 10 + 43 + 31 + 31 + tx_size_foreach := tx_basesize / 2 + tx_size_foreach += 1 // rounding + + input_wit_size := 107 + + our_tx_vsize := uint32(((tx_size_foreach + (41 * our_txin_num)) * 3 + (tx_size_foreach + (41 * our_txin_num) + (input_wit_size*our_txin_num) )) / 4) + their_tx_vsize := uint32(((tx_size_foreach + (41 * their_txin_num)) * 3 + (tx_size_foreach + (41 * their_txin_num) + (input_wit_size*their_txin_num) )) / 4) + + //rounding + our_tx_vsize += 1 + their_tx_vsize += 1 + + + our_fee := int64(our_tx_vsize * c.FeePerByte) + their_fee := int64(their_tx_vsize * c.FeePerByte) + + // add change and sort - tx.AddTxOut(wire.NewTxOut(theirInputTotal-c.TheirFundingAmount-500, lnutil.DirectWPKHScriptFromPKH(c.TheirChangePKH))) - tx.AddTxOut(wire.NewTxOut(ourInputTotal-c.OurFundingAmount-500, lnutil.DirectWPKHScriptFromPKH(c.OurChangePKH))) + + their_txout := wire.NewTxOut(theirInputTotal-c.TheirFundingAmount-their_fee, lnutil.DirectWPKHScriptFromPKH(c.TheirChangePKH)) + tx.AddTxOut(their_txout) + + + our_txout := wire.NewTxOut(ourInputTotal-c.OurFundingAmount-our_fee, lnutil.DirectWPKHScriptFromPKH(c.OurChangePKH)) + tx.AddTxOut(our_txout) + + txsort.InPlaceSort(tx) @@ -610,9 +664,28 @@ func (nd *LitNode) SettleContract(cIdx uint64, oracleValue int64, oracleSig [32] } + //=========================================== + // Claim TX + //=========================================== + + + // Here the transaction size is always the same + // n := 8 + VarIntSerializeSize(uint64(len(msg.TxIn))) + + // VarIntSerializeSize(uint64(len(msg.TxOut))) + // n = 10 + // Plus Single input 41 + // Plus Single output 31 + // Plus 2 for all wittness transactions + // Plus Witness Data 151 + + // TxSize = 4 + 4 + 1 + 1 + 2 + 151 + 41 + 31 = 235 + // Vsize = ((235 - 151 - 2) * 3 + 235) / 4 = 120,25 + + if ( d.ValueOurs != 0){ - fee := int64(500) + vsize := uint32(121) + fee := vsize * c.FeePerByte // TODO: Claim the contract settlement output back to our wallet - otherwise the peer can claim it after locktime. txClaim := wire.NewMsgTx() @@ -622,7 +695,7 @@ func (nd *LitNode) SettleContract(cIdx uint64, oracleValue int64, oracleSig [32] txClaim.AddTxIn(wire.NewTxIn(&settleOutpoint, nil, nil)) addr, err := wal.NewAdr() - txClaim.AddTxOut(wire.NewTxOut(settleTx.TxOut[0].Value-fee, lnutil.DirectWPKHScriptFromPKH(addr))) // todo calc fee - fee is double here because the contract output already had the fee deducted in the settlement TX + txClaim.AddTxOut(wire.NewTxOut(settleTx.TxOut[0].Value-int64(fee), lnutil.DirectWPKHScriptFromPKH(addr))) kg.Step[2] = UseContractPayoutBase privSpend, _ := wal.GetPriv(kg) diff --git a/qln/msghandler.go b/qln/msghandler.go index d1e980c50..88b1aa35f 100644 --- a/qln/msghandler.go +++ b/qln/msghandler.go @@ -574,8 +574,24 @@ func (nd *LitNode) HandleContractOPEvent(c *lnutil.DlcContract, if err != nil { return err } - txClaim.AddTxOut(wire.NewTxOut(value-500, - lnutil.DirectWPKHScriptFromPKH(addr))) // todo calc fee + + // Here the transaction size is always the same + // n := 8 + VarIntSerializeSize(uint64(len(msg.TxIn))) + + // VarIntSerializeSize(uint64(len(msg.TxOut))) + // n = 10 + // Plus Single input 41 + // Plus Single output 31 + // Plus 2 for all wittness transactions + // Plus Witness Data 108 + + // TxSize = 4 + 4 + 1 + 1 + 2 + 108 + 41 + 31 = 192 + // Vsize = ((192 - 108 - 2) * 3 + 192) / 4 = 109,5 + + vsize := uint32(110) + fee := vsize * c.FeePerByte + + txClaim.AddTxOut(wire.NewTxOut(value-int64(fee), + lnutil.DirectWPKHScriptFromPKH(addr))) var kg portxo.KeyGen kg.Depth = 5 From e9aefd2efae68b9af251afa803b5a812237526ab Mon Sep 17 00:00:00 2001 From: infografx Date: Sat, 20 Jul 2019 19:59:43 +0300 Subject: [PATCH 07/24] Contract settlement from counterparty works. --- qln/dlc.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/qln/dlc.go b/qln/dlc.go index fd61c7846..cdda22a95 100644 --- a/qln/dlc.go +++ b/qln/dlc.go @@ -85,6 +85,16 @@ func (nd *LitNode) OfferDlc(peerIdx uint32, cIdx uint64) error { return err } + ourPayoutPKHKey, err := nd.GetUsePub(kg, UseContractPayoutPKH) + if err != nil { + logging.Errorf("Error while getting our payout pubkey: %s", err.Error()) + c.Status = lnutil.ContractStatusError + nd.DlcManager.SaveContract(c) + return err + } + + copy(c.OurPayoutPKH[:], btcutil.Hash160(ourPayoutPKHKey[:])) + // Fund the contract err = nd.FundContract(c) if err != nil { @@ -227,6 +237,7 @@ func (nd *LitNode) DlcOfferHandler(msg lnutil.DlcOfferMsg, peer *RemotePeer) { c.OurChangePKH = msg.Contract.TheirChangePKH c.TheirChangePKH = msg.Contract.OurChangePKH c.TheirIdx = msg.Contract.Idx + c.TheirPayoutPKH = msg.Contract.OurPayoutPKH c.Division = make([]lnutil.DlcContractDivision, len(msg.Contract.Division)) for i := 0; i < len(msg.Contract.Division); i++ { @@ -324,6 +335,8 @@ func (nd *LitNode) DlcContractAckHandler(msg lnutil.DlcContractAckMsg, peer *Rem c.Status = lnutil.ContractStatusAcknowledged + c.TheirSettlementSignatures = msg.SettlementSignatures + err = nd.DlcManager.SaveContract(c) if err != nil { logging.Errorf("DlcContractAckHandler SaveContract err %s\n", err.Error()) From 39f5f4c4ea96562a77f3bbb4e9c8dd733405489b Mon Sep 17 00:00:00 2001 From: infografx Date: Mon, 22 Jul 2019 23:08:48 +0300 Subject: [PATCH 08/24] Merge branch 'milestone2-test' into master-mit-lit_milestone2-test --- dlc/contract.go | 21 ++ litrpc/dlccmds.go | 38 +++ lnp2p/msgproc.go | 1 + lnutil/dlclib.go | 22 +- qln/dlc.go | 20 +- qln/msghandler.go | 1 + test/itest_dlc.py | 780 +++++++++++++++++++++++++++++++++++++++++++++ test/itest_send.py | 2 +- test/runtests.py | 2 +- test/testlib.py | 105 +++++- test/tests.txt | 15 + 11 files changed, 979 insertions(+), 28 deletions(-) create mode 100644 test/itest_dlc.py diff --git a/dlc/contract.go b/dlc/contract.go index 411391a2f..492f94217 100644 --- a/dlc/contract.go +++ b/dlc/contract.go @@ -265,3 +265,24 @@ func (mgr *DlcManager) SetContractFeePerByte(cIdx uint64, feeperbyte uint32) err return nil } + + +// //GetContractDivision +// GetContractDivision(args.CIdx, args.OracleValue) +// func (mgr *DlcManager) GetContractDivision(cIdx uint64, feeperbyte uint32) error { +// c, err := mgr.LoadContract(cIdx) +// if err != nil { +// return err +// } + +// if c.Status != lnutil.ContractStatusDraft { +// return fmt.Errorf("You cannot change or set the coin type unless" + +// " the contract is in Draft state") +// } + +// c.FeePerByte = feeperbyte + +// mgr.SaveContract(c) + +// return nil +// } diff --git a/litrpc/dlccmds.go b/litrpc/dlccmds.go index f54d0e625..001928369 100644 --- a/litrpc/dlccmds.go +++ b/litrpc/dlccmds.go @@ -2,6 +2,7 @@ package litrpc import ( "encoding/hex" + "fmt" "github.com/mit-dci/lit/dlc" "github.com/mit-dci/lit/lnutil" @@ -340,6 +341,43 @@ func (r *LitRPC) SetContractFeePerByte(args SetContractFeePerByteArgs, return nil } +//---------------------------------------------------------- + +type GetContractDivisionArgs struct { + CIdx uint64 + OracleValue int64 +} + +type GetContractDivisionReply struct { + ValueOurs int64 +} + +// GetContractDivision +func (r *LitRPC) GetContractDivision(args GetContractDivisionArgs, + reply *GetContractDivisionReply) error { + + //err = r.Node.DlcManager.GetContractDivision(args.CIdx, args.OracleValue) + + c, err1 := r.Node.DlcManager.LoadContract(args.CIdx) + if err1 != nil { + fmt.Errorf("GetContractDivision(): LoadContract err %s\n", err1.Error()) + return err1 + } + + + d, err2 := c.GetDivision(args.OracleValue) + if err2 != nil { + fmt.Errorf("GetContractDivision(): c.GetDivision err %s\n", err2.Error()) + return err2 + } + reply.ValueOurs = d.ValueOurs + + return nil +} + + +//----------------------------------------------------------- + type OfferContractArgs struct { diff --git a/lnp2p/msgproc.go b/lnp2p/msgproc.go index 3df578fe8..b7d210451 100644 --- a/lnp2p/msgproc.go +++ b/lnp2p/msgproc.go @@ -2,6 +2,7 @@ package lnp2p import ( "fmt" + "os" "github.com/mit-dci/lit/logging" "sync" "github.com/mit-dci/lit/lnutil" diff --git a/lnutil/dlclib.go b/lnutil/dlclib.go index 2f3bc1d92..d4c17f5b7 100644 --- a/lnutil/dlclib.go +++ b/lnutil/dlclib.go @@ -6,9 +6,10 @@ import ( "encoding/binary" "fmt" "math/big" + "errors" + "os" "github.com/mit-dci/lit/btcutil/chaincfg/chainhash" - "github.com/mit-dci/lit/consts" "github.com/mit-dci/lit/crypto/koblitz" "github.com/mit-dci/lit/logging" "github.com/mit-dci/lit/wire" @@ -487,7 +488,6 @@ func SettlementTx(c *DlcContract, d DlcContractDivision, valueOurs := d.ValueOurs valueTheirs := totalContractValue - d.ValueOurs - if totalContractValue < int64(totalFee) { return nil, errors.New("totalContractValue < totalFee") @@ -504,16 +504,13 @@ func SettlementTx(c *DlcContract, d DlcContractDivision, if ours { // exclude wire.NewTxOut from size (i.e 31) - vsize = uint32(149) - totalFee = vsize * uint32(c.FeePerByte) - + vsize = uint32(149) }else{ - // exclude DlcOutput from size (i.e 43) vsize = uint32(137) - totalFee = vsize * uint32(c.FeePerByte) } + totalFee = vsize * uint32(c.FeePerByte) feeEach = uint32(float64(totalFee) / float64(2)) feeOurs = feeEach @@ -523,7 +520,8 @@ func SettlementTx(c *DlcContract, d DlcContractDivision, feeTheirs = totalFee }else{ - feeTheirs += uint32(valueOurs) + feeTheirs += uint32(valueOurs) // Also if we win less that the fees, our prize goes + // to a counterparty to increase his fee for a tx. valueOurs = 0 } @@ -538,23 +536,25 @@ func SettlementTx(c *DlcContract, d DlcContractDivision, vsize = uint32(149) } totalFee = vsize * c.FeePerByte - feeEach = int64(float64(totalFee) / float64(2)) + + + feeEach = uint32(float64(totalFee) / float64(2)) feeOurs = feeEach feeTheirs = feeEach if valueTheirs == 0 { feeOurs = totalFee }else{ - feeOurs += valueTheirs + feeOurs += uint32(valueTheirs) valueTheirs = 0 } } + valueOurs -= int64(feeOurs) valueTheirs -= int64(feeTheirs) - var buf bytes.Buffer binary.Write(&buf, binary.BigEndian, uint64(0)) binary.Write(&buf, binary.BigEndian, uint64(0)) diff --git a/qln/dlc.go b/qln/dlc.go index ea1d510b6..f8c316661 100644 --- a/qln/dlc.go +++ b/qln/dlc.go @@ -2,6 +2,9 @@ package qln import ( "fmt" + "os" + // "bytes" + // "bufio" "github.com/mit-dci/lit/btcutil" "github.com/mit-dci/lit/btcutil/txscript" @@ -492,28 +495,22 @@ func (nd *LitNode) BuildDlcFundingTransaction(c *lnutil.DlcContract) (wire.MsgTx var ourInputTotal int64 var theirInputTotal int64 - our_in_size := int(0) - their_in_size := int(0) - - for i, u := range c.OurFundingInputs { + our_txin_num := 0 + for _, u := range c.OurFundingInputs { txin := wire.NewTxIn(&u.Outpoint, nil, nil) - - our_in_size += txin.SerializeSize() - tx.AddTxIn(txin) ourInputTotal += u.Value + our_txin_num += 1 } their_txin_num := 0 - for i, u := range c.TheirFundingInputs { + for _, u := range c.TheirFundingInputs { txin := wire.NewTxIn(&u.Outpoint, nil, nil) - - their_in_size += txin.SerializeSize() - tx.AddTxIn(txin) theirInputTotal += u.Value + their_txin_num += 1 } @@ -542,7 +539,6 @@ func (nd *LitNode) BuildDlcFundingTransaction(c *lnutil.DlcContract) (wire.MsgTx our_fee := int64(our_tx_vsize * c.FeePerByte) their_fee := int64(their_tx_vsize * c.FeePerByte) - // add change and sort their_txout := wire.NewTxOut(theirInputTotal-c.TheirFundingAmount-their_fee, lnutil.DirectWPKHScriptFromPKH(c.TheirChangePKH)) diff --git a/qln/msghandler.go b/qln/msghandler.go index 88b1aa35f..846cbb2f0 100644 --- a/qln/msghandler.go +++ b/qln/msghandler.go @@ -3,6 +3,7 @@ package qln import ( "bytes" "fmt" + "os" "github.com/mit-dci/lit/logging" diff --git a/test/itest_dlc.py b/test/itest_dlc.py new file mode 100644 index 000000000..5a0cd634a --- /dev/null +++ b/test/itest_dlc.py @@ -0,0 +1,780 @@ +import testlib + +import time, datetime +import json + +import pprint + +import requests # pip3 install requests + +import codecs + +deb_mod = False + +def run_t(env, params): + global deb_mod + try: + + lit_funding_amt = params[0] + contract_funding_amt = params[1] + oracle_value = params[2] + node_to_settle = params[3] + valueFullyOurs=params[4] + valueFullyTheirs=params[5] + + FundingTxVsize = params[6][0] + SettlementTxVsize = params[6][1] + + feeperbyte = params[7] + + SetTxFeeOurs = params[8] + SetTxFeeTheirs = params[9] + + ClaimTxFeeOurs = params[10] + ClaimTxFeeTheirs = params[11] + + bc = env.bitcoind + + #------------ + # Create oracles + #------------ + + env.new_oracle(1, oracle_value) # publishing interval is 1 second. + + #settle_lit = env.lits[node_to_settle] + + oracle1 = env.oracles[0] + + time.sleep(2) + + #------------ + # Create lits + #------------ + + lit1 = env.lits[0] + lit2 = env.lits[1] + + + pp = pprint.PrettyPrinter(indent=4) + + + #------------------------------------------ + if deb_mod: + print("ADDRESSES BEFORE SEND TO ADDRESS") + print("LIT1 Addresses") + print(pp.pprint(lit1.rpc.GetAddresses())) + + print("LIT2 Addresses") + print(pp.pprint(lit2.rpc.GetAddresses())) + + print("bitcoind Addresses") + print(pp.pprint(bc.rpc.listaddressgroupings())) + #------------------------------------------ + + + lit1.connect_to_peer(lit2) + print("---------------") + print('Connecting lit1:', lit1.lnid, 'to lit2:', lit2.lnid) + + addr1 = lit1.make_new_addr() + txid1 = bc.rpc.sendtoaddress(addr1, lit_funding_amt) + + if deb_mod: + print("Funding TxId lit1: " + str(txid1)) + + time.sleep(5) + + addr2 = lit2.make_new_addr() + txid2 = bc.rpc.sendtoaddress(addr2, lit_funding_amt) + + if deb_mod: + print("Funding TxId lit2: " + str(txid2)) + + time.sleep(5) + + env.generate_block() + time.sleep(5) + + print("Funding") + bals1 = lit1.get_balance_info() + print('new lit1 balance:', bals1['TxoTotal'], 'in txos,', bals1['ChanTotal'], 'in chans') + bal1sum = bals1['TxoTotal'] + bals1['ChanTotal'] + print(' = sum ', bal1sum) + + print(lit_funding_amt) + + lit_funding_amt *= 100000000 # to satoshi + + + + + bals2 = lit2.get_balance_info() + print('new lit2 balance:', bals2['TxoTotal'], 'in txos,', bals2['ChanTotal'], 'in chans') + bal2sum = bals2['TxoTotal'] + bals2['ChanTotal'] + print(' = sum ', bal2sum) + + + assert bal1sum == lit_funding_amt, "Funding lit1 does not works" + assert bal2sum == lit_funding_amt, "Funding lit2 does not works" + + + #------------------------------------------ + if deb_mod: + print("ADDRESSES AFTER SEND TO ADDRESS") + print("LIT1 Addresses") + print(pp.pprint(lit1.rpc.GetAddresses())) + + print("LIT2 Addresses") + print(pp.pprint(lit2.rpc.GetAddresses())) + + print("bitcoind Addresses") + print(pp.pprint(bc.rpc.listaddressgroupings())) + #------------------------------------------ + + + # #------------ + # # Add oracles + # #------------ + + res = lit1.rpc.ListOracles() + assert len(res) != 0, "Initial lis of oracles must be empty" + + oracle1_pubkey = json.loads(oracle1.get_pubkey()) + assert len(oracle1_pubkey["A"]) == 66, "Wrong oracle1 pub key" + + # oracle2_pubkey = json.loads(oracle2.get_pubkey()) + # assert len(oracle2_pubkey["A"]) == 66, "Wrong oracle2 pub key" + + oracle_res1 = lit1.rpc.AddOracle(Key=oracle1_pubkey["A"], Name="oracle1") + assert oracle_res1["Oracle"]["Idx"] == 1, "AddOracle does not works" + + res = lit1.rpc.ListOracles(ListOraclesArgs={}) + assert len(res["Oracles"]) == 1, "ListOracles 1 does not works" + + + lit2.rpc.AddOracle(Key=oracle1_pubkey["A"], Name="oracle1") + + + # #------------ + # # Now we have to create a contract in the lit1 node. + # #------------ + + contract = lit1.rpc.NewContract() + + res = lit1.rpc.ListContracts() + assert len(res["Contracts"]) == 1, "ListContracts does not works" + + + res = lit1.rpc.GetContract(Idx=1) + assert res["Contract"]["Idx"] == 1, "GetContract does not works" + + + res = lit1.rpc.SetContractOracle(CIdx=contract["Contract"]["Idx"], OIdx=oracle_res1["Oracle"]["Idx"]) + assert res["Success"], "SetContractOracle does not works" + + datasources = json.loads(oracle1.get_datasources()) + + # Since the oracle publishes data every 1 second (we set this time above), + # we increase the time for a point by 3 seconds. + + settlement_time = int(time.time()) + 3 + + # dlc contract settime 1 1552080600 + lit1.rpc.SetContractSettlementTime(CIdx=contract["Contract"]["Idx"], Time=settlement_time) + + res = lit1.rpc.ListContracts() + assert res["Contracts"][contract["Contract"]["Idx"] - 1]["OracleTimestamp"] == settlement_time, "SetContractSettlementTime does not match settlement_time" + + rpoint1 = oracle1.get_rpoint(datasources[0]["id"], settlement_time) + + decode_hex = codecs.getdecoder("hex_codec") + b_RPoint = decode_hex(json.loads(rpoint1)['R'])[0] + RPoint = [elem for elem in b_RPoint] + + res = lit1.rpc.SetContractRPoint(CIdx=contract["Contract"]["Idx"], RPoint=RPoint) + assert res["Success"], "SetContractRpoint does not works" + + lit1.rpc.SetContractCoinType(CIdx=contract["Contract"]["Idx"], CoinType = 257) + res = lit1.rpc.GetContract(Idx=contract["Contract"]["Idx"]) + assert res["Contract"]["CoinType"] == 257, "SetContractCoinType does not works" + + + lit1.rpc.SetContractFeePerByte(CIdx=contract["Contract"]["Idx"], FeePerByte = feeperbyte) + res = lit1.rpc.GetContract(Idx=contract["Contract"]["Idx"]) + assert res["Contract"]["FeePerByte"] == feeperbyte, "SetContractFeePerByte does not works" + + ourFundingAmount = contract_funding_amt + theirFundingAmount = contract_funding_amt + + lit1.rpc.SetContractFunding(CIdx=contract["Contract"]["Idx"], OurAmount=ourFundingAmount, TheirAmount=theirFundingAmount) + res = lit1.rpc.GetContract(Idx=contract["Contract"]["Idx"]) + assert res["Contract"]["OurFundingAmount"] == ourFundingAmount, "SetContractFunding does not works" + assert res["Contract"]["TheirFundingAmount"] == theirFundingAmount, "SetContractFunding does not works" + + print("Before SetContractDivision") + + res = lit1.rpc.SetContractDivision(CIdx=contract["Contract"]["Idx"], ValueFullyOurs=valueFullyOurs, ValueFullyTheirs=valueFullyTheirs) + assert res["Success"], "SetContractDivision does not works" + + print("After SetContractDivision") + + time.sleep(8) + + + res = lit1.rpc.ListConnections() + print(res) + + print("Before OfferContract") + + res = lit1.rpc.OfferContract(CIdx=contract["Contract"]["Idx"], PeerIdx=lit1.get_peer_id(lit2)) + assert res["Success"], "OfferContract does not works" + + print("After OfferContract") + + time.sleep(8) + + + print("Before ContractRespond") + + res = lit2.rpc.ContractRespond(AcceptOrDecline=True, CIdx=1) + assert res["Success"], "ContractRespond on lit2 does not works" + + print("After ContractRespond") + + time.sleep(8) + + #------------------------------------------ + + if deb_mod: + print("ADDRESSES AFTER CONTRACT RESPOND") + print("LIT1 Addresses") + print(lit1.rpc.GetAddresses()) + + print("LIT2 Addresses") + print(lit2.rpc.GetAddresses()) + + print("bitcoind Addresses") + print(bc.rpc.listaddressgroupings()) + + + # #------------------------------------------ + + + print("Before Generate Block") + + env.generate_block() + time.sleep(5) + + print("After Generate Block") + + print("Accept") + bals1 = lit1.get_balance_info() + print('new lit1 balance:', bals1['TxoTotal'], 'in txos,', bals1['ChanTotal'], 'in chans') + bal1sum = bals1['TxoTotal'] + bals1['ChanTotal'] + print(' = sum ', bal1sum) + + lit1_bal_after_accept = (lit_funding_amt - ourFundingAmount) - (126*feeperbyte) + + + bals2 = lit2.get_balance_info() + print('new lit2 balance:', bals2['TxoTotal'], 'in txos,', bals2['ChanTotal'], 'in chans') + bal2sum = bals2['TxoTotal'] + bals2['ChanTotal'] + print(' = sum ', bal2sum) + + lit2_bal_after_accept = (lit_funding_amt - theirFundingAmount) - (126*feeperbyte) + + + assert bal1sum == lit1_bal_after_accept, "lit1 Balance after contract accept does not match" + assert bal2sum == lit2_bal_after_accept, "lit2 Balance after contract accept does not match" + + oracle1_val = "" + oracle1_sig = "" + + i = 0 + while True: + res = oracle1.get_publication(json.loads(rpoint1)['R']) + time.sleep(5) + i += 1 + if i>4: + assert False, "Error: Oracle does not publish data" + + try: + oracle1_val = json.loads(res)["value"] + oracle1_sig = json.loads(res)["signature"] + break + except BaseException as e: + print(e) + next + + + b_OracleSig = decode_hex(oracle1_sig)[0] + OracleSig = [elem for elem in b_OracleSig] + + + print("Before SettleContract") + time.sleep(8) + + + res = env.lits[node_to_settle].rpc.SettleContract(CIdx=contract["Contract"]["Idx"], OracleValue=oracle1_val, OracleSig=OracleSig) + assert res["Success"], "SettleContract does not works." + + time.sleep(8) + + print('After SettleContract:') + print(res) + + + try: + env.generate_block(1) + time.sleep(2) + env.generate_block(10) + time.sleep(2) + env.generate_block(1) + except BaseException as be: + print("Exception After SettleContract: ") + print(be) + + time.sleep(10) + #------------------------------------------ + + if deb_mod: + + best_block_hash = bc.rpc.getbestblockhash() + bb = bc.rpc.getblock(best_block_hash) + print(bb) + print("bb['height']: " + str(bb['height'])) + + print("Balance from RPC: " + str(bc.rpc.getbalance())) + + # batch support : print timestamps of blocks 0 to 99 in 2 RPC round-trips: + commands = [ [ "getblockhash", height] for height in range(bb['height'] + 1) ] + block_hashes = bc.rpc.batch_(commands) + blocks = bc.rpc.batch_([ [ "getblock", h ] for h in block_hashes ]) + block_times = [ block["time"] for block in blocks ] + print(block_times) + + print('--------------------') + + for b in blocks: + print("--------BLOCK--------") + print(b) + tx = b["tx"] + #print(tx) + try: + + for i in range(len(tx)): + print("--------TRANSACTION--------") + rtx = bc.rpc.getrawtransaction(tx[i]) + print(rtx) + decoded = bc.rpc.decoderawtransaction(rtx) + pp.pprint(decoded) + except BaseException as be: + print(be) + # print(type(rtx)) + print('--------') + + + #------------------------------------------ + #------------------------------------------ + print("AFter Settle") + + print("ORACLE VALUE:", oracle1_val, "; oracle signature:", oracle1_sig) + + valueOurs = 0 + if node_to_settle == 0: + + valueOurs = env.lits[node_to_settle].rpc.GetContractDivision(CIdx=contract["Contract"]["Idx"],OracleValue=oracle1_val)['ValueOurs'] + valueTheirs = contract_funding_amt * 2 - valueOurs + + print("valueOurs:", valueOurs, "; valueTheirs:", valueTheirs) + + + elif node_to_settle == 1: + + valueTheirs = env.lits[node_to_settle].rpc.GetContractDivision(CIdx=contract["Contract"]["Idx"],OracleValue=oracle1_val)['ValueOurs'] + valueOurs = contract_funding_amt * 2 - valueTheirs + + print("valueOurs:", valueOurs, "; valueTheirs:", valueTheirs) + + else: + assert False, "Unknown node index." + + + lit1_bal_after_settle = valueOurs - SetTxFeeOurs + lit2_bal_after_settle = valueTheirs - SetTxFeeTheirs + + + lit1_bal_after_claim = lit1_bal_after_settle - ClaimTxFeeOurs + lit2_bal_after_claim = lit2_bal_after_settle - ClaimTxFeeTheirs + + lit1_bal_result = lit1_bal_after_claim + lit1_bal_after_accept + lit2_bal_result = lit2_bal_after_claim + lit2_bal_after_accept + + print("============== Fees Calc ===========================") + print("lit1_bal_after_settle", lit1_bal_after_settle) + print("lit2_bal_after_settle", lit2_bal_after_settle) + + print("lit1_bal_after_claim",lit1_bal_after_claim) + print("lit2_bal_after_claim",lit2_bal_after_claim) + + print("lit1_bal_result: ", lit1_bal_result) + print("lit2_bal_result: ", lit2_bal_result) + print("====================================================") + + + + bals1 = lit1.get_balance_info() + print('new lit1 balance:', bals1['TxoTotal'], 'in txos,', bals1['ChanTotal'], 'in chans') + bal1sum = bals1['TxoTotal'] + bals1['ChanTotal'] + print(bals1) + print(' = sum ', bal1sum) + + + bals2 = lit2.get_balance_info() + print('new lit2 balance:', bals2['TxoTotal'], 'in txos,', bals2['ChanTotal'], 'in chans') + bal2sum = bals2['TxoTotal'] + bals2['ChanTotal'] + print(bals2) + print(' = sum ', bal2sum) + + + assert bal1sum == lit1_bal_result, "The resulting lit1 node balance does not match." + assert bal2sum == lit2_bal_result, "The resulting lit2 node balance does not match." + + + + + + #------------------------------------------ + if deb_mod: + print("ADDRESSES AFTER SETTLE") + print("LIT1 Addresses") + print(pp.pprint(lit1.rpc.GetAddresses())) + + print("LIT2 Addresses") + print(pp.pprint(lit2.rpc.GetAddresses())) + + print("bitcoind Addresses") + print(pp.pprint(bc.rpc.listaddressgroupings())) + #------------------------------------------ + + + + print("=====START CONTRACT N1=====") + res = lit1.rpc.ListContracts() + #print(pp.pprint(res)) + print(res) + print("=====END CONTRACT N1=====") + + print("=====START CONTRACT N2=====") + res = lit2.rpc.ListContracts() + #print(pp.pprint(res)) + print(res) + print("=====END CONTRACT N2=====") + + + + except BaseException as be: + raise be + + + +def t_11_0(env): + + #----------------------------- + # 1)Funding transaction. + # Here can be a situation when peers have different number of inputs. + + # ::lit1:: BuildDlcFundingTransaction: qln/dlc.go: our_tx_vsize: 126 + # ::lit1:: BuildDlcFundingTransaction: qln/dlc.go: their_tx_vsize: 126 + # ::lit1:: BuildDlcFundingTransaction: qln/dlc.go: our_fee: 10080 + # ::lit1:: BuildDlcFundingTransaction: qln/dlc.go: their_fee: 10080 + + # Vsize from Blockchain: 252 + + # So we expect lit1, and lit2 balances equal to 89989920 !!! + # 90000000 - 89989920 = 10080 + # But this is only when peers have one input each. What we expect. + + #----------------------------- + # 2) SettlementTx vsize will be printed + + # ::lit0:: SettlementTx()1: qln/dlclib.go: --------------------: + # ::lit0:: SettlementTx()1: qln/dlclib.go: valueOurs: 18000000 + # ::lit0:: SettlementTx()1: qln/dlclib.go: valueTheirs: 2000000 + # ::lit0:: SettlementTx()1: qln/dlclib.go: --------------------: + # ::lit0:: SettlementTx()2: qln/dlclib.go: --------------------: + # ::lit0:: SettlementTx()2: qln/dlclib.go: valueOurs: 18000000 + # ::lit0:: SettlementTx()2: qln/dlclib.go: valueTheirs: 2000000 + # ::lit0:: SettlementTx()2: qln/dlclib.go: --------------------: + # ::lit0:: SettlementTx()3: qln/dlclib.go: --------------------: + # ::lit0:: SettlementTx()3: qln/dlclib.go: totalFee: 14400 + # ::lit0:: SettlementTx()3: qln/dlclib.go: feeEach: 7200 + # ::lit0:: SettlementTx()3: qln/dlclib.go: feeOurs: 7200 + # ::lit0:: SettlementTx()3: qln/dlclib.go: feeTheirs: 7200 + # ::lit0:: SettlementTx()3: qln/dlclib.go: valueOurs: 17992800 + # ::lit0:: SettlementTx()3: qln/dlclib.go: valueTheirs: 1992800 + # ::lit0:: SettlementTx()3: qln/dlclib.go: vsize: 0 # Actually we have 14400/80 = 180 + # ::lit0:: SettlementTx()3: qln/dlclib.go: --------------------: + + + # Vsize from Blockchain: 181 + + # There fore we expect here + # valueOurs: 17992800 = 18000000 - 7200 !!! + # valueTheirs: 1992800 = 2000000 - 7200 !!! + + + #----------------------------- + + # 3) Claim TX in SettleContract + # Here the transaction vsize is always the same: 121 + + + # Vsize from Blockchain: 121 + + #----------------------------- + + # 4) Claim TX from another peer + # Here the transaction vsize is always the same: 110 + + # Vsize from Blockchain: 110 + + #----------------------------- + + # 17992800 - (121 * 80) = 17983120 + # 89989920 + 17983120 = 107973040 + + # 1992800 - (110*80) = 1984000 + # 89989920 + 1984000 = 91973920 + + #----------------------------- + + # AFter Settle + # new lit1 balance: 107973040 in txos, 0 in chans + # = sum 107973040 + # {'CoinType': 257, 'SyncHeight': 514, 'ChanTotal': 0, 'TxoTotal': 107973040, 'MatureWitty': 107973040, 'FeeRate': 80} + # new lit2 balance: 91973920 in txos, 0 in chans + # = sum 91973920 + + #----------------------------- + + oracle_value = 11 + node_to_settle = 0 + + valueFullyOurs=10 + valueFullyTheirs=20 + + lit_funding_amt = 1 # 1 BTC + contract_funding_amt = 10000000 # satoshi + + FundingTxVsize = 252 + SettlementTxVsize = 181 + + SetTxFeeOurs = 7200 + SetTxFeeTheirs = 7200 + + ClaimTxFeeOurs = 121 * 80 + ClaimTxFeeTheirs = 110 * 80 + + + feeperbyte = 80 + + + vsizes = [FundingTxVsize, SettlementTxVsize] + + params = [lit_funding_amt, contract_funding_amt, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] + + run_t(env, params) + + + +# ==================================================================================== +# ==================================================================================== + + +def t_1300_1(env): + + #----------------------------- + # 1)Funding transaction. + # Here can be a situation when peers have different number of inputs. + + # ::lit1:: BuildDlcFundingTransaction: qln/dlc.go: our_tx_vsize: 126 + # ::lit1:: BuildDlcFundingTransaction: qln/dlc.go: their_tx_vsize: 126 + # ::lit1:: BuildDlcFundingTransaction: qln/dlc.go: our_fee: 10080 + # ::lit1:: BuildDlcFundingTransaction: qln/dlc.go: their_fee: 10080 + + # Vsize from Blockchain: 252 + + # So we expect lit1, and lit2 balances equal to 89989920 !!! + # 90000000 - 89989920 = 10080 + # But this is only when peers have one input each. What we expect. + + #----------------------------- + # 2) SettlementTx vsize will be printed + + #::lit1:: SettlementTx()1: qln/dlclib.go: --------------------: + #::lit1:: SettlementTx()1: qln/dlclib.go: valueOurs: 6000000 + #::lit1:: SettlementTx()1: qln/dlclib.go: valueTheirs: 14000000 + #::lit1:: SettlementTx()1: qln/dlclib.go: --------------------: + #::lit1:: SettlementTx()2: qln/dlclib.go: --------------------: + #::lit1:: SettlementTx()2: qln/dlclib.go: valueOurs: 6000000 + #::lit1:: SettlementTx()2: qln/dlclib.go: valueTheirs: 14000000 + #::lit1:: SettlementTx()2: qln/dlclib.go: --------------------: + #::lit1:: SettlementTx()3: qln/dlclib.go: --------------------: + #::lit1:: SettlementTx()3: qln/dlclib.go: totalFee: 14400 + #::lit1:: SettlementTx()3: qln/dlclib.go: feeEach: 7200 + #::lit1:: SettlementTx()3: qln/dlclib.go: feeOurs: 7200 + #::lit1:: SettlementTx()3: qln/dlclib.go: feeTheirs: 7200 + #::lit1:: SettlementTx()3: qln/dlclib.go: valueOurs: 5992800 + #::lit1:: SettlementTx()3: qln/dlclib.go: valueTheirs: 13992800 + #::lit1:: SettlementTx()3: qln/dlclib.go: vsize: 0 # Actually we have 14400/80 = 180 + #::lit1:: SettlementTx()3: qln/dlclib.go: --------------------: + + + # Vsize from Blockchain: 181 + + # There fore we expect here + # valueOurs: 5992800 = 6000000 - 7200 !!! + # valueTheirs: 13992800 = 14000000 - 7200 !!! + + + #----------------------------- + + # 3) Claim TX in SettleContract lit1 + # Here the transaction vsize is always the same: 121 + + + # Vsize from Blockchain: 121 + + #----------------------------- + + # 4) Claim TX from another peer lit0 + # Here the transaction vsize is always the same: 110 + + # Vsize from Blockchain: 110 + + #----------------------------- + + # 5992800 - (121 * 80) = 5983120 + # 89989920 + 5983120 = 95973040 + + # 13992800 - (110*80) = 13984000 + # 89989920 + 13984000 = 103973920 + + #----------------------------- + + # AFter Settle + # new lit1 balance: 103973920 in txos, 0 in chans + # = sum 103973920 + + + # new lit2 balance: 95973040 in txos, 0 in chans + # = sum 95973040 + + #----------------------------- + + oracle_value = 1300 + node_to_settle = 1 + + valueFullyOurs=1000 + valueFullyTheirs=2000 + + lit_funding_amt = 1 # 1 BTC + contract_funding_amt = 10000000 # satoshi + + FundingTxVsize = 252 + SettlementTxVsize = 181 + SettleContractClaimTxVsize = 110 + PeerClaimTxVsize = 121 + + SetTxFeeOurs = 7200 + SetTxFeeTheirs = 7200 + + ClaimTxFeeOurs = 110 * 80 + ClaimTxFeeTheirs = 121 * 80 + + + feeperbyte = 80 + + + vsizes = [FundingTxVsize, SettlementTxVsize, SettleContractClaimTxVsize, PeerClaimTxVsize] + + params = [lit_funding_amt, contract_funding_amt, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] + + run_t(env, params) + + + +# ==================================================================================== +# ==================================================================================== + + + +def t_10_0(env): + + + oracle_value = 10 + node_to_settle = 0 + + valueFullyOurs=10 + valueFullyTheirs=20 + + lit_funding_amt = 1 # 1 BTC + contract_funding_amt = 10000000 # satoshi + + FundingTxVsize = 252 + SettlementTxVsize = 181 + + SetTxFeeOurs = 11920 + SetTxFeeTheirs = 0 + + ClaimTxFeeOurs = 121 * 80 + ClaimTxFeeTheirs = 0 + + + feeperbyte = 80 + + + vsizes = [FundingTxVsize, SettlementTxVsize] + + params = [lit_funding_amt, contract_funding_amt, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] + + run_t(env, params) + + + +# ==================================================================================== +# ==================================================================================== + + + +def t_20_0(env): + + + oracle_value = 20 + node_to_settle = 0 + + valueFullyOurs=10 + valueFullyTheirs=20 + + lit_funding_amt = 1 # 1 BTC + contract_funding_amt = 10000000 # satoshi + + FundingTxVsize = 252 + SettlementTxVsize = 181 + + SetTxFeeOurs = 0 + SetTxFeeTheirs = 10960 + + ClaimTxFeeOurs = 0 + ClaimTxFeeTheirs = 110 * 80 + + + feeperbyte = 80 + + + vsizes = [FundingTxVsize, SettlementTxVsize] + + params = [lit_funding_amt, contract_funding_amt, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] + + run_t(env, params) \ No newline at end of file diff --git a/test/itest_send.py b/test/itest_send.py index 780ddb93b..fcc896f1f 100644 --- a/test/itest_send.py +++ b/test/itest_send.py @@ -38,5 +38,5 @@ def run_test(env): # Validate. bal2 = bc.rpc.getbalance() print('bitcoind balance:', bal0, '->', bal1, '->', bal2, '(', bal2 - bal1, ')') - if bal2 != bal1 + 12.5 + 0.5: # the 12.5 is because we mined a block + if float(bal2) != float(bal1) + 12.5 + 0.5: # the 12.5 is because we mined a block raise AssertionError("Balance in bitcoind doesn't match what we think it should be!") diff --git a/test/runtests.py b/test/runtests.py index 441c6d656..2f1303e1b 100755 --- a/test/runtests.py +++ b/test/runtests.py @@ -73,7 +73,7 @@ def load_tests_from_file(path): 'name': tname, 'pretty_name': pretty, 'test_func': tfn, - 'node_cnt': t['node_cnt'] + 'node_cnt': t['node_cnt'], }) return tests diff --git a/test/testlib.py b/test/testlib.py index e1d55757f..cf036b19f 100644 --- a/test/testlib.py +++ b/test/testlib.py @@ -6,12 +6,21 @@ import logging import random import shutil +import json import testutil import btcrpc import litrpc +import requests -LIT_BIN = "%s/../lit" % paths.abspath(paths.dirname(__file__)) + +from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException + + +# The dlcoracle binary must be accessible throught a PATH variable. + +#LIT_BIN = "%s/../lit" % paths.abspath(paths.dirname(__file__)) +LIT_BIN = "/home/andriy/Documents/go_lit/dev/lit" REGTEST_COINTYPE = 257 @@ -53,6 +62,8 @@ def get_new_id(): next_id += 1 return id + + class LitNode(): def __init__(self, bcnode): self.bcnode = bcnode @@ -88,7 +99,7 @@ def start(self): # Now figure out the args to use and then start Lit. args = [ LIT_BIN, - "-vv", + #"-vv", "--reg", "127.0.0.1:" + str(self.bcnode.p2p_port), "--tn3", "", # disable autoconnect "--dir", self.data_dir, @@ -119,6 +130,7 @@ def get_sync_height(self): for bal in self.rpc.balance(): if bal['CoinType'] == REGTEST_COINTYPE: return bal['SyncHeight'] + print("return -1") return -1 def connect_to_peer(self, other): @@ -194,15 +206,19 @@ def __init__(self): "-rpcuser=rpcuser", "-rpcpassword=rpcpass", "-rpcport=" + str(self.rpc_port), + "-txindex" ] + self.proc = subprocess.Popen(args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + # Make the RPC client for it. testutil.wait_until_port("localhost", self.rpc_port) testutil.wait_until_port("localhost", self.p2p_port) - self.rpc = btcrpc.BtcClient("localhost", self.rpc_port, "rpcuser", "rpcpass") + + self.rpc = AuthServiceProxy("http://%s:%s@127.0.0.1:%s"%("rpcuser", "rpcpass", self.rpc_port), timeout=240) # Make sure that we're actually ready to accept RPC calls. def ck_ready(): @@ -234,10 +250,85 @@ def shutdown(self): else: pass # do nothing I guess? +class OracleNode(): + def __init__(self, interval, valueToPublish): + + self.data_dir = new_data_dir("oracle") + self.httpport = str(new_port()) + self.interval = str(interval) + self.valueToPublish = str(valueToPublish) + + # Write a hexkey to the privkey file + with open(paths.join(self.data_dir, "privkey.hex"), 'w+') as f: + s = '' + for _ in range(192): + s += hexchars[random.randint(0, len(hexchars) - 1)] + print('Using key:', s) + f.write(s + "\n") + + self.start() + + + def start(self): + + # See if we should print stdout + outputredir = subprocess.DEVNULL + ev_output_show = os.getenv("ORACLE_OUTPUT_SHOW", default="0") + if ev_output_show == "1": + outputredir = None + + # Now figure out the args to use and then start Lit. + args = [ + "dlcoracle", + "--DataDir="+self.data_dir, + "--HttpPort=" + self.httpport, + "--Interval=" + self.interval, + "--ValueToPublish=" + self.valueToPublish, + ] + + penv = os.environ.copy() + + self.proc = subprocess.Popen(args, + stdin=subprocess.DEVNULL, + stdout=outputredir, + stderr=outputredir, + env=penv) + + def shutdown(self): + if self.proc is not None: + self.proc.kill() + self.proc.wait() + self.proc = None + else: + pass # do nothing I guess? + + shutil.rmtree(self.data_dir) + + def get_pubkey(self): + res = requests.get("http://localhost:"+self.httpport+"/api/pubkey") + return res.text + + def get_datasources(self): + res = requests.get("http://localhost:"+self.httpport+"/api/datasources") + return res.text + + + def get_rpoint(self, datasourceID, unixTime): + res = requests.get("http://localhost:"+self.httpport+"/api/rpoint/" + str(datasourceID) + "/" + str(unixTime)) + print("get_rpoint:", "http://localhost:"+self.httpport+"/api/rpoint/" + str(datasourceID) + "/" + str(unixTime)) + print(res.text) + return res.text + + def get_publication(self, rpoint): + res = requests.get("http://localhost:"+self.httpport+"/api/publication/" + rpoint) + return res.text + + class TestEnv(): def __init__(self, litcnt): logger.info("starting nodes...") self.bitcoind = BitcoinNode() + self.oracles = [] self.lits = [] for i in range(litcnt): node = LitNode(self.bitcoind) @@ -260,6 +351,12 @@ def new_lit_node(self): self.generate_block(count=0) # Force it to wait for sync. return node + def new_oracle(self, interval, valueToPublish): + oracle = OracleNode(interval, valueToPublish) + self.oracles.append(oracle) + return oracle + + def generate_block(self, count=1): if count > 0: self.bitcoind.rpc.generate(count) @@ -279,6 +376,8 @@ def shutdown(self): for l in self.lits: l.shutdown() self.bitcoind.shutdown() + for o in self.oracles: + o.shutdown() def clean_data_dir(): datadir = get_root_data_dir() diff --git a/test/tests.txt b/test/tests.txt index 08fc62242..2da1aa0c3 100644 --- a/test/tests.txt +++ b/test/tests.txt @@ -23,3 +23,18 @@ pushbreak 2 forward pushbreak 2 reverse pushclose 2 forward pushclose 2 reverse + +# DLC subsystem tx sizes calculation, etc... + +# regular contract +dlc 2 t_11_0 + +# large contract to test updeted messaging subsystem +# and also to test settlement from counterparty +dlc 2 t_1300_1 + +# test at left edge +dlc 2 t_10_0 + +# test at right edge +dlc 2 t_20_0 From 86a9164bdf12118d3b9332e3c194f4694e5d6485 Mon Sep 17 00:00:00 2001 From: infografx Date: Mon, 22 Jul 2019 23:10:14 +0300 Subject: [PATCH 09/24] Fix --- lnutil/dlclib.go | 1 - 1 file changed, 1 deletion(-) diff --git a/lnutil/dlclib.go b/lnutil/dlclib.go index d4c17f5b7..02e991410 100644 --- a/lnutil/dlclib.go +++ b/lnutil/dlclib.go @@ -7,7 +7,6 @@ import ( "fmt" "math/big" "errors" - "os" "github.com/mit-dci/lit/btcutil/chaincfg/chainhash" "github.com/mit-dci/lit/crypto/koblitz" From b8ede08afec791cc62aca6753be8992f271845d1 Mon Sep 17 00:00:00 2001 From: infografx Date: Tue, 23 Jul 2019 00:14:17 +0300 Subject: [PATCH 10/24] Fix. --- lnp2p/msgproc.go | 1 - qln/dlc.go | 3 --- qln/msghandler.go | 1 - 3 files changed, 5 deletions(-) diff --git a/lnp2p/msgproc.go b/lnp2p/msgproc.go index b7d210451..3df578fe8 100644 --- a/lnp2p/msgproc.go +++ b/lnp2p/msgproc.go @@ -2,7 +2,6 @@ package lnp2p import ( "fmt" - "os" "github.com/mit-dci/lit/logging" "sync" "github.com/mit-dci/lit/lnutil" diff --git a/qln/dlc.go b/qln/dlc.go index f8c316661..40e4e7b5b 100644 --- a/qln/dlc.go +++ b/qln/dlc.go @@ -2,9 +2,6 @@ package qln import ( "fmt" - "os" - // "bytes" - // "bufio" "github.com/mit-dci/lit/btcutil" "github.com/mit-dci/lit/btcutil/txscript" diff --git a/qln/msghandler.go b/qln/msghandler.go index 846cbb2f0..88b1aa35f 100644 --- a/qln/msghandler.go +++ b/qln/msghandler.go @@ -3,7 +3,6 @@ package qln import ( "bytes" "fmt" - "os" "github.com/mit-dci/lit/logging" From bbff3190d66172f805baa57f5f67adf58dc0aee2 Mon Sep 17 00:00:00 2001 From: infografx Date: Thu, 25 Jul 2019 13:49:21 +0300 Subject: [PATCH 11/24] Fix. --- test/testlib.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/testlib.py b/test/testlib.py index cf036b19f..3a755a599 100644 --- a/test/testlib.py +++ b/test/testlib.py @@ -19,8 +19,7 @@ # The dlcoracle binary must be accessible throught a PATH variable. -#LIT_BIN = "%s/../lit" % paths.abspath(paths.dirname(__file__)) -LIT_BIN = "/home/andriy/Documents/go_lit/dev/lit" +LIT_BIN = "%s/../lit" % paths.abspath(paths.dirname(__file__)) REGTEST_COINTYPE = 257 From 238c50fece171c1b5f7d48bf6895f9592c342416 Mon Sep 17 00:00:00 2001 From: infografx Date: Thu, 25 Jul 2019 17:01:00 +0300 Subject: [PATCH 12/24] Remove import btcrpc --- test/testlib.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/testlib.py b/test/testlib.py index 3a755a599..2be516382 100644 --- a/test/testlib.py +++ b/test/testlib.py @@ -9,7 +9,6 @@ import json import testutil -import btcrpc import litrpc import requests From 502803e13aa8f91ddab81e053fcf5d91984dceac Mon Sep 17 00:00:00 2001 From: infografx Date: Thu, 25 Jul 2019 21:35:23 +0300 Subject: [PATCH 13/24] The test for milestone2 is completed. --- test/itest_dlc.py | 83 ++++++++++++++++++++++++++++++++++++++++++++++- test/tests.txt | 6 ++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/test/itest_dlc.py b/test/itest_dlc.py index 5a0cd634a..32d5ae2e7 100644 --- a/test/itest_dlc.py +++ b/test/itest_dlc.py @@ -748,6 +748,46 @@ def t_10_0(env): +def t_10_1(env): + + + oracle_value = 10 + node_to_settle = 1 + + valueFullyOurs=10 + valueFullyTheirs=20 + + lit_funding_amt = 1 # 1 BTC + contract_funding_amt = 10000000 # satoshi + + FundingTxVsize = 252 + SettlementTxVsize = 137 + + + + SetTxFeeOurs = 10960 + SetTxFeeTheirs = 0 + + ClaimTxFeeOurs = 110 * 80 + ClaimTxFeeTheirs = 0 + + + feeperbyte = 80 + + + vsizes = [FundingTxVsize, SettlementTxVsize] + + params = [lit_funding_amt, contract_funding_amt, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] + + run_t(env, params) + + + +# ==================================================================================== +# ==================================================================================== + + + def t_20_0(env): @@ -777,4 +817,45 @@ def t_20_0(env): params = [lit_funding_amt, contract_funding_amt, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] - run_t(env, params) \ No newline at end of file + run_t(env, params) + + + +# ==================================================================================== +# ==================================================================================== + + + +def t_20_1(env): + + + oracle_value = 20 + node_to_settle = 1 + + valueFullyOurs=10 + valueFullyTheirs=20 + + lit_funding_amt = 1 # 1 BTC + contract_funding_amt = 10000000 # satoshi + + FundingTxVsize = 252 + SettlementTxVsize = 149 + + + + SetTxFeeOurs = 0 + SetTxFeeTheirs = 11920 + + ClaimTxFeeOurs = 0 + ClaimTxFeeTheirs = 121 * 80 + + + feeperbyte = 80 + + + vsizes = [FundingTxVsize, SettlementTxVsize] + + params = [lit_funding_amt, contract_funding_amt, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] + + run_t(env, params) + \ No newline at end of file diff --git a/test/tests.txt b/test/tests.txt index 2da1aa0c3..bf0ed801a 100644 --- a/test/tests.txt +++ b/test/tests.txt @@ -36,5 +36,11 @@ dlc 2 t_1300_1 # test at left edge dlc 2 t_10_0 +# test at left edge from the counterparty +dlc 2 t_10_1 + # test at right edge dlc 2 t_20_0 + +# test at right edge from the counterparty +dlc 2 t_20_1 From d2020b6ece7aa623f2316cdb95b7cd1aae290cd2 Mon Sep 17 00:00:00 2001 From: infografx Date: Fri, 26 Jul 2019 14:06:39 +0300 Subject: [PATCH 14/24] Fix. --- lnutil/dlclib.go | 6 +-- test/itest_dlc.py | 104 +++++++++++++++++++++------------------------- 2 files changed, 50 insertions(+), 60 deletions(-) diff --git a/lnutil/dlclib.go b/lnutil/dlclib.go index 02e991410..83c5c4a64 100644 --- a/lnutil/dlclib.go +++ b/lnutil/dlclib.go @@ -493,7 +493,7 @@ func SettlementTx(c *DlcContract, d DlcContractDivision, } - vsize :=uint32(0) + vsize :=uint32(maxVsize) // We don't have enough to pay for a fee. We get 0, our contract partner // pays the rest of the fee @@ -503,7 +503,7 @@ func SettlementTx(c *DlcContract, d DlcContractDivision, if ours { // exclude wire.NewTxOut from size (i.e 31) - vsize = uint32(149) + vsize = uint32(150) }else{ // exclude DlcOutput from size (i.e 43) vsize = uint32(137) @@ -532,7 +532,7 @@ func SettlementTx(c *DlcContract, d DlcContractDivision, if ours { vsize = uint32(137) }else{ - vsize = uint32(149) + vsize = uint32(150) } totalFee = vsize * c.FeePerByte diff --git a/test/itest_dlc.py b/test/itest_dlc.py index 32d5ae2e7..7cfc66975 100644 --- a/test/itest_dlc.py +++ b/test/itest_dlc.py @@ -381,23 +381,12 @@ def run_t(env, params): print("ORACLE VALUE:", oracle1_val, "; oracle signature:", oracle1_sig) valueOurs = 0 - if node_to_settle == 0: - - valueOurs = env.lits[node_to_settle].rpc.GetContractDivision(CIdx=contract["Contract"]["Idx"],OracleValue=oracle1_val)['ValueOurs'] - valueTheirs = contract_funding_amt * 2 - valueOurs - - print("valueOurs:", valueOurs, "; valueTheirs:", valueTheirs) - - - elif node_to_settle == 1: - - valueTheirs = env.lits[node_to_settle].rpc.GetContractDivision(CIdx=contract["Contract"]["Idx"],OracleValue=oracle1_val)['ValueOurs'] - valueOurs = contract_funding_amt * 2 - valueTheirs + - print("valueOurs:", valueOurs, "; valueTheirs:", valueTheirs) + valueOurs = env.lits[node_to_settle].rpc.GetContractDivision(CIdx=contract["Contract"]["Idx"],OracleValue=oracle1_val)['ValueOurs'] + valueTheirs = contract_funding_amt * 2 - valueOurs - else: - assert False, "Unknown node index." + print("valueOurs:", valueOurs, "; valueTheirs:", valueTheirs) lit1_bal_after_settle = valueOurs - SetTxFeeOurs @@ -436,10 +425,12 @@ def run_t(env, params): print(bals2) print(' = sum ', bal2sum) - - assert bal1sum == lit1_bal_result, "The resulting lit1 node balance does not match." - assert bal2sum == lit2_bal_result, "The resulting lit2 node balance does not match." - + if node_to_settle == 0: + assert bal1sum == lit1_bal_result, "The resulting lit1 node balance does not match." + assert bal2sum == lit2_bal_result, "The resulting lit2 node balance does not match." + elif node_to_settle == 1: + assert bal1sum == lit2_bal_result, "The resulting lit1 node balance does not match." + assert bal2sum == lit1_bal_result, "The resulting lit2 node balance does not match." @@ -568,7 +559,7 @@ def t_11_0(env): contract_funding_amt = 10000000 # satoshi FundingTxVsize = 252 - SettlementTxVsize = 181 + SettlementTxVsize = 180 SetTxFeeOurs = 7200 SetTxFeeTheirs = 7200 @@ -612,23 +603,23 @@ def t_1300_1(env): #----------------------------- # 2) SettlementTx vsize will be printed - #::lit1:: SettlementTx()1: qln/dlclib.go: --------------------: - #::lit1:: SettlementTx()1: qln/dlclib.go: valueOurs: 6000000 - #::lit1:: SettlementTx()1: qln/dlclib.go: valueTheirs: 14000000 - #::lit1:: SettlementTx()1: qln/dlclib.go: --------------------: - #::lit1:: SettlementTx()2: qln/dlclib.go: --------------------: - #::lit1:: SettlementTx()2: qln/dlclib.go: valueOurs: 6000000 - #::lit1:: SettlementTx()2: qln/dlclib.go: valueTheirs: 14000000 - #::lit1:: SettlementTx()2: qln/dlclib.go: --------------------: - #::lit1:: SettlementTx()3: qln/dlclib.go: --------------------: - #::lit1:: SettlementTx()3: qln/dlclib.go: totalFee: 14400 - #::lit1:: SettlementTx()3: qln/dlclib.go: feeEach: 7200 - #::lit1:: SettlementTx()3: qln/dlclib.go: feeOurs: 7200 - #::lit1:: SettlementTx()3: qln/dlclib.go: feeTheirs: 7200 - #::lit1:: SettlementTx()3: qln/dlclib.go: valueOurs: 5992800 - #::lit1:: SettlementTx()3: qln/dlclib.go: valueTheirs: 13992800 - #::lit1:: SettlementTx()3: qln/dlclib.go: vsize: 0 # Actually we have 14400/80 = 180 - #::lit1:: SettlementTx()3: qln/dlclib.go: --------------------: + # ::lit1:: SettlementTx()1: qln/dlclib.go: --------------------: + # ::lit1:: SettlementTx()1: qln/dlclib.go: valueOurs: 6000000 + # ::lit1:: SettlementTx()1: qln/dlclib.go: valueTheirs: 14000000 + # ::lit1:: SettlementTx()1: qln/dlclib.go: --------------------: + # ::lit1:: SettlementTx()2: qln/dlclib.go: --------------------: + # ::lit1:: SettlementTx()2: qln/dlclib.go: valueOurs: 5992800 + # ::lit1:: SettlementTx()2: qln/dlclib.go: valueTheirs: 13992800 + # ::lit1:: SettlementTx()2: qln/dlclib.go: --------------------: + # ::lit1:: SettlementTx()2: qln/dlclib.go: --------------------: + # ::lit1:: SettlementTx()2: qln/dlclib.go: totalFee: 14400 + # ::lit1:: SettlementTx()2: qln/dlclib.go: feeEach: 7200 + # ::lit1:: SettlementTx()2: qln/dlclib.go: feeOurs: 7200 + # ::lit1:: SettlementTx()2: qln/dlclib.go: feeTheirs: 7200 + # ::lit1:: SettlementTx()2: qln/dlclib.go: valueOurs: 5992800 + # ::lit1:: SettlementTx()2: qln/dlclib.go: valueTheirs: 13992800 + # ::lit1:: SettlementTx()2: qln/dlclib.go: vsize: 180 + # ::lit1:: SettlementTx()2: qln/dlclib.go: --------------------: # Vsize from Blockchain: 181 @@ -683,21 +674,20 @@ def t_1300_1(env): contract_funding_amt = 10000000 # satoshi FundingTxVsize = 252 - SettlementTxVsize = 181 - SettleContractClaimTxVsize = 110 - PeerClaimTxVsize = 121 + SettlementTxVsize = 180 + SetTxFeeOurs = 7200 SetTxFeeTheirs = 7200 - ClaimTxFeeOurs = 110 * 80 - ClaimTxFeeTheirs = 121 * 80 + ClaimTxFeeOurs = 121 * 80 + ClaimTxFeeTheirs = 110 * 80 feeperbyte = 80 - vsizes = [FundingTxVsize, SettlementTxVsize, SettleContractClaimTxVsize, PeerClaimTxVsize] + vsizes = [FundingTxVsize, SettlementTxVsize] params = [lit_funding_amt, contract_funding_amt, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] @@ -723,9 +713,9 @@ def t_10_0(env): contract_funding_amt = 10000000 # satoshi FundingTxVsize = 252 - SettlementTxVsize = 181 + SettlementTxVsize = 150 - SetTxFeeOurs = 11920 + SetTxFeeOurs = 150 * 80 SetTxFeeTheirs = 0 ClaimTxFeeOurs = 121 * 80 @@ -765,11 +755,11 @@ def t_10_1(env): - SetTxFeeOurs = 10960 - SetTxFeeTheirs = 0 + SetTxFeeOurs = 0 + SetTxFeeTheirs = 137 * 80 - ClaimTxFeeOurs = 110 * 80 - ClaimTxFeeTheirs = 0 + ClaimTxFeeOurs = 0 + ClaimTxFeeTheirs = 110 * 80 feeperbyte = 80 @@ -801,10 +791,10 @@ def t_20_0(env): contract_funding_amt = 10000000 # satoshi FundingTxVsize = 252 - SettlementTxVsize = 181 + SettlementTxVsize = 137 SetTxFeeOurs = 0 - SetTxFeeTheirs = 10960 + SetTxFeeTheirs = 137 * 80 ClaimTxFeeOurs = 0 ClaimTxFeeTheirs = 110 * 80 @@ -821,6 +811,7 @@ def t_20_0(env): + # ==================================================================================== # ==================================================================================== @@ -839,15 +830,15 @@ def t_20_1(env): contract_funding_amt = 10000000 # satoshi FundingTxVsize = 252 - SettlementTxVsize = 149 + SettlementTxVsize = 150 - SetTxFeeOurs = 0 - SetTxFeeTheirs = 11920 + SetTxFeeOurs = 150 * 80 + SetTxFeeTheirs = 0 - ClaimTxFeeOurs = 0 - ClaimTxFeeTheirs = 121 * 80 + ClaimTxFeeOurs = 121 * 80 + ClaimTxFeeTheirs = 0 feeperbyte = 80 @@ -858,4 +849,3 @@ def t_20_1(env): params = [lit_funding_amt, contract_funding_amt, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] run_t(env, params) - \ No newline at end of file From 73295c75d8ba1d80c85624120dd13a5f62a79934 Mon Sep 17 00:00:00 2001 From: infografx Date: Fri, 26 Jul 2019 19:22:35 +0300 Subject: [PATCH 15/24] Add FeePerByte to the lit-af utility. --- cmd/lit-af/dlccmds.go | 57 +++++++++++++++++++++++++++++++++++++++++++ dlc/contract.go | 24 ++---------------- litrpc/dlccmds.go | 4 +-- lnutil/dlclib.go | 2 +- 4 files changed, 61 insertions(+), 26 deletions(-) diff --git a/cmd/lit-af/dlccmds.go b/cmd/lit-af/dlccmds.go index 943806efd..e9201d124 100644 --- a/cmd/lit-af/dlccmds.go +++ b/cmd/lit-af/dlccmds.go @@ -117,6 +117,9 @@ var contractCommand = &Command{ fmt.Sprintf("%-20s %s", lnutil.White("setcointype"), "Sets the cointype of a contract"), + fmt.Sprintf("%-20s %s", + lnutil.White("setfeeperbyte"), + "Sets the fee per byte for a contract"), fmt.Sprintf("%-20s %s", lnutil.White("offer"), "Offer a draft contract to one of your peers"), @@ -289,6 +292,20 @@ var setContractCoinTypeCommand = &Command{ ), ShortDescription: "Sets the coin type to use for the contract\n", } +var setContractFeePerByteCommand = &Command{ + Format: fmt.Sprintf("%s%s\n", lnutil.White("dlc contract setfeeperbyte"), + lnutil.ReqColor("cid", "feeperbyte")), + Description: fmt.Sprintf("%s\n%s\n%s\n", + "Sets the fee per byte to use for the contract", + fmt.Sprintf("%-10s %s", + lnutil.White("cid"), + "The ID of the contract"), + fmt.Sprintf("%-10s %s", + lnutil.White("cointype"), + "The fee per byte in satoshi to use for the contract"), + ), + ShortDescription: "Sets the fee per byte in satoshi to use for the contract\n", +} var declineContractCommand = &Command{ Format: fmt.Sprintf("%s%s\n", lnutil.White("dlc contract decline"), lnutil.ReqColor("cid")), @@ -504,6 +521,10 @@ func (lc *litAfClient) DlcContract(textArgs []string) error { return lc.DlcSetContractCoinType(textArgs) } + if cmd == "setfeeperbyte" { + return lc.DlcSetContractFeePerByte(textArgs) + } + if cmd == "offer" { return lc.DlcOfferContract(textArgs) } @@ -803,6 +824,40 @@ func (lc *litAfClient) DlcSetContractCoinType(textArgs []string) error { return nil } + +func (lc *litAfClient) DlcSetContractFeePerByte(textArgs []string) error { + stopEx, err := CheckHelpCommand(setContractFeePerByteCommand, textArgs, 2) + if err != nil || stopEx { + return err + } + + args := new(litrpc.SetContractFeePerByteArgs) + reply := new(litrpc.SetContractFeePerByteReply) + + cIdx, err := strconv.ParseUint(textArgs[0], 10, 64) + if err != nil { + return err + } + feeperbyte, err := strconv.ParseUint(textArgs[1], 10, 64) + if err != nil { + return err + } + + args.CIdx = cIdx + args.FeePerByte = uint32(feeperbyte) + + err = lc.Call("LitRPC.SetContractFeePerByte", args, reply) + if err != nil { + return err + } + + fmt.Fprint(color.Output, "Fee per byte set successfully\n") + + return nil +} + + + func (lc *litAfClient) DlcSetContractDivision(textArgs []string) error { stopEx, err := CheckHelpCommand(setContractDivisionCommand, textArgs, 3) if err != nil || stopEx { @@ -969,6 +1024,8 @@ func PrintContract(c *lnutil.DlcContract) { lnutil.White("Funded by peer"), c.TheirFundingAmount) fmt.Fprintf(color.Output, "%-30s : %d\n", lnutil.White("Coin type"), c.CoinType) + fmt.Fprintf(color.Output, "%-30s : %d\n", + lnutil.White("Fee per byte"), c.FeePerByte) peer := "None" if c.PeerIdx > 0 { diff --git a/dlc/contract.go b/dlc/contract.go index 492f94217..86ef2674f 100644 --- a/dlc/contract.go +++ b/dlc/contract.go @@ -16,6 +16,7 @@ func (mgr *DlcManager) AddContract() (*lnutil.DlcContract, error) { c := new(lnutil.DlcContract) c.Status = lnutil.ContractStatusDraft c.CoinType = COINTYPE_NOT_SET + c.FeePerByte = FEEPERBYTE_NOT_SET err = mgr.SaveContract(c) if err != nil { return nil, err @@ -255,7 +256,7 @@ func (mgr *DlcManager) SetContractFeePerByte(cIdx uint64, feeperbyte uint32) err } if c.Status != lnutil.ContractStatusDraft { - return fmt.Errorf("You cannot change or set the coin type unless" + + return fmt.Errorf("You cannot change or set the fee per byte unless" + " the contract is in Draft state") } @@ -265,24 +266,3 @@ func (mgr *DlcManager) SetContractFeePerByte(cIdx uint64, feeperbyte uint32) err return nil } - - -// //GetContractDivision -// GetContractDivision(args.CIdx, args.OracleValue) -// func (mgr *DlcManager) GetContractDivision(cIdx uint64, feeperbyte uint32) error { -// c, err := mgr.LoadContract(cIdx) -// if err != nil { -// return err -// } - -// if c.Status != lnutil.ContractStatusDraft { -// return fmt.Errorf("You cannot change or set the coin type unless" + -// " the contract is in Draft state") -// } - -// c.FeePerByte = feeperbyte - -// mgr.SaveContract(c) - -// return nil -// } diff --git a/litrpc/dlccmds.go b/litrpc/dlccmds.go index 001928369..0c9e6ab50 100644 --- a/litrpc/dlccmds.go +++ b/litrpc/dlccmds.go @@ -325,9 +325,7 @@ type SetContractFeePerByteReply struct { Success bool } -// SetContractFeePerByte sets the coin type the contract will be in. Note that a -// peer that doesn't have a wallet of that type will automatically decline the -// contract. +// SetContractFeePerByte sets the fee per byte for the contract. func (r *LitRPC) SetContractFeePerByte(args SetContractFeePerByteArgs, reply *SetContractFeePerByteReply) error { var err error diff --git a/lnutil/dlclib.go b/lnutil/dlclib.go index 83c5c4a64..826ceca3b 100644 --- a/lnutil/dlclib.go +++ b/lnutil/dlclib.go @@ -130,7 +130,7 @@ func DlcContractFromBytes(b []byte) (*DlcContract, error) { feePerByte, err := wire.ReadVarInt(buf, 0) if err != nil { - logging.Errorf("Error while deserializing varint for coinType: %s", err.Error()) + logging.Errorf("Error while deserializing varint for feePerByte: %s", err.Error()) return nil, err } c.FeePerByte = uint32(feePerByte) From bc28dc95e4885100c71396f1f8ae9473dafaf6ea Mon Sep 17 00:00:00 2001 From: infografx Date: Tue, 17 Sep 2019 15:48:31 +0300 Subject: [PATCH 16/24] Merge branch 'master-mit-lit_milestone2-test_wos' into master-mit-lit_dlcrefund DlcRefundTx created. --- .gitignore | 1 + cmd/lit-af/dlccmds.go | 56 +++++ dlc/contract.go | 28 ++- docs/execute-dlc-litaf.md | 7 + litrpc/dlccmds.go | 48 +++- lnutil/dlclib.go | 88 +++++++- lnutil/msglib.go | 35 ++- qln/dlc.go | 126 ++++++++++- qln/magicnums.go | 4 +- test/itest_dlc.py | 80 ++----- test/itest_dlcrefund.py | 455 ++++++++++++++++++++++++++++++++++++++ test/testlib.py | 5 +- test/tests.txt | 3 + 13 files changed, 845 insertions(+), 91 deletions(-) create mode 100644 test/itest_dlcrefund.py diff --git a/.gitignore b/.gitignore index 902c32b19..c4a5e8d9c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Lit stuff lit +dlcoracle lit-af !cmd/lit-af cmd/lit-af/lit-af diff --git a/cmd/lit-af/dlccmds.go b/cmd/lit-af/dlccmds.go index e9201d124..861751892 100644 --- a/cmd/lit-af/dlccmds.go +++ b/cmd/lit-af/dlccmds.go @@ -102,6 +102,9 @@ var contractCommand = &Command{ fmt.Sprintf("%-20s %s", lnutil.White("settime"), "Sets the settlement time of a contract"), + fmt.Sprintf("%-20s %s", + lnutil.White("setrefundtime"), + "Sets the refund time of a contract"), fmt.Sprintf("%-20s %s", lnutil.White("setdatafeed"), "Sets the data feed to use, will fetch the R point"), @@ -241,6 +244,23 @@ var setContractSettlementTimeCommand = &Command{ ), ShortDescription: "Sets the settlement time for the contract\n", } + + +var setContractRefundTimeCommand = &Command{ + Format: fmt.Sprintf("%s%s\n", lnutil.White("dlc contract settime"), + lnutil.ReqColor("cid", "time")), + Description: fmt.Sprintf("%s\n%s\n%s\n", + "Sets the refund time for the contract", + fmt.Sprintf("%-10s %s", + lnutil.White("cid"), + "The ID of the contract"), + fmt.Sprintf("%-10s %s", + lnutil.White("time"), + "The refund time (unix timestamp)"), + ), + ShortDescription: "Sets the settlement time for the contract\n", +} + var setContractFundingCommand = &Command{ Format: fmt.Sprintf("%s%s\n", lnutil.White("dlc contract setfunding"), lnutil.ReqColor("cid", "ourAmount", "theirAmount")), @@ -509,6 +529,10 @@ func (lc *litAfClient) DlcContract(textArgs []string) error { return lc.DlcSetContractSettlementTime(textArgs) } + if cmd == "setrefundtime" { + return lc.DlcSetContractRefundTime(textArgs) + } + if cmd == "setfunding" { return lc.DlcSetContractFunding(textArgs) } @@ -758,6 +782,38 @@ func (lc *litAfClient) DlcSetContractSettlementTime(textArgs []string) error { return nil } +func (lc *litAfClient) DlcSetContractRefundTime(textArgs []string) error { + stopEx, err := CheckHelpCommand(setContractRefundTimeCommand, textArgs, 2) + if err != nil || stopEx { + return err + } + + args := new(litrpc.SetContractSettlementTimeArgs) + reply := new(litrpc.SetContractSettlementTimeReply) + + cIdx, err := strconv.ParseUint(textArgs[0], 10, 64) + if err != nil { + return err + } + time, err := strconv.ParseUint(textArgs[1], 10, 64) + if err != nil { + return err + } + args.CIdx = cIdx + args.Time = time + + err = lc.Call("LitRPC.SetContractRefundTime", args, reply) + if err != nil { + return err + } + + fmt.Fprint(color.Output, "Refund time set successfully\n") + + return nil +} + + + func (lc *litAfClient) DlcSetContractFunding(textArgs []string) error { stopEx, err := CheckHelpCommand(setContractFundingCommand, textArgs, 3) if err != nil || stopEx { diff --git a/dlc/contract.go b/dlc/contract.go index 86ef2674f..7ffb58f86 100644 --- a/dlc/contract.go +++ b/dlc/contract.go @@ -34,24 +34,18 @@ func (mgr *DlcManager) SetContractOracle(cIdx, oIdx uint64) error { if err != nil { return err } - if c.Status != lnutil.ContractStatusDraft { return fmt.Errorf("You cannot change or set the oracle unless the" + " contract is in Draft state") } - o, err := mgr.LoadOracle(oIdx) if err != nil { return err } - c.OracleA = o.A - // Reset the R point when changing the oracle c.OracleR = [33]byte{} - mgr.SaveContract(c) - return nil } @@ -62,22 +56,36 @@ func (mgr *DlcManager) SetContractSettlementTime(cIdx, time uint64) error { if err != nil { return err } - if c.Status != lnutil.ContractStatusDraft { return fmt.Errorf("You cannot change or set the settlement time" + " unless the contract is in Draft state") } - c.OracleTimestamp = time - // Reset the R point c.OracleR = [33]byte{} - mgr.SaveContract(c) + return nil +} + +// SetContractRefundTime. If until this time Oracle does not publish the data, +// then either party can publish a RefundTransaction +func (mgr *DlcManager) SetContractRefundTime(cIdx, time uint64) error { + c, err := mgr.LoadContract(cIdx) + if err != nil { + return err + } + if c.Status != lnutil.ContractStatusDraft { + return fmt.Errorf("You cannot change or set the settlement time" + + " unless the contract is in Draft state") + } + c.RefundTimestamp = time + mgr.SaveContract(c) return nil } + + // SetContractDatafeed will automatically fetch the R-point from the REST API, // if an oracle is imported from a REST API. You need to set the settlement time // first, because the R point is a key unique for the time and feed diff --git a/docs/execute-dlc-litaf.md b/docs/execute-dlc-litaf.md index 6a4cc53cc..45dc25ae0 100644 --- a/docs/execute-dlc-litaf.md +++ b/docs/execute-dlc-litaf.md @@ -64,6 +64,13 @@ Next, we configure the timestamp at which the contract will settle. This is a ti dlc contract settime 1 1528848000 ``` +Now we have to configure refund timestamp after which the refund transaction becomes valid. + +``` +dlc contract setrefundtime 1 1528848000 +``` +In this case the refund transaction becomes valid at the same time. + Then, we configure the R-point for the contract, as mentioned earlier this is the public key to the one-time signing key used by the oracle to sign the value it will publish. ``` diff --git a/litrpc/dlccmds.go b/litrpc/dlccmds.go index 0c9e6ab50..0dd2f7913 100644 --- a/litrpc/dlccmds.go +++ b/litrpc/dlccmds.go @@ -220,8 +220,8 @@ type SetContractSettlementTimeReply struct { Success bool } -// SetContractSettlementTime sets the time this contract will settle (the -// unix epoch) +// SetContractSettlementTime sets the time this the oracle will publish data ( +// unix time) func (r *LitRPC) SetContractSettlementTime(args SetContractSettlementTimeArgs, reply *SetContractSettlementTimeReply) error { var err error @@ -235,6 +235,24 @@ func (r *LitRPC) SetContractSettlementTime(args SetContractSettlementTimeArgs, return nil } + +// SetContractRefundTime. If until this time Oracle does not publish the data, +// then either party can publish a RefundTransaction +func (r *LitRPC) SetContractRefundTime(args SetContractSettlementTimeArgs, + reply *SetContractSettlementTimeReply) error { + var err error + + err = r.Node.DlcManager.SetContractRefundTime(args.CIdx, args.Time) + if err != nil { + return err + } + + reply.Success = true + return nil +} + + + type SetContractFundingArgs struct { CIdx uint64 OurAmount int64 @@ -457,3 +475,29 @@ func (r *LitRPC) SettleContract(args SettleContractArgs, reply.Success = true return nil } + + +//====================================================================== + + +type RefundContractArgs struct { + CIdx uint64 +} + +type RefundContractReply struct { + Success bool +} + +// RefundContract + +func (r *LitRPC) RefundContract(args RefundContractArgs,reply *RefundContractReply) error { + var err error + + reply.Success, err = r.Node.RefundContract(args.CIdx) + if err != nil { + return err + } + + reply.Success = true + return nil +} diff --git a/lnutil/dlclib.go b/lnutil/dlclib.go index 826ceca3b..db2d7da49 100644 --- a/lnutil/dlclib.go +++ b/lnutil/dlclib.go @@ -12,6 +12,11 @@ import ( "github.com/mit-dci/lit/crypto/koblitz" "github.com/mit-dci/lit/logging" "github.com/mit-dci/lit/wire" + + "github.com/mit-dci/lit/btcutil/txscript" + "github.com/mit-dci/lit/btcutil/txsort" + "github.com/mit-dci/lit/sig64" + ) // DlcContractStatus is an enumeration containing the various statuses a @@ -54,6 +59,8 @@ type DlcContract struct { OracleA, OracleR [33]byte // The time we expect the oracle to publish OracleTimestamp uint64 + // The time after which the refund transaction becomes valid. + RefundTimestamp uint64 // The payout specification Division []DlcContractDivision // The amounts either side are funding @@ -62,6 +69,9 @@ type DlcContract struct { OurChangePKH, TheirChangePKH [20]byte // Pubkey used in the funding multisig output OurFundMultisigPub, TheirFundMultisigPub [33]byte + //OurRevokePub, TheirRevokePub [33]byte + OurRevokePKH, TheirRevokePKH [20]byte + OurrefundTxSig64, TheirrefundTxSig64 [64]byte // Pubkey to be used in the commit script (combined with oracle pubkey // or CSV timeout) OurPayoutBase, TheirPayoutBase [33]byte @@ -139,6 +149,10 @@ func DlcContractFromBytes(b []byte) (*DlcContract, error) { if err != nil { return nil, err } + c.RefundTimestamp, err = wire.ReadVarInt(buf, 0) + if err != nil { + return nil, err + } ourFundingAmount, err := wire.ReadVarInt(buf, 0) if err != nil { return nil, err @@ -156,6 +170,12 @@ func DlcContractFromBytes(b []byte) (*DlcContract, error) { copy(c.OurFundMultisigPub[:], buf.Next(33)) copy(c.TheirFundMultisigPub[:], buf.Next(33)) + copy(c.OurRevokePKH[:], buf.Next(20)) + copy(c.TheirRevokePKH[:], buf.Next(20)) + + copy(c.OurrefundTxSig64[:], buf.Next(64)) + copy(c.TheirrefundTxSig64[:], buf.Next(64)) + copy(c.OurPayoutBase[:], buf.Next(33)) copy(c.TheirPayoutBase[:], buf.Next(33)) @@ -257,6 +277,7 @@ func (self *DlcContract) Bytes() []byte { wire.WriteVarInt(&buf, 0, uint64(self.CoinType)) wire.WriteVarInt(&buf, 0, uint64(self.FeePerByte)) wire.WriteVarInt(&buf, 0, uint64(self.OracleTimestamp)) + wire.WriteVarInt(&buf, 0, uint64(self.RefundTimestamp)) wire.WriteVarInt(&buf, 0, uint64(self.OurFundingAmount)) wire.WriteVarInt(&buf, 0, uint64(self.TheirFundingAmount)) @@ -264,6 +285,13 @@ func (self *DlcContract) Bytes() []byte { buf.Write(self.TheirChangePKH[:]) buf.Write(self.OurFundMultisigPub[:]) buf.Write(self.TheirFundMultisigPub[:]) + + buf.Write(self.OurRevokePKH[:]) + buf.Write(self.TheirRevokePKH[:]) + + buf.Write(self.OurrefundTxSig64[:]) + buf.Write(self.TheirrefundTxSig64[:]) + buf.Write(self.OurPayoutBase[:]) buf.Write(self.TheirPayoutBase[:]) buf.Write(self.OurPayoutPKH[:]) @@ -362,8 +390,7 @@ func DlcOutput(pkPeer, pkOracleSig, pkOurs [33]byte, value int64) *wire.TxOut { // signature and their own private key to claim the funds from the output. // However, if they send the wrong one, they won't be able to claim the funds // and we can claim them once the time delay has passed. -func DlcCommitScript(pubKeyPeer, pubKeyOracleSig, ourPubKey [33]byte, - delay uint16) []byte { +func DlcCommitScript(pubKeyPeer, pubKeyOracleSig, ourPubKey [33]byte, delay uint16) []byte { // Combine pubKey and Oracle Sig combinedPubKey := CombinePubs(pubKeyPeer, pubKeyOracleSig) return CommitScript(combinedPubKey, ourPubKey, delay) @@ -591,3 +618,60 @@ func SettlementTx(c *DlcContract, d DlcContractDivision, return tx, nil } + + + + +// RefundTx returns the transaction to refund the contract +// in the case the oracle does not publish a value. +func RefundTx(c *DlcContract) (*wire.MsgTx, error) { + + vsize := uint32(169) + fee := int64(vsize * c.FeePerByte) + + tx := wire.NewMsgTx() + tx.Version = 2 + tx.LockTime = uint32(c.RefundTimestamp) + + txin := wire.NewTxIn(&c.FundingOutpoint, nil, nil) + txin.Sequence = 0 + tx.AddTxIn(txin) + + ourRefScript := DirectWPKHScriptFromPKH(c.OurRevokePKH) + ourOutput := wire.NewTxOut(c.OurFundingAmount - fee, ourRefScript) + tx.AddTxOut(ourOutput) + + theirRefScript := DirectWPKHScriptFromPKH(c.TheirRevokePKH) + theirOutput := wire.NewTxOut(c.TheirFundingAmount - fee, theirRefScript) + tx.AddTxOut(theirOutput) + + txsort.InPlaceSort(tx) + + + return tx, nil + +} + + +// SignRefundTx +func SignRefundTx(c *DlcContract, tx *wire.MsgTx, priv *koblitz.PrivateKey) (error) { + + pre, _, err := FundTxScript(c.OurFundMultisigPub, c.TheirFundMultisigPub) + if err != nil { + return err + } + + hCache := txscript.NewTxSigHashes(tx) + + sig, err := txscript.RawTxInWitnessSignature(tx, hCache, 0, c.OurFundingAmount+c.TheirFundingAmount, pre, txscript.SigHashAll, priv) + if err != nil { + return err + } + + sig = sig[:len(sig)-1] + sig64 , _ := sig64.SigCompress(sig) + c.OurrefundTxSig64 = sig64 + + return nil + +} \ No newline at end of file diff --git a/lnutil/msglib.go b/lnutil/msglib.go index f7d1b9fd3..99e6ac2b9 100644 --- a/lnutil/msglib.go +++ b/lnutil/msglib.go @@ -1744,6 +1744,9 @@ type DlcOfferAcceptMsg struct { OurFundMultisigPub [33]byte // The Pubkey to be used to in the contract settlement OurPayoutBase [33]byte + //OurRevokePub [33]byte + OurRevokePKH [20]byte + OurrefundTxSig64 [64]byte // The PKH to be paid to in the contract settlement OurPayoutPKH [20]byte // The UTXOs we are using to fund the contract @@ -1765,6 +1768,13 @@ func NewDlcOfferAcceptMsg(contract *DlcContract, msg.OurChangePKH = contract.OurChangePKH msg.OurFundMultisigPub = contract.OurFundMultisigPub msg.OurPayoutBase = contract.OurPayoutBase + + //msg.OurRevokePub = contract.OurRevokePub + + msg.OurRevokePKH = contract.OurRevokePKH + + msg.OurrefundTxSig64 = contract.OurrefundTxSig64 + msg.OurPayoutPKH = contract.OurPayoutPKH msg.SettlementSignatures = signatures return *msg @@ -1790,6 +1800,13 @@ func NewDlcOfferAcceptMsgFromBytes(b []byte, copy(msg.OurChangePKH[:], buf.Next(20)) copy(msg.OurFundMultisigPub[:], buf.Next(33)) copy(msg.OurPayoutBase[:], buf.Next(33)) + + //copy(msg.OurRevokePub[:], buf.Next(33)) + + copy(msg.OurRevokePKH[:], buf.Next(20)) + + copy(msg.OurrefundTxSig64[:], buf.Next(64)) + copy(msg.OurPayoutPKH[:], buf.Next(20)) inputCount, _ := wire.ReadVarInt(buf, 0) @@ -1828,6 +1845,12 @@ func (msg DlcOfferAcceptMsg) Bytes() []byte { buf.Write(msg.OurChangePKH[:]) buf.Write(msg.OurFundMultisigPub[:]) buf.Write(msg.OurPayoutBase[:]) + + //buf.Write(msg.OurRevokePub[:]) + buf.Write(msg.OurRevokePKH[:]) + + buf.Write(msg.OurrefundTxSig64[:]) + buf.Write(msg.OurPayoutPKH[:]) inputCount := uint64(len(msg.FundingInputs)) @@ -1869,17 +1892,21 @@ type DlcContractAckMsg struct { Idx uint64 // The settlement signatures of the party acknowledging SettlementSignatures []DlcContractSettlementSignature + + OurrefundTxSig64 [64]byte } // NewDlcContractAckMsg generates a new DlcContractAckMsg struct based on the // passed contract and signatures func NewDlcContractAckMsg(contract *DlcContract, - signatures []DlcContractSettlementSignature) DlcContractAckMsg { + signatures []DlcContractSettlementSignature, OurrefundTxSig64 [64]byte) DlcContractAckMsg { msg := new(DlcContractAckMsg) msg.PeerIdx = contract.PeerIdx msg.Idx = contract.TheirIdx msg.SettlementSignatures = signatures + + msg.OurrefundTxSig64 = OurrefundTxSig64 return *msg } @@ -1908,6 +1935,9 @@ func NewDlcContractAckMsgFromBytes(b []byte, binary.Read(buf, binary.BigEndian, &msg.SettlementSignatures[i].Outcome) copy(msg.SettlementSignatures[i].Signature[:], buf.Next(64)) } + + copy(msg.OurrefundTxSig64[:], buf.Next(64)) + return *msg, nil } @@ -1926,6 +1956,9 @@ func (msg DlcContractAckMsg) Bytes() []byte { binary.Write(&buf, binary.BigEndian, outcome) buf.Write(msg.SettlementSignatures[i].Signature[:]) } + + buf.Write(msg.OurrefundTxSig64[:]) + return buf.Bytes() } diff --git a/qln/dlc.go b/qln/dlc.go index 40e4e7b5b..9a540c41f 100644 --- a/qln/dlc.go +++ b/qln/dlc.go @@ -2,7 +2,6 @@ package qln import ( "fmt" - "github.com/mit-dci/lit/btcutil" "github.com/mit-dci/lit/btcutil/txscript" "github.com/mit-dci/lit/btcutil/txsort" @@ -53,6 +52,10 @@ func (nd *LitNode) OfferDlc(peerIdx uint32, cIdx uint64) error { return fmt.Errorf("You need to set a settlement time for the contract before offering it") } + if c.RefundTimestamp == 0 { + return fmt.Errorf("You need to set a refund time for the contract before offering it") + } + if c.CoinType == dlc.COINTYPE_NOT_SET { return fmt.Errorf("You need to set a coin type for the contract before offering it") } @@ -79,6 +82,7 @@ func (nd *LitNode) OfferDlc(peerIdx uint32, cIdx uint64) error { kg.Step[3] = c.PeerIdx | 1<<31 kg.Step[4] = uint32(c.Idx) | 1<<31 + c.OurFundMultisigPub, err = nd.GetUsePub(kg, UseContractFundMultisig) if err != nil { return err @@ -105,6 +109,8 @@ func (nd *LitNode) OfferDlc(peerIdx uint32, cIdx uint64) error { return err } + wal, _ := nd.SubWallet[c.CoinType] + c.OurRevokePKH, err = wal.NewAdr() msg := lnutil.NewDlcOfferMsg(peerIdx, c) c.Status = lnutil.ContractStatusOfferedByMe @@ -206,6 +212,9 @@ func (nd *LitNode) AcceptDlc(cIdx uint64) error { } copy(c.OurPayoutPKH[:], btcutil.Hash160(ourPayoutPKHKey[:])) + wal, _ := nd.SubWallet[c.CoinType] + c.OurRevokePKH, err = wal.NewAdr() + // Now we can sign the division sigs, err := nd.SignSettlementDivisions(c) if err != nil { @@ -215,6 +224,33 @@ func (nd *LitNode) AcceptDlc(cIdx uint64) error { return } + + //----------------------------------------------- + + + refundTx, err := lnutil.RefundTx(c) + if err != nil { + logging.Errorf("Error of RefundTx: %s", err.Error()) + c.Status = lnutil.ContractStatusError + nd.DlcManager.SaveContract(c) + return + } + + kg.Step[2] = UseContractFundMultisig + mypriv, err := wal.GetPriv(kg) + //wal, _ := nd.SubWallet[c.CoinType] + + + err = lnutil.SignRefundTx(c, refundTx, mypriv) + if err != nil { + logging.Errorf("Error of SignRefundTx: %s", err.Error()) + c.Status = lnutil.ContractStatusError + nd.DlcManager.SaveContract(c) + return + } + + // //------------------------------------------------ + msg := lnutil.NewDlcOfferAcceptMsg(c, sigs) c.Status = lnutil.ContractStatusAccepted @@ -243,6 +279,10 @@ func (nd *LitNode) DlcOfferHandler(msg lnutil.DlcOfferMsg, peer *RemotePeer) { c.TheirIdx = msg.Contract.Idx c.TheirPayoutPKH = msg.Contract.OurPayoutPKH + //c.TheirRevokePub = msg.Contract.OurRevokePub + + c.TheirRevokePKH = msg.Contract.OurRevokePKH + c.Division = make([]lnutil.DlcContractDivision, len(msg.Contract.Division)) for i := 0; i < len(msg.Contract.Division); i++ { c.Division[i].OracleValue = msg.Contract.Division[i].OracleValue @@ -255,6 +295,7 @@ func (nd *LitNode) DlcOfferHandler(msg lnutil.DlcOfferMsg, peer *RemotePeer) { c.OracleA = msg.Contract.OracleA c.OracleR = msg.Contract.OracleR c.OracleTimestamp = msg.Contract.OracleTimestamp + c.RefundTimestamp = msg.Contract.RefundTimestamp err := nd.DlcManager.SaveContract(c) if err != nil { @@ -301,6 +342,10 @@ func (nd *LitNode) DlcAcceptHandler(msg lnutil.DlcOfferAcceptMsg, peer *RemotePe c.TheirPayoutBase = msg.OurPayoutBase c.TheirPayoutPKH = msg.OurPayoutPKH c.TheirIdx = msg.OurIdx + c.TheirRevokePKH = msg.OurRevokePKH + c.TheirrefundTxSig64 = msg.OurrefundTxSig64 + + //------------------------------------------ c.Status = lnutil.ContractStatusAccepted err = nd.DlcManager.SaveContract(c) @@ -315,18 +360,49 @@ func (nd *LitNode) DlcAcceptHandler(msg lnutil.DlcOfferAcceptMsg, peer *RemotePe return err } - outMsg := lnutil.NewDlcContractAckMsg(c, sigs) + //------------------------------------------ + + wal, _ := nd.SubWallet[c.CoinType] + + refundTx, err := lnutil.RefundTx(c) + if err != nil { + logging.Errorf("Error of RefundTx: %s", err.Error()) + c.Status = lnutil.ContractStatusError + nd.DlcManager.SaveContract(c) + return err + } + + + var kg portxo.KeyGen + kg.Depth = 5 + kg.Step[0] = 44 | 1<<31 + kg.Step[1] = c.CoinType | 1<<31 + kg.Step[2] = UseContractFundMultisig + kg.Step[3] = c.PeerIdx | 1<<31 + kg.Step[4] = uint32(c.Idx) | 1<<31 + + mypriv, err := wal.GetPriv(kg) + + err = lnutil.SignRefundTx(c, refundTx, mypriv) + if err != nil { + logging.Errorf("Error of SignRefundTx: %s", err.Error()) + c.Status = lnutil.ContractStatusError + nd.DlcManager.SaveContract(c) + return err + } + + //------------------------------------------ + + outMsg := lnutil.NewDlcContractAckMsg(c, sigs, c.OurrefundTxSig64) c.Status = lnutil.ContractStatusAcknowledged err = nd.DlcManager.SaveContract(c) if err != nil { return err } - nd.tmpSendLitMsg(outMsg) return nil - } func (nd *LitNode) DlcContractAckHandler(msg lnutil.DlcContractAckMsg, peer *RemotePeer) { @@ -339,8 +415,8 @@ func (nd *LitNode) DlcContractAckHandler(msg lnutil.DlcContractAckMsg, peer *Rem // TODO: Check signatures c.Status = lnutil.ContractStatusAcknowledged - c.TheirSettlementSignatures = msg.SettlementSignatures + c.TheirrefundTxSig64 = msg.OurrefundTxSig64 err = nd.DlcManager.SaveContract(c) if err != nil { @@ -389,7 +465,6 @@ func (nd *LitNode) DlcFundingSigsHandler(msg lnutil.DlcContractFundingSigsMsg, p } wal.SignMyInputs(msg.SignedFundingTx) - wal.DirectSendTx(msg.SignedFundingTx) err = wal.WatchThis(c.FundingOutpoint) @@ -495,6 +570,7 @@ func (nd *LitNode) BuildDlcFundingTransaction(c *lnutil.DlcContract) (wire.MsgTx our_txin_num := 0 for _, u := range c.OurFundingInputs { txin := wire.NewTxIn(&u.Outpoint, nil, nil) + tx.AddTxIn(txin) ourInputTotal += u.Value our_txin_num += 1 @@ -505,6 +581,7 @@ func (nd *LitNode) BuildDlcFundingTransaction(c *lnutil.DlcContract) (wire.MsgTx their_txin_num := 0 for _, u := range c.TheirFundingInputs { txin := wire.NewTxIn(&u.Outpoint, nil, nil) + tx.AddTxIn(txin) theirInputTotal += u.Value their_txin_num += 1 @@ -537,16 +614,12 @@ func (nd *LitNode) BuildDlcFundingTransaction(c *lnutil.DlcContract) (wire.MsgTx their_fee := int64(their_tx_vsize * c.FeePerByte) // add change and sort - their_txout := wire.NewTxOut(theirInputTotal-c.TheirFundingAmount-their_fee, lnutil.DirectWPKHScriptFromPKH(c.TheirChangePKH)) tx.AddTxOut(their_txout) - our_txout := wire.NewTxOut(ourInputTotal-c.OurFundingAmount-our_fee, lnutil.DirectWPKHScriptFromPKH(c.OurChangePKH)) tx.AddTxOut(our_txout) - - txsort.InPlaceSort(tx) // get txo for channel @@ -654,6 +727,7 @@ func (nd *LitNode) SettleContract(cIdx uint64, oracleValue int64, oracleSig [32] logging.Errorf("SettleContract FundTxScript err %s", err.Error()) return [32]byte{}, [32]byte{}, err } + // swap if needed if swap { @@ -743,3 +817,35 @@ func (nd *LitNode) SettleContract(cIdx uint64, oracleValue int64, oracleSig [32] } } + + + +func (nd *LitNode) RefundContract(cIdx uint64) (bool, error) { + + c, err := nd.DlcManager.LoadContract(cIdx) + if err != nil { + logging.Errorf("SettleContract FindContract err %s\n", err.Error()) + return false, err + } + + wal, _ := nd.SubWallet[c.CoinType] + + refundTx, err := lnutil.RefundTx(c) + myBigSig := sig64.SigDecompress(c.OurrefundTxSig64) + myBigSig = append(myBigSig, byte(txscript.SigHashAll)) + theirBigSig := sig64.SigDecompress(c.TheirrefundTxSig64) + theirBigSig = append(theirBigSig, byte(txscript.SigHashAll)) + pre, swap, err := lnutil.FundTxScript(c.OurFundMultisigPub, c.TheirFundMultisigPub) + + // swap if needed + if swap { + refundTx.TxIn[0].Witness = SpendMultiSigWitStack(pre, theirBigSig, myBigSig) + } else { + refundTx.TxIn[0].Witness = SpendMultiSigWitStack(pre, myBigSig, theirBigSig) + } + + err = wal.DirectSendTx(refundTx) + + return true, nil + +} diff --git a/qln/magicnums.go b/qln/magicnums.go index 8fdf7a4be..6ad674cc4 100644 --- a/qln/magicnums.go +++ b/qln/magicnums.go @@ -20,7 +20,9 @@ const ( // key derivation path for contract payout PKH (the hash the contract // pays TO) - UseContractPayoutPKH = 52 | hdkeychain.HardenedKeyStart + UseContractPayoutPKH = 53 | hdkeychain.HardenedKeyStart + + UseContractRevoke = 54 | hdkeychain.HardenedKeyStart // key derivation path for HTLC pubkeys UseHTLCBase = 60 | hdkeychain.HardenedKeyStart diff --git a/test/itest_dlc.py b/test/itest_dlc.py index 7cfc66975..ea00f4a22 100644 --- a/test/itest_dlc.py +++ b/test/itest_dlc.py @@ -41,8 +41,6 @@ def run_t(env, params): env.new_oracle(1, oracle_value) # publishing interval is 1 second. - #settle_lit = env.lits[node_to_settle] - oracle1 = env.oracles[0] time.sleep(2) @@ -142,9 +140,6 @@ def run_t(env, params): oracle1_pubkey = json.loads(oracle1.get_pubkey()) assert len(oracle1_pubkey["A"]) == 66, "Wrong oracle1 pub key" - # oracle2_pubkey = json.loads(oracle2.get_pubkey()) - # assert len(oracle2_pubkey["A"]) == 66, "Wrong oracle2 pub key" - oracle_res1 = lit1.rpc.AddOracle(Key=oracle1_pubkey["A"], Name="oracle1") assert oracle_res1["Oracle"]["Idx"] == 1, "AddOracle does not works" @@ -179,8 +174,13 @@ def run_t(env, params): settlement_time = int(time.time()) + 3 - # dlc contract settime 1 1552080600 - lit1.rpc.SetContractSettlementTime(CIdx=contract["Contract"]["Idx"], Time=settlement_time) + # dlc contract settime + res = lit1.rpc.SetContractSettlementTime(CIdx=contract["Contract"]["Idx"], Time=settlement_time) + assert res["Success"], "SetContractSettlementTime does not works" + + # we set settlement_time equal to refundtime, actually the refund transaction will be valid. + res = lit1.rpc.SetContractRefundTime(CIdx=contract["Contract"]["Idx"], Time=settlement_time) + assert res["Success"], "SetContractRefundTime does not works" res = lit1.rpc.ListContracts() assert res["Contracts"][contract["Contract"]["Idx"] - 1]["OracleTimestamp"] == settlement_time, "SetContractSettlementTime does not match settlement_time" @@ -218,7 +218,7 @@ def run_t(env, params): print("After SetContractDivision") - time.sleep(8) + time.sleep(5) res = lit1.rpc.ListConnections() @@ -231,7 +231,7 @@ def run_t(env, params): print("After OfferContract") - time.sleep(8) + time.sleep(5) print("Before ContractRespond") @@ -241,7 +241,7 @@ def run_t(env, params): print("After ContractRespond") - time.sleep(8) + time.sleep(5) #------------------------------------------ @@ -263,7 +263,7 @@ def run_t(env, params): print("Before Generate Block") env.generate_block() - time.sleep(5) + time.sleep(2) print("After Generate Block") @@ -312,13 +312,13 @@ def run_t(env, params): print("Before SettleContract") - time.sleep(8) + time.sleep(5) res = env.lits[node_to_settle].rpc.SettleContract(CIdx=contract["Contract"]["Idx"], OracleValue=oracle1_val, OracleSig=OracleSig) assert res["Success"], "SettleContract does not works." - time.sleep(8) + time.sleep(5) print('After SettleContract:') print(res) @@ -326,15 +326,15 @@ def run_t(env, params): try: env.generate_block(1) - time.sleep(2) + time.sleep(1) env.generate_block(10) - time.sleep(2) + time.sleep(1) env.generate_block(1) + time.sleep(1) except BaseException as be: print("Exception After SettleContract: ") print(be) - time.sleep(10) #------------------------------------------ if deb_mod: @@ -475,11 +475,6 @@ def t_11_0(env): # 1)Funding transaction. # Here can be a situation when peers have different number of inputs. - # ::lit1:: BuildDlcFundingTransaction: qln/dlc.go: our_tx_vsize: 126 - # ::lit1:: BuildDlcFundingTransaction: qln/dlc.go: their_tx_vsize: 126 - # ::lit1:: BuildDlcFundingTransaction: qln/dlc.go: our_fee: 10080 - # ::lit1:: BuildDlcFundingTransaction: qln/dlc.go: their_fee: 10080 - # Vsize from Blockchain: 252 # So we expect lit1, and lit2 balances equal to 89989920 !!! @@ -489,25 +484,7 @@ def t_11_0(env): #----------------------------- # 2) SettlementTx vsize will be printed - # ::lit0:: SettlementTx()1: qln/dlclib.go: --------------------: - # ::lit0:: SettlementTx()1: qln/dlclib.go: valueOurs: 18000000 - # ::lit0:: SettlementTx()1: qln/dlclib.go: valueTheirs: 2000000 - # ::lit0:: SettlementTx()1: qln/dlclib.go: --------------------: - # ::lit0:: SettlementTx()2: qln/dlclib.go: --------------------: - # ::lit0:: SettlementTx()2: qln/dlclib.go: valueOurs: 18000000 - # ::lit0:: SettlementTx()2: qln/dlclib.go: valueTheirs: 2000000 - # ::lit0:: SettlementTx()2: qln/dlclib.go: --------------------: - # ::lit0:: SettlementTx()3: qln/dlclib.go: --------------------: - # ::lit0:: SettlementTx()3: qln/dlclib.go: totalFee: 14400 - # ::lit0:: SettlementTx()3: qln/dlclib.go: feeEach: 7200 - # ::lit0:: SettlementTx()3: qln/dlclib.go: feeOurs: 7200 - # ::lit0:: SettlementTx()3: qln/dlclib.go: feeTheirs: 7200 - # ::lit0:: SettlementTx()3: qln/dlclib.go: valueOurs: 17992800 - # ::lit0:: SettlementTx()3: qln/dlclib.go: valueTheirs: 1992800 - # ::lit0:: SettlementTx()3: qln/dlclib.go: vsize: 0 # Actually we have 14400/80 = 180 - # ::lit0:: SettlementTx()3: qln/dlclib.go: --------------------: - - + # Vsize from Blockchain: 181 # There fore we expect here @@ -589,11 +566,6 @@ def t_1300_1(env): # 1)Funding transaction. # Here can be a situation when peers have different number of inputs. - # ::lit1:: BuildDlcFundingTransaction: qln/dlc.go: our_tx_vsize: 126 - # ::lit1:: BuildDlcFundingTransaction: qln/dlc.go: their_tx_vsize: 126 - # ::lit1:: BuildDlcFundingTransaction: qln/dlc.go: our_fee: 10080 - # ::lit1:: BuildDlcFundingTransaction: qln/dlc.go: their_fee: 10080 - # Vsize from Blockchain: 252 # So we expect lit1, and lit2 balances equal to 89989920 !!! @@ -603,24 +575,6 @@ def t_1300_1(env): #----------------------------- # 2) SettlementTx vsize will be printed - # ::lit1:: SettlementTx()1: qln/dlclib.go: --------------------: - # ::lit1:: SettlementTx()1: qln/dlclib.go: valueOurs: 6000000 - # ::lit1:: SettlementTx()1: qln/dlclib.go: valueTheirs: 14000000 - # ::lit1:: SettlementTx()1: qln/dlclib.go: --------------------: - # ::lit1:: SettlementTx()2: qln/dlclib.go: --------------------: - # ::lit1:: SettlementTx()2: qln/dlclib.go: valueOurs: 5992800 - # ::lit1:: SettlementTx()2: qln/dlclib.go: valueTheirs: 13992800 - # ::lit1:: SettlementTx()2: qln/dlclib.go: --------------------: - # ::lit1:: SettlementTx()2: qln/dlclib.go: --------------------: - # ::lit1:: SettlementTx()2: qln/dlclib.go: totalFee: 14400 - # ::lit1:: SettlementTx()2: qln/dlclib.go: feeEach: 7200 - # ::lit1:: SettlementTx()2: qln/dlclib.go: feeOurs: 7200 - # ::lit1:: SettlementTx()2: qln/dlclib.go: feeTheirs: 7200 - # ::lit1:: SettlementTx()2: qln/dlclib.go: valueOurs: 5992800 - # ::lit1:: SettlementTx()2: qln/dlclib.go: valueTheirs: 13992800 - # ::lit1:: SettlementTx()2: qln/dlclib.go: vsize: 180 - # ::lit1:: SettlementTx()2: qln/dlclib.go: --------------------: - # Vsize from Blockchain: 181 diff --git a/test/itest_dlcrefund.py b/test/itest_dlcrefund.py new file mode 100644 index 000000000..c24664e76 --- /dev/null +++ b/test/itest_dlcrefund.py @@ -0,0 +1,455 @@ +import testlib + +import time, datetime +import json + +import pprint + +import requests # pip3 install requests + +import codecs + +deb_mod = False + + +def run_t(env, params): + global deb_mod + try: + + lit_funding_amt = params[0] + contract_funding_amt = params[1] + oracle_value = params[2] + valueFullyOurs=params[3] + valueFullyTheirs=params[4] + + feeperbyte = params[5] + + node_to_refund = params[6] + + bc = env.bitcoind + + #------------ + # Create oracles + #------------ + + env.new_oracle(1, oracle_value) # publishing interval is 1 second. + + oracle1 = env.oracles[0] + + time.sleep(2) + + #------------ + # Create lits + #------------ + + lit1 = env.lits[0] + lit2 = env.lits[1] + + + pp = pprint.PrettyPrinter(indent=4) + + + #------------------------------------------ + if deb_mod: + print("ADDRESSES BEFORE SEND TO ADDRESS") + print("LIT1 Addresses") + print(pp.pprint(lit1.rpc.GetAddresses())) + + print("LIT2 Addresses") + print(pp.pprint(lit2.rpc.GetAddresses())) + + print("bitcoind Addresses") + print(pp.pprint(bc.rpc.listaddressgroupings())) + #------------------------------------------ + + + lit1.connect_to_peer(lit2) + print("---------------") + print('Connecting lit1:', lit1.lnid, 'to lit2:', lit2.lnid) + + addr1 = lit1.make_new_addr() + txid1 = bc.rpc.sendtoaddress(addr1, lit_funding_amt) + + if deb_mod: + print("Funding TxId lit1: " + str(txid1)) + + time.sleep(2) + + addr2 = lit2.make_new_addr() + txid2 = bc.rpc.sendtoaddress(addr2, lit_funding_amt) + + if deb_mod: + print("Funding TxId lit2: " + str(txid2)) + + time.sleep(2) + + env.generate_block() + time.sleep(2) + + + #------------------------------------------ + if deb_mod: + print("ADDRESSES AFTER SEND TO ADDRESS") + print("LIT1 Addresses") + print(pp.pprint(lit1.rpc.GetAddresses())) + + print("LIT2 Addresses") + print(pp.pprint(lit2.rpc.GetAddresses())) + + print("bitcoind Addresses") + print(pp.pprint(bc.rpc.listaddressgroupings())) + #------------------------------------------ + + + print("Funding") + bals1 = lit1.get_balance_info() + print('new lit1 balance:', bals1['TxoTotal'], 'in txos,', bals1['ChanTotal'], 'in chans') + bal1sum = bals1['TxoTotal'] + bals1['ChanTotal'] + print(' = sum ', bal1sum) + + print(lit_funding_amt) + + lit_funding_amt *= 100000000 # to satoshi + + + bals2 = lit2.get_balance_info() + print('new lit2 balance:', bals2['TxoTotal'], 'in txos,', bals2['ChanTotal'], 'in chans') + bal2sum = bals2['TxoTotal'] + bals2['ChanTotal'] + print(' = sum ', bal2sum) + + + assert bal1sum == lit_funding_amt, "Funding lit1 does not works" + assert bal2sum == lit_funding_amt, "Funding lit2 does not works" + + # #------------ + # # Add oracles + # #------------ + + res = lit1.rpc.ListOracles() + assert len(res) != 0, "Initial lis of oracles must be empty" + + oracle1_pubkey = json.loads(oracle1.get_pubkey()) + assert len(oracle1_pubkey["A"]) == 66, "Wrong oracle1 pub key" + + oracle_res1 = lit1.rpc.AddOracle(Key=oracle1_pubkey["A"], Name="oracle1") + assert oracle_res1["Oracle"]["Idx"] == 1, "AddOracle does not works" + + res = lit1.rpc.ListOracles(ListOraclesArgs={}) + assert len(res["Oracles"]) == 1, "ListOracles 1 does not works" + + + lit2.rpc.AddOracle(Key=oracle1_pubkey["A"], Name="oracle1") + + + # #------------ + # # Now we have to create a contract in the lit1 node. + # #------------ + + contract = lit1.rpc.NewContract() + + res = lit1.rpc.ListContracts() + assert len(res["Contracts"]) == 1, "ListContracts does not works" + + + res = lit1.rpc.GetContract(Idx=1) + assert res["Contract"]["Idx"] == 1, "GetContract does not works" + + + res = lit1.rpc.SetContractOracle(CIdx=contract["Contract"]["Idx"], OIdx=oracle_res1["Oracle"]["Idx"]) + assert res["Success"], "SetContractOracle does not works" + + datasources = json.loads(oracle1.get_datasources()) + + # Since the oracle publishes data every 1 second (we set this time above), + # we increase the time for a point by 3 seconds. + + settlement_time = int(time.time()) + 3 + + # dlc contract settime + res = lit1.rpc.SetContractSettlementTime(CIdx=contract["Contract"]["Idx"], Time=settlement_time) + assert res["Success"], "SetContractSettlementTime does not works" + + # we set settlement_time equal to refundtime, actually the refund transaction will be valid. + res = lit1.rpc.SetContractRefundTime(CIdx=contract["Contract"]["Idx"], Time=settlement_time) + assert res["Success"], "SetContractRefundTime does not works" + + res = lit1.rpc.ListContracts() + assert res["Contracts"][contract["Contract"]["Idx"] - 1]["OracleTimestamp"] == settlement_time, "SetContractSettlementTime does not match settlement_time" + + rpoint1 = oracle1.get_rpoint(datasources[0]["id"], settlement_time) + + decode_hex = codecs.getdecoder("hex_codec") + b_RPoint = decode_hex(json.loads(rpoint1)['R'])[0] + RPoint = [elem for elem in b_RPoint] + + res = lit1.rpc.SetContractRPoint(CIdx=contract["Contract"]["Idx"], RPoint=RPoint) + assert res["Success"], "SetContractRpoint does not works" + + lit1.rpc.SetContractCoinType(CIdx=contract["Contract"]["Idx"], CoinType = 257) + res = lit1.rpc.GetContract(Idx=contract["Contract"]["Idx"]) + assert res["Contract"]["CoinType"] == 257, "SetContractCoinType does not works" + + + lit1.rpc.SetContractFeePerByte(CIdx=contract["Contract"]["Idx"], FeePerByte = feeperbyte) + res = lit1.rpc.GetContract(Idx=contract["Contract"]["Idx"]) + assert res["Contract"]["FeePerByte"] == feeperbyte, "SetContractFeePerByte does not works" + + ourFundingAmount = contract_funding_amt + theirFundingAmount = contract_funding_amt + + lit1.rpc.SetContractFunding(CIdx=contract["Contract"]["Idx"], OurAmount=ourFundingAmount, TheirAmount=theirFundingAmount) + res = lit1.rpc.GetContract(Idx=contract["Contract"]["Idx"]) + assert res["Contract"]["OurFundingAmount"] == ourFundingAmount, "SetContractFunding does not works" + assert res["Contract"]["TheirFundingAmount"] == theirFundingAmount, "SetContractFunding does not works" + + print("Before SetContractDivision") + + res = lit1.rpc.SetContractDivision(CIdx=contract["Contract"]["Idx"], ValueFullyOurs=valueFullyOurs, ValueFullyTheirs=valueFullyTheirs) + assert res["Success"], "SetContractDivision does not works" + + print("After SetContractDivision") + + time.sleep(3) + + res = lit1.rpc.ListConnections() + print(res) + + print("Before OfferContract") + + res = lit1.rpc.OfferContract(CIdx=contract["Contract"]["Idx"], PeerIdx=lit1.get_peer_id(lit2)) + assert res["Success"], "OfferContract does not works" + + print("After OfferContract") + + time.sleep(3) + + print("Before ContractRespond") + + res = lit2.rpc.ContractRespond(AcceptOrDecline=True, CIdx=1) + assert res["Success"], "ContractRespond on lit2 does not works" + + print("After ContractRespond") + + time.sleep(3) + + #------------------------------------------ + + if deb_mod: + print("ADDRESSES AFTER CONTRACT RESPOND") + print("LIT1 Addresses") + print(pp.pprint(lit1.rpc.GetAddresses())) + + print("LIT2 Addresses") + print(pp.pprint(lit2.rpc.GetAddresses())) + + print("bitcoind Addresses") + print(pp.pprint(bc.rpc.listaddressgroupings())) + + + # #------------------------------------------ + + + print("Before Generate Block") + + env.generate_block() + time.sleep(2) + + print("After Generate Block") + + print("Accept") + bals1 = lit1.get_balance_info() + print('new lit1 balance:', bals1['TxoTotal'], 'in txos,', bals1['ChanTotal'], 'in chans') + bal1sum = bals1['TxoTotal'] + bals1['ChanTotal'] + print(' = sum ', bal1sum) + + lit1_bal_after_accept = (lit_funding_amt - ourFundingAmount) - (126*feeperbyte) + + + bals2 = lit2.get_balance_info() + print('new lit2 balance:', bals2['TxoTotal'], 'in txos,', bals2['ChanTotal'], 'in chans') + bal2sum = bals2['TxoTotal'] + bals2['ChanTotal'] + print(' = sum ', bal2sum) + + lit2_bal_after_accept = (lit_funding_amt - theirFundingAmount) - (126*feeperbyte) + + + assert bal1sum == lit1_bal_after_accept, "lit1 Balance after contract accept does not match" + assert bal2sum == lit2_bal_after_accept, "lit2 Balance after contract accept does not match" + + oracle1_val = "" + oracle1_sig = "" + + i = 0 + while True: + res = oracle1.get_publication(json.loads(rpoint1)['R']) + time.sleep(5) + i += 1 + if i>4: + assert False, "Error: Oracle does not publish data" + + try: + oracle1_val = json.loads(res)["value"] + oracle1_sig = json.loads(res)["signature"] + break + except BaseException as e: + print(e) + next + + + print("Before Refund Contract") + time.sleep(2) + + res = env.lits[node_to_refund].rpc.RefundContract(CIdx=1) + + time.sleep(2) + env.generate_block() + time.sleep(1) + env.generate_block() + time.sleep(1) + + + if deb_mod: + print("ADDRESSES AFTER CONTRACT Refund") + print("LIT1 Addresses") + print(pp.pprint(lit1.rpc.GetAddresses())) + + print("LIT2 Addresses") + print(pp.pprint(lit2.rpc.GetAddresses())) + + print("bitcoind Addresses") + print(pp.pprint(bc.rpc.listaddressgroupings())) + + + + print("Refund") + bals1 = lit1.get_balance_info() + print('new lit1 balance:', bals1['TxoTotal'], 'in txos,', bals1['ChanTotal'], 'in chans') + bal1sum = bals1['TxoTotal'] + bals1['ChanTotal'] + print(' = sum ', bal1sum) + + + bals2 = lit2.get_balance_info() + print('new lit2 balance:', bals2['TxoTotal'], 'in txos,', bals2['ChanTotal'], 'in chans') + bal2sum = bals2['TxoTotal'] + bals2['ChanTotal'] + print(' = sum ', bal2sum) + + assert bal1sum == 99976400, "lit1 After Refund balance does not match." + assert bal2sum == 99976400, "lit2 After Refund balance does not match." + + + #----------------------------------------------- + # Send 10000 sat from lit1 to lit2 + + print("Address to send: ") + print(lit2.rpc.GetAddresses()['WitAddresses'][0]) + + res = lit1.rpc.Send(DestAddrs=[lit2.rpc.GetAddresses()['WitAddresses'][0]], Amts=[99960240]) + + time.sleep(1) + env.generate_block() + time.sleep(2) + + print("After Spend") + bals1 = lit1.get_balance_info() + print('new lit1 balance:', bals1['TxoTotal'], 'in txos,', bals1['ChanTotal'], 'in chans') + bal1sum = bals1['TxoTotal'] + bals1['ChanTotal'] + print(' = sum ', bal1sum) + + + bals2 = lit2.get_balance_info() + print('new lit2 balance:', bals2['TxoTotal'], 'in txos,', bals2['ChanTotal'], 'in chans') + bal2sum = bals2['TxoTotal'] + bals2['ChanTotal'] + print(' = sum ', bal2sum) + + assert bal1sum == 0, "lit1 After Spend balance does not match." + assert bal2sum == 199936640, "lit2 After Spend balance does not match." + + + + + + #------------------------------------------------ + + if deb_mod: + + print("==========================================================") + print('Print Blockchain Info') + print("==========================================================") + + + best_block_hash = bc.rpc.getbestblockhash() + bb = bc.rpc.getblock(best_block_hash) + print(bb) + print("bb['height']: " + str(bb['height'])) + + print("Balance from RPC: " + str(bc.rpc.getbalance())) + + # batch support : print timestamps of blocks 0 to 99 in 2 RPC round-trips: + commands = [ [ "getblockhash", height] for height in range(bb['height'] + 1) ] + block_hashes = bc.rpc.batch_(commands) + blocks = bc.rpc.batch_([ [ "getblock", h ] for h in block_hashes ]) + block_times = [ block["time"] for block in blocks ] + print(block_times) + + print('--------------------') + + for b in blocks: + print("--------BLOCK--------") + print(b) + tx = b["tx"] + try: + + for i in range(len(tx)): + print("--------TRANSACTION--------") + rtx = bc.rpc.getrawtransaction(tx[i]) + print(rtx) + decoded = bc.rpc.decoderawtransaction(rtx) + pp.pprint(decoded) + except BaseException as be: + print(be) + print('--------') + + except BaseException as be: + raise be + + +# ==================================================================================== +# ==================================================================================== + + + +def forward(env): + + oracle_value = 10 + node_to_settle = 0 + + valueFullyOurs=10 + valueFullyTheirs=20 + + lit_funding_amt = 1 # 1 BTC + contract_funding_amt = 10000000 # satoshi + + feeperbyte = 80 + + params = [lit_funding_amt, contract_funding_amt, oracle_value, valueFullyOurs, valueFullyTheirs, feeperbyte, 0] + + run_t(env, params) + + + +def reverse(env): + + oracle_value = 10 + node_to_settle = 0 + + valueFullyOurs=10 + valueFullyTheirs=20 + + lit_funding_amt = 1 # 1 BTC + contract_funding_amt = 10000000 # satoshi + + feeperbyte = 80 + + params = [lit_funding_amt, contract_funding_amt, oracle_value, valueFullyOurs, valueFullyTheirs, feeperbyte, 1] + + run_t(env, params) \ No newline at end of file diff --git a/test/testlib.py b/test/testlib.py index 2be516382..6b82e6099 100644 --- a/test/testlib.py +++ b/test/testlib.py @@ -20,6 +20,8 @@ LIT_BIN = "%s/../lit" % paths.abspath(paths.dirname(__file__)) +ORACLE_BIN = "%s/../dlcoracle" % paths.abspath(paths.dirname(__file__)) + REGTEST_COINTYPE = 257 logger = logging.getLogger("testframework") @@ -61,7 +63,6 @@ def get_new_id(): return id - class LitNode(): def __init__(self, bcnode): self.bcnode = bcnode @@ -277,7 +278,7 @@ def start(self): # Now figure out the args to use and then start Lit. args = [ - "dlcoracle", + ORACLE_BIN, "--DataDir="+self.data_dir, "--HttpPort=" + self.httpport, "--Interval=" + self.interval, diff --git a/test/tests.txt b/test/tests.txt index bf0ed801a..4bf317dfc 100644 --- a/test/tests.txt +++ b/test/tests.txt @@ -44,3 +44,6 @@ dlc 2 t_20_0 # test at right edge from the counterparty dlc 2 t_20_1 + +dlcrefund 2 forward +dlcrefund 2 reverse \ No newline at end of file From f1910e0deb0110676b2f1cb55a089cc7104f46f0 Mon Sep 17 00:00:00 2001 From: infografx Date: Tue, 17 Sep 2019 15:55:53 +0300 Subject: [PATCH 17/24] Some cleanup. --- lnutil/msglib.go | 16 ---------------- qln/dlc.go | 10 +--------- 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/lnutil/msglib.go b/lnutil/msglib.go index 99e6ac2b9..c4e3ce919 100644 --- a/lnutil/msglib.go +++ b/lnutil/msglib.go @@ -1768,13 +1768,8 @@ func NewDlcOfferAcceptMsg(contract *DlcContract, msg.OurChangePKH = contract.OurChangePKH msg.OurFundMultisigPub = contract.OurFundMultisigPub msg.OurPayoutBase = contract.OurPayoutBase - - //msg.OurRevokePub = contract.OurRevokePub - msg.OurRevokePKH = contract.OurRevokePKH - msg.OurrefundTxSig64 = contract.OurrefundTxSig64 - msg.OurPayoutPKH = contract.OurPayoutPKH msg.SettlementSignatures = signatures return *msg @@ -1800,13 +1795,8 @@ func NewDlcOfferAcceptMsgFromBytes(b []byte, copy(msg.OurChangePKH[:], buf.Next(20)) copy(msg.OurFundMultisigPub[:], buf.Next(33)) copy(msg.OurPayoutBase[:], buf.Next(33)) - - //copy(msg.OurRevokePub[:], buf.Next(33)) - copy(msg.OurRevokePKH[:], buf.Next(20)) - copy(msg.OurrefundTxSig64[:], buf.Next(64)) - copy(msg.OurPayoutPKH[:], buf.Next(20)) inputCount, _ := wire.ReadVarInt(buf, 0) @@ -1845,12 +1835,8 @@ func (msg DlcOfferAcceptMsg) Bytes() []byte { buf.Write(msg.OurChangePKH[:]) buf.Write(msg.OurFundMultisigPub[:]) buf.Write(msg.OurPayoutBase[:]) - - //buf.Write(msg.OurRevokePub[:]) buf.Write(msg.OurRevokePKH[:]) - buf.Write(msg.OurrefundTxSig64[:]) - buf.Write(msg.OurPayoutPKH[:]) inputCount := uint64(len(msg.FundingInputs)) @@ -1892,7 +1878,6 @@ type DlcContractAckMsg struct { Idx uint64 // The settlement signatures of the party acknowledging SettlementSignatures []DlcContractSettlementSignature - OurrefundTxSig64 [64]byte } @@ -1905,7 +1890,6 @@ func NewDlcContractAckMsg(contract *DlcContract, msg.PeerIdx = contract.PeerIdx msg.Idx = contract.TheirIdx msg.SettlementSignatures = signatures - msg.OurrefundTxSig64 = OurrefundTxSig64 return *msg } diff --git a/qln/dlc.go b/qln/dlc.go index 9a540c41f..70c77ba55 100644 --- a/qln/dlc.go +++ b/qln/dlc.go @@ -224,10 +224,6 @@ func (nd *LitNode) AcceptDlc(cIdx uint64) error { return } - - //----------------------------------------------- - - refundTx, err := lnutil.RefundTx(c) if err != nil { logging.Errorf("Error of RefundTx: %s", err.Error()) @@ -248,9 +244,7 @@ func (nd *LitNode) AcceptDlc(cIdx uint64) error { nd.DlcManager.SaveContract(c) return } - - // //------------------------------------------------ - + msg := lnutil.NewDlcOfferAcceptMsg(c, sigs) c.Status = lnutil.ContractStatusAccepted @@ -588,8 +582,6 @@ func (nd *LitNode) BuildDlcFundingTransaction(c *lnutil.DlcContract) (wire.MsgTx } - - //==================================================== // Here can be a situation when peers have different number of inputs. From 42c809132cc84331ad796383d585d443290815e5 Mon Sep 17 00:00:00 2001 From: infografx Date: Fri, 20 Sep 2019 15:34:39 +0300 Subject: [PATCH 18/24] A little fix. --- lnutil/dlclib.go | 14 +++++++------- lnutil/msglib.go | 8 ++++---- qln/dlc.go | 8 ++++---- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lnutil/dlclib.go b/lnutil/dlclib.go index db2d7da49..bd9214a6c 100644 --- a/lnutil/dlclib.go +++ b/lnutil/dlclib.go @@ -70,7 +70,7 @@ type DlcContract struct { // Pubkey used in the funding multisig output OurFundMultisigPub, TheirFundMultisigPub [33]byte //OurRevokePub, TheirRevokePub [33]byte - OurRevokePKH, TheirRevokePKH [20]byte + OurRefundPKH, TheirRefundPKH [20]byte OurrefundTxSig64, TheirrefundTxSig64 [64]byte // Pubkey to be used in the commit script (combined with oracle pubkey // or CSV timeout) @@ -170,8 +170,8 @@ func DlcContractFromBytes(b []byte) (*DlcContract, error) { copy(c.OurFundMultisigPub[:], buf.Next(33)) copy(c.TheirFundMultisigPub[:], buf.Next(33)) - copy(c.OurRevokePKH[:], buf.Next(20)) - copy(c.TheirRevokePKH[:], buf.Next(20)) + copy(c.OurRefundPKH[:], buf.Next(20)) + copy(c.TheirRefundPKH[:], buf.Next(20)) copy(c.OurrefundTxSig64[:], buf.Next(64)) copy(c.TheirrefundTxSig64[:], buf.Next(64)) @@ -286,8 +286,8 @@ func (self *DlcContract) Bytes() []byte { buf.Write(self.OurFundMultisigPub[:]) buf.Write(self.TheirFundMultisigPub[:]) - buf.Write(self.OurRevokePKH[:]) - buf.Write(self.TheirRevokePKH[:]) + buf.Write(self.OurRefundPKH[:]) + buf.Write(self.TheirRefundPKH[:]) buf.Write(self.OurrefundTxSig64[:]) buf.Write(self.TheirrefundTxSig64[:]) @@ -637,11 +637,11 @@ func RefundTx(c *DlcContract) (*wire.MsgTx, error) { txin.Sequence = 0 tx.AddTxIn(txin) - ourRefScript := DirectWPKHScriptFromPKH(c.OurRevokePKH) + ourRefScript := DirectWPKHScriptFromPKH(c.OurRefundPKH) ourOutput := wire.NewTxOut(c.OurFundingAmount - fee, ourRefScript) tx.AddTxOut(ourOutput) - theirRefScript := DirectWPKHScriptFromPKH(c.TheirRevokePKH) + theirRefScript := DirectWPKHScriptFromPKH(c.TheirRefundPKH) theirOutput := wire.NewTxOut(c.TheirFundingAmount - fee, theirRefScript) tx.AddTxOut(theirOutput) diff --git a/lnutil/msglib.go b/lnutil/msglib.go index c4e3ce919..987cade6f 100644 --- a/lnutil/msglib.go +++ b/lnutil/msglib.go @@ -1745,7 +1745,7 @@ type DlcOfferAcceptMsg struct { // The Pubkey to be used to in the contract settlement OurPayoutBase [33]byte //OurRevokePub [33]byte - OurRevokePKH [20]byte + OurRefundPKH [20]byte OurrefundTxSig64 [64]byte // The PKH to be paid to in the contract settlement OurPayoutPKH [20]byte @@ -1768,7 +1768,7 @@ func NewDlcOfferAcceptMsg(contract *DlcContract, msg.OurChangePKH = contract.OurChangePKH msg.OurFundMultisigPub = contract.OurFundMultisigPub msg.OurPayoutBase = contract.OurPayoutBase - msg.OurRevokePKH = contract.OurRevokePKH + msg.OurRefundPKH = contract.OurRefundPKH msg.OurrefundTxSig64 = contract.OurrefundTxSig64 msg.OurPayoutPKH = contract.OurPayoutPKH msg.SettlementSignatures = signatures @@ -1795,7 +1795,7 @@ func NewDlcOfferAcceptMsgFromBytes(b []byte, copy(msg.OurChangePKH[:], buf.Next(20)) copy(msg.OurFundMultisigPub[:], buf.Next(33)) copy(msg.OurPayoutBase[:], buf.Next(33)) - copy(msg.OurRevokePKH[:], buf.Next(20)) + copy(msg.OurRefundPKH[:], buf.Next(20)) copy(msg.OurrefundTxSig64[:], buf.Next(64)) copy(msg.OurPayoutPKH[:], buf.Next(20)) @@ -1835,7 +1835,7 @@ func (msg DlcOfferAcceptMsg) Bytes() []byte { buf.Write(msg.OurChangePKH[:]) buf.Write(msg.OurFundMultisigPub[:]) buf.Write(msg.OurPayoutBase[:]) - buf.Write(msg.OurRevokePKH[:]) + buf.Write(msg.OurRefundPKH[:]) buf.Write(msg.OurrefundTxSig64[:]) buf.Write(msg.OurPayoutPKH[:]) diff --git a/qln/dlc.go b/qln/dlc.go index 70c77ba55..d665005f7 100644 --- a/qln/dlc.go +++ b/qln/dlc.go @@ -110,7 +110,7 @@ func (nd *LitNode) OfferDlc(peerIdx uint32, cIdx uint64) error { } wal, _ := nd.SubWallet[c.CoinType] - c.OurRevokePKH, err = wal.NewAdr() + c.OurRefundPKH, err = wal.NewAdr() msg := lnutil.NewDlcOfferMsg(peerIdx, c) c.Status = lnutil.ContractStatusOfferedByMe @@ -213,7 +213,7 @@ func (nd *LitNode) AcceptDlc(cIdx uint64) error { copy(c.OurPayoutPKH[:], btcutil.Hash160(ourPayoutPKHKey[:])) wal, _ := nd.SubWallet[c.CoinType] - c.OurRevokePKH, err = wal.NewAdr() + c.OurRefundPKH, err = wal.NewAdr() // Now we can sign the division sigs, err := nd.SignSettlementDivisions(c) @@ -275,7 +275,7 @@ func (nd *LitNode) DlcOfferHandler(msg lnutil.DlcOfferMsg, peer *RemotePeer) { //c.TheirRevokePub = msg.Contract.OurRevokePub - c.TheirRevokePKH = msg.Contract.OurRevokePKH + c.TheirRefundPKH = msg.Contract.OurRefundPKH c.Division = make([]lnutil.DlcContractDivision, len(msg.Contract.Division)) for i := 0; i < len(msg.Contract.Division); i++ { @@ -336,7 +336,7 @@ func (nd *LitNode) DlcAcceptHandler(msg lnutil.DlcOfferAcceptMsg, peer *RemotePe c.TheirPayoutBase = msg.OurPayoutBase c.TheirPayoutPKH = msg.OurPayoutPKH c.TheirIdx = msg.OurIdx - c.TheirRevokePKH = msg.OurRevokePKH + c.TheirRefundPKH = msg.OurRefundPKH c.TheirrefundTxSig64 = msg.OurrefundTxSig64 //------------------------------------------ From 89a32d078a23f705fbab917da8b5ece3e8d50d11 Mon Sep 17 00:00:00 2001 From: infografx Date: Mon, 30 Sep 2019 14:15:27 +0300 Subject: [PATCH 19/24] Multiple oracles support added. --- cmd/lit-af/dlccmds.go | 78 ++++++++++- consts/consts.go | 1 + dlc/contract.go | 79 +++++++++-- docs/execute-dlc-litaf.md | 7 + litrpc/dlccmds.go | 33 ++++- lnutil/dlclib.go | 85 ++++++++---- qln/dlc.go | 71 +++++++--- test/itest_dlc.py | 283 ++++++++++++++++++-------------------- test/itest_dlcrefund.py | 105 ++++++-------- test/testlib.py | 4 +- test/tests.txt | 9 +- 11 files changed, 470 insertions(+), 285 deletions(-) diff --git a/cmd/lit-af/dlccmds.go b/cmd/lit-af/dlccmds.go index 861751892..c055575f1 100644 --- a/cmd/lit-af/dlccmds.go +++ b/cmd/lit-af/dlccmds.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" "time" + "errors" "github.com/fatih/color" "github.com/mit-dci/lit/litrpc" @@ -122,7 +123,9 @@ var contractCommand = &Command{ "Sets the cointype of a contract"), fmt.Sprintf("%-20s %s", lnutil.White("setfeeperbyte"), - "Sets the fee per byte for a contract"), + fmt.Sprintf("%-20s %s", + lnutil.White("setoraclesnumber"), + "Sets the oracles number for a contract"), fmt.Sprintf("%-20s %s", lnutil.White("offer"), "Offer a draft contract to one of your peers"), @@ -321,11 +324,27 @@ var setContractFeePerByteCommand = &Command{ lnutil.White("cid"), "The ID of the contract"), fmt.Sprintf("%-10s %s", - lnutil.White("cointype"), + lnutil.White("feeperbyte"), "The fee per byte in satoshi to use for the contract"), ), ShortDescription: "Sets the fee per byte in satoshi to use for the contract\n", } + +var setContractOraclesNumberCommand = &Command{ + Format: fmt.Sprintf("%s%s\n", lnutil.White("dlc contract setoraclesnumber"), + lnutil.ReqColor("cid", "oraclesnumber")), + Description: fmt.Sprintf("%s\n%s\n%s\n", + "Sets the oracles number to use for the contract", + fmt.Sprintf("%-10s %s", + lnutil.White("cid"), + "The ID of the contract"), + fmt.Sprintf("%-10s %s", + lnutil.White("oraclesnumber"), + "The oracles number to use for the contract"), + ), + ShortDescription: "Sets a number of oracles required for the contract\n", +} + var declineContractCommand = &Command{ Format: fmt.Sprintf("%s%s\n", lnutil.White("dlc contract decline"), lnutil.ReqColor("cid")), @@ -547,7 +566,11 @@ func (lc *litAfClient) DlcContract(textArgs []string) error { if cmd == "setfeeperbyte" { return lc.DlcSetContractFeePerByte(textArgs) - } + } + + if cmd == "setoraclesnumber" { + return lc.DlcSetContractOraclesNumber(textArgs) + } if cmd == "offer" { return lc.DlcOfferContract(textArgs) @@ -914,6 +937,45 @@ func (lc *litAfClient) DlcSetContractFeePerByte(textArgs []string) error { + +func (lc *litAfClient) DlcSetContractOraclesNumber(textArgs []string) error { + stopEx, err := CheckHelpCommand(setContractOraclesNumberCommand, textArgs, 2) + if err != nil || stopEx { + return err + } + + args := new(litrpc.SetContractOraclesNumberArgs) + reply := new(litrpc.SetContractOraclesNumberReply) + + cIdx, err := strconv.ParseUint(textArgs[0], 10, 64) + if err != nil { + return err + } + OraclesNumber, err := strconv.ParseUint(textArgs[1], 10, 64) + if err != nil { + return err + } + + if OraclesNumber > 1 { + return errors.New("Multiple oracles supported only from RPC cals.") + } + + args.CIdx = cIdx + args.OraclesNumber = uint32(OraclesNumber) + + err = lc.Call("LitRPC.SetContractOraclesNumber", args, reply) + if err != nil { + return err + } + + fmt.Fprint(color.Output, "SetContractOraclesNumber set successfully\n") + + return nil +} + + + + func (lc *litAfClient) DlcSetContractDivision(textArgs []string) error { stopEx, err := CheckHelpCommand(setContractDivisionCommand, textArgs, 3) if err != nil || stopEx { @@ -1067,13 +1129,16 @@ func PrintContract(c *lnutil.DlcContract) { fmt.Fprintf(color.Output, "%-30s : %d\n", lnutil.White("Index"), c.Idx) fmt.Fprintf(color.Output, "%-30s : [%x...%x...%x]\n", lnutil.White("Oracle public key"), - c.OracleA[:2], c.OracleA[15:16], c.OracleA[31:]) + c.OracleA[0][:2], c.OracleA[0][15:16], c.OracleA[0][31:]) fmt.Fprintf(color.Output, "%-30s : [%x...%x...%x]\n", lnutil.White("Oracle R-point"), c.OracleR[:2], - c.OracleR[15:16], c.OracleR[31:]) + c.OracleR[0][15:16], c.OracleR[0][31:]) fmt.Fprintf(color.Output, "%-30s : %s\n", lnutil.White("Settlement time"), time.Unix(int64(c.OracleTimestamp), 0).UTC().Format(time.UnixDate)) + fmt.Fprintf(color.Output, "%-30s : %s\n", + lnutil.White("Refund time"), + time.Unix(int64(c.RefundTimestamp), 0).UTC().Format(time.UnixDate)) fmt.Fprintf(color.Output, "%-30s : %d\n", lnutil.White("Funded by us"), c.OurFundingAmount) fmt.Fprintf(color.Output, "%-30s : %d\n", @@ -1082,6 +1147,9 @@ func PrintContract(c *lnutil.DlcContract) { lnutil.White("Coin type"), c.CoinType) fmt.Fprintf(color.Output, "%-30s : %d\n", lnutil.White("Fee per byte"), c.FeePerByte) + fmt.Fprintf(color.Output, "%-30s : %d\n", + lnutil.White("Oracles number"), c.OraclesNumber) + peer := "None" if c.PeerIdx > 0 { diff --git a/consts/consts.go b/consts/consts.go index b4dbe7d4e..9d076e2e0 100644 --- a/consts/consts.go +++ b/consts/consts.go @@ -24,4 +24,5 @@ const ( QcStateFee = 10 // fixqcstatefee DefaultLockTime = 500 //default lock time DlcSettlementTxFee = 1000 + MaxOraclesNumber = 8 ) diff --git a/dlc/contract.go b/dlc/contract.go index 7ffb58f86..3ba479e66 100644 --- a/dlc/contract.go +++ b/dlc/contract.go @@ -4,10 +4,12 @@ import ( "fmt" "github.com/mit-dci/lit/lnutil" + "github.com/mit-dci/lit/consts" ) const COINTYPE_NOT_SET = ^uint32(0) // Max Uint const FEEPERBYTE_NOT_SET = ^uint32(0) // Max Uint +const ORACLESNUMBER_NOT_SET = ^uint32(0) // Max Uint // AddContract starts a new draft contract func (mgr *DlcManager) AddContract() (*lnutil.DlcContract, error) { @@ -17,6 +19,7 @@ func (mgr *DlcManager) AddContract() (*lnutil.DlcContract, error) { c.Status = lnutil.ContractStatusDraft c.CoinType = COINTYPE_NOT_SET c.FeePerByte = FEEPERBYTE_NOT_SET + c.OraclesNumber = ORACLESNUMBER_NOT_SET err = mgr.SaveContract(c) if err != nil { return nil, err @@ -28,7 +31,7 @@ func (mgr *DlcManager) AddContract() (*lnutil.DlcContract, error) { // SetContractOracle assigns a particular oracle to a contract - used for // determining which pubkey A to use and can also allow for fetching R-points // automatically when the oracle was imported from a REST api -func (mgr *DlcManager) SetContractOracle(cIdx, oIdx uint64) error { +func (mgr *DlcManager) SetContractOracle(cIdx uint64, oIdx []uint64) error { c, err := mgr.LoadContract(cIdx) if err != nil { @@ -38,13 +41,27 @@ func (mgr *DlcManager) SetContractOracle(cIdx, oIdx uint64) error { return fmt.Errorf("You cannot change or set the oracle unless the" + " contract is in Draft state") } - o, err := mgr.LoadOracle(oIdx) - if err != nil { - return err + + + if c.OraclesNumber == ORACLESNUMBER_NOT_SET { + return fmt.Errorf("You need to set the OraclesNumber variable.") + } + + if len(oIdx) < int(c.OraclesNumber) { + return fmt.Errorf("You cannot set the number of oracles in less than" + + " in a variable OraclesNumber") } - c.OracleA = o.A - // Reset the R point when changing the oracle - c.OracleR = [33]byte{} + + for i := uint64(1); i <= uint64(c.OraclesNumber); i++ { + o, err := mgr.LoadOracle(i) + if err != nil { + return err + } + c.OracleA[i-1] = o.A + // Reset R point after oracle setting + c.OracleR[i-1] = [33]byte{} + } + mgr.SaveContract(c) return nil } @@ -61,8 +78,14 @@ func (mgr *DlcManager) SetContractSettlementTime(cIdx, time uint64) error { " unless the contract is in Draft state") } c.OracleTimestamp = time + + fmt.Printf("c.OracleTimestamp %d \n", c.OracleTimestamp) + // Reset the R point - c.OracleR = [33]byte{} + for i, _ := range c.OracleR{ + c.OracleR[i] = [33]byte{} + } + mgr.SaveContract(c) return nil } @@ -89,6 +112,7 @@ func (mgr *DlcManager) SetContractRefundTime(cIdx, time uint64) error { // SetContractDatafeed will automatically fetch the R-point from the REST API, // if an oracle is imported from a REST API. You need to set the settlement time // first, because the R point is a key unique for the time and feed +// TODO change for multiple oracles. func (mgr *DlcManager) SetContractDatafeed(cIdx, feed uint64) error { c, err := mgr.LoadContract(cIdx) if err != nil { @@ -105,12 +129,12 @@ func (mgr *DlcManager) SetContractDatafeed(cIdx, feed uint64) error { " otherwise no R point can be retrieved for the feed") } - o, err := mgr.FindOracleByKey(c.OracleA) + o, err := mgr.FindOracleByKey(c.OracleA[0]) if err != nil { return err } - c.OracleR, err = o.FetchRPoint(feed, c.OracleTimestamp) + c.OracleR[0], err = o.FetchRPoint(feed, c.OracleTimestamp) if err != nil { return err } @@ -124,7 +148,7 @@ func (mgr *DlcManager) SetContractDatafeed(cIdx, feed uint64) error { // SetContractRPoint allows you to manually set the R-point key if an oracle is // not imported from a REST API -func (mgr *DlcManager) SetContractRPoint(cIdx uint64, rPoint [33]byte) error { +func (mgr *DlcManager) SetContractRPoint(cIdx uint64, rPoint [][33]byte) error { c, err := mgr.LoadContract(cIdx) if err != nil { return err @@ -135,7 +159,11 @@ func (mgr *DlcManager) SetContractRPoint(cIdx uint64, rPoint [33]byte) error { " contract is in Draft state") } - c.OracleR = rPoint + for i:=uint32(0); i < c.OraclesNumber; i++{ + c.OracleR[i] = rPoint[i] + } + + err = mgr.SaveContract(c) if err != nil { @@ -274,3 +302,30 @@ func (mgr *DlcManager) SetContractFeePerByte(cIdx uint64, feeperbyte uint32) err return nil } + + + +//SetContractOraclesNumber sets a number of oracles required for the contract. +func (mgr *DlcManager) SetContractOraclesNumber(cIdx uint64, oraclesNumber uint32) error { + c, err := mgr.LoadContract(cIdx) + if err != nil { + return err + } + + + if c.Status != lnutil.ContractStatusDraft { + return fmt.Errorf("You cannot change or set the OraclesNumber unless" + + " the contract is in Draft state") + } + + + if oraclesNumber > consts.MaxOraclesNumber{ + return fmt.Errorf("You cannot set OraclesNumber greater that %d (consts.MaxOraclesNumber)", consts.MaxOraclesNumber) + } + + c.OraclesNumber = oraclesNumber + + mgr.SaveContract(c) + + return nil +} diff --git a/docs/execute-dlc-litaf.md b/docs/execute-dlc-litaf.md index 45dc25ae0..9ef9e02c1 100644 --- a/docs/execute-dlc-litaf.md +++ b/docs/execute-dlc-litaf.md @@ -52,6 +52,13 @@ First, we create a new draft contract dlc contract new ``` +Multiple oracles supported only from RPC cals. +With lit-af utility you have to set oracles number to 1. + +``` +dlc contract setoraclesnumber 1 1 +``` + Then, we configure the contract to use the oracle we added in step 2. Remember, if this oracle got a different ID, you have to adjust the second parameter to this call. ``` diff --git a/litrpc/dlccmds.go b/litrpc/dlccmds.go index 0dd2f7913..33b21f461 100644 --- a/litrpc/dlccmds.go +++ b/litrpc/dlccmds.go @@ -6,6 +6,7 @@ import ( "github.com/mit-dci/lit/dlc" "github.com/mit-dci/lit/lnutil" + "github.com/mit-dci/lit/consts" ) type ListOraclesArgs struct { @@ -143,7 +144,7 @@ func (r *LitRPC) GetContract(args GetContractArgs, type SetContractOracleArgs struct { CIdx uint64 - OIdx uint64 + OIdx []uint64 } type SetContractOracleReply struct { @@ -190,7 +191,7 @@ func (r *LitRPC) SetContractDatafeed(args SetContractDatafeedArgs, type SetContractRPointArgs struct { CIdx uint64 - RPoint [33]byte + RPoint [][33]byte } type SetContractRPointReply struct { @@ -343,6 +344,7 @@ type SetContractFeePerByteReply struct { Success bool } + // SetContractFeePerByte sets the fee per byte for the contract. func (r *LitRPC) SetContractFeePerByte(args SetContractFeePerByteArgs, reply *SetContractFeePerByteReply) error { @@ -357,7 +359,30 @@ func (r *LitRPC) SetContractFeePerByte(args SetContractFeePerByteArgs, return nil } -//---------------------------------------------------------- + + +type SetContractOraclesNumberArgs struct { + CIdx uint64 + OraclesNumber uint32 +} + +type SetContractOraclesNumberReply struct { + Success bool +} + + +func (r *LitRPC) SetContractOraclesNumber(args SetContractOraclesNumberArgs, + reply *SetContractOraclesNumberReply) error { + var err error + + err = r.Node.DlcManager.SetContractOraclesNumber(args.CIdx, args.OraclesNumber) + if err != nil { + return err + } + + reply.Success = true + return nil +} type GetContractDivisionArgs struct { CIdx uint64 @@ -450,7 +475,7 @@ func (r *LitRPC) ContractRespond(args ContractRespondArgs, reply *ContractRespon type SettleContractArgs struct { CIdx uint64 OracleValue int64 - OracleSig [32]byte + OracleSig [consts.MaxOraclesNumber][32]byte } type SettleContractReply struct { diff --git a/lnutil/dlclib.go b/lnutil/dlclib.go index bd9214a6c..843388fbd 100644 --- a/lnutil/dlclib.go +++ b/lnutil/dlclib.go @@ -16,6 +16,7 @@ import ( "github.com/mit-dci/lit/btcutil/txscript" "github.com/mit-dci/lit/btcutil/txsort" "github.com/mit-dci/lit/sig64" + "github.com/mit-dci/lit/consts" ) @@ -55,8 +56,10 @@ type DlcContract struct { CoinType uint32 // Fee per byte FeePerByte uint32 + // It is a number of oracles required for the contract. + OraclesNumber uint32 // Pub keys of the oracle and the R point used in the contract - OracleA, OracleR [33]byte + OracleA, OracleR [consts.MaxOraclesNumber][33]byte // The time we expect the oracle to publish OracleTimestamp uint64 // The time after which the refund transaction becomes valid. @@ -121,9 +124,6 @@ func DlcContractFromBytes(b []byte) (*DlcContract, error) { } c.TheirIdx = theirIdx - copy(c.OracleA[:], buf.Next(33)) - copy(c.OracleR[:], buf.Next(33)) - peerIdx, err := wire.ReadVarInt(buf, 0) if err != nil { logging.Errorf("Error while deserializing varint for peerIdx: %s", err.Error()) @@ -143,7 +143,22 @@ func DlcContractFromBytes(b []byte) (*DlcContract, error) { logging.Errorf("Error while deserializing varint for feePerByte: %s", err.Error()) return nil, err } - c.FeePerByte = uint32(feePerByte) + c.FeePerByte = uint32(feePerByte) + + + oraclesNumber, err := wire.ReadVarInt(buf, 0) + if err != nil { + logging.Errorf("Error while deserializing varint for oraclesNumber: %s", err.Error()) + return nil, err + } + + c.OraclesNumber = uint32(oraclesNumber) + + for i := uint64(0); i < uint64(consts.MaxOraclesNumber); i++ { + + copy(c.OracleA[i][:], buf.Next(33)) + copy(c.OracleR[i][:], buf.Next(33)) + } c.OracleTimestamp, err = wire.ReadVarInt(buf, 0) if err != nil { @@ -271,11 +286,23 @@ func (self *DlcContract) Bytes() []byte { wire.WriteVarInt(&buf, 0, uint64(self.Idx)) wire.WriteVarInt(&buf, 0, uint64(self.TheirIdx)) - buf.Write(self.OracleA[:]) - buf.Write(self.OracleR[:]) wire.WriteVarInt(&buf, 0, uint64(self.PeerIdx)) wire.WriteVarInt(&buf, 0, uint64(self.CoinType)) wire.WriteVarInt(&buf, 0, uint64(self.FeePerByte)) + + wire.WriteVarInt(&buf, 0, uint64(self.OraclesNumber)) + + //fmt.Printf("Bytes() self.OraclesNumber: %d\n", self.OraclesNumber) + + + for i := uint64(0); i < uint64(consts.MaxOraclesNumber); i++ { + + //fmt.Printf("Bytes() i: %d\n", i) + + buf.Write(self.OracleA[i][:]) + buf.Write(self.OracleR[i][:]) + } + wire.WriteVarInt(&buf, 0, uint64(self.OracleTimestamp)) wire.WriteVarInt(&buf, 0, uint64(self.RefundTimestamp)) wire.WriteVarInt(&buf, 0, uint64(self.OurFundingAmount)) @@ -377,8 +404,9 @@ func PrintTx(tx *wire.MsgTx) { // DlcOutput returns a Txo for a particular value that pays to // (PubKeyPeer+PubKeyOracleSig or (OurPubKey and TimeDelay)) -func DlcOutput(pkPeer, pkOracleSig, pkOurs [33]byte, value int64) *wire.TxOut { - scriptBytes := DlcCommitScript(pkPeer, pkOracleSig, pkOurs, 5) +func DlcOutput(pkPeer, pkOurs [33]byte, oraclesSigPub [][33]byte, value int64) *wire.TxOut { + + scriptBytes := DlcCommitScript(pkPeer, pkOurs, oraclesSigPub, 5) scriptBytes = P2WSHify(scriptBytes) return wire.NewTxOut(value, scriptBytes) @@ -390,9 +418,15 @@ func DlcOutput(pkPeer, pkOracleSig, pkOurs [33]byte, value int64) *wire.TxOut { // signature and their own private key to claim the funds from the output. // However, if they send the wrong one, they won't be able to claim the funds // and we can claim them once the time delay has passed. -func DlcCommitScript(pubKeyPeer, pubKeyOracleSig, ourPubKey [33]byte, delay uint16) []byte { +func DlcCommitScript(pubKeyPeer, ourPubKey [33]byte, oraclesSigPub [][33]byte, delay uint16) []byte { // Combine pubKey and Oracle Sig - combinedPubKey := CombinePubs(pubKeyPeer, pubKeyOracleSig) + + combinedPubKey := CombinePubs(pubKeyPeer, oraclesSigPub[0]) + + for i := 1; i < len(oraclesSigPub); i++ { + combinedPubKey = CombinePubs(combinedPubKey, oraclesSigPub[i]) + } + return CommitScript(combinedPubKey, ourPubKey, delay) } @@ -586,33 +620,38 @@ func SettlementTx(c *DlcContract, d DlcContractDivision, binary.Write(&buf, binary.BigEndian, uint64(0)) binary.Write(&buf, binary.BigEndian, uint64(0)) binary.Write(&buf, binary.BigEndian, d.OracleValue) - oracleSigPub, err := DlcCalcOracleSignaturePubKey(buf.Bytes(), - c.OracleA, c.OracleR) - if err != nil { - return nil, err + + + var oraclesSigPub [][33]byte + + for i:=uint32(0); i < c.OraclesNumber; i++ { + + res, err := DlcCalcOracleSignaturePubKey(buf.Bytes(),c.OracleA[i], c.OracleR[i]) + if err != nil { + return nil, err + } + + oraclesSigPub = append(oraclesSigPub, res) + } // Ours = the one we generate & sign. Theirs (ours = false) = the one they // generated, so we can use their sigs if ours { if valueTheirs > 0 { - tx.AddTxOut(DlcOutput(c.TheirPayoutBase, oracleSigPub, - c.OurPayoutBase, valueTheirs)) + tx.AddTxOut(DlcOutput(c.TheirPayoutBase, c.OurPayoutBase, oraclesSigPub, valueTheirs)) } if valueOurs > 0 { - tx.AddTxOut(wire.NewTxOut(valueOurs, - DirectWPKHScriptFromPKH(c.OurPayoutPKH))) + tx.AddTxOut(wire.NewTxOut(valueOurs, DirectWPKHScriptFromPKH(c.OurPayoutPKH))) } } else { if valueOurs > 0 { - tx.AddTxOut(DlcOutput(c.OurPayoutBase, oracleSigPub, - c.TheirPayoutBase, valueOurs)) + tx.AddTxOut(DlcOutput(c.OurPayoutBase, c.TheirPayoutBase, oraclesSigPub, valueOurs)) } if valueTheirs > 0 { - tx.AddTxOut(wire.NewTxOut(valueTheirs, - DirectWPKHScriptFromPKH(c.TheirPayoutPKH))) + tx.AddTxOut(wire.NewTxOut(valueTheirs, DirectWPKHScriptFromPKH(c.TheirPayoutPKH))) } } diff --git a/qln/dlc.go b/qln/dlc.go index d665005f7..caf8c944a 100644 --- a/qln/dlc.go +++ b/qln/dlc.go @@ -12,6 +12,7 @@ import ( "github.com/mit-dci/lit/portxo" "github.com/mit-dci/lit/sig64" "github.com/mit-dci/lit/wire" + "github.com/mit-dci/lit/consts" ) func (nd *LitNode) AddContract() (*lnutil.DlcContract, error) { @@ -40,12 +41,26 @@ func (nd *LitNode) OfferDlc(peerIdx uint32, cIdx uint64) error { var nullBytes [33]byte // Check if everything's set - if c.OracleA == nullBytes { - return fmt.Errorf("You need to set an oracle for the contract before offering it") + + + if c.OraclesNumber == dlc.ORACLESNUMBER_NOT_SET { + return fmt.Errorf("You need to set an oracles number for the contract before offering it") } - if c.OracleR == nullBytes { - return fmt.Errorf("You need to set an R-point for the contract before offering it") + if c.OraclesNumber > consts.MaxOraclesNumber { + return fmt.Errorf("The number of oracles have to be less than 8.") + } + + for o := uint32(0); o < c.OraclesNumber; o++ { + + if c.OracleA[o] == nullBytes { + return fmt.Errorf("You need to set all %d oracls for the contract before offering it", c.OraclesNumber) + } + + if c.OracleR[o] == nullBytes { + return fmt.Errorf("You need to set all %d R-points for the contract before offering it", c.OraclesNumber) + } + } if c.OracleTimestamp == 0 { @@ -286,8 +301,16 @@ func (nd *LitNode) DlcOfferHandler(msg lnutil.DlcOfferMsg, peer *RemotePeer) { // Copy c.CoinType = msg.Contract.CoinType c.FeePerByte = msg.Contract.FeePerByte - c.OracleA = msg.Contract.OracleA - c.OracleR = msg.Contract.OracleR + + c.OraclesNumber = msg.Contract.OraclesNumber + + for i:=uint32(0); i < c.OraclesNumber; i++ { + + c.OracleA[i] = msg.Contract.OracleA[i] + c.OracleR[i] = msg.Contract.OracleR[i] + + } + c.OracleTimestamp = msg.Contract.OracleTimestamp c.RefundTimestamp = msg.Contract.RefundTimestamp @@ -339,7 +362,6 @@ func (nd *LitNode) DlcAcceptHandler(msg lnutil.DlcOfferAcceptMsg, peer *RemotePe c.TheirRefundPKH = msg.OurRefundPKH c.TheirrefundTxSig64 = msg.OurrefundTxSig64 - //------------------------------------------ c.Status = lnutil.ContractStatusAccepted err = nd.DlcManager.SaveContract(c) @@ -354,8 +376,6 @@ func (nd *LitNode) DlcAcceptHandler(msg lnutil.DlcOfferAcceptMsg, peer *RemotePe return err } - //------------------------------------------ - wal, _ := nd.SubWallet[c.CoinType] refundTx, err := lnutil.RefundTx(c) @@ -385,8 +405,6 @@ func (nd *LitNode) DlcAcceptHandler(msg lnutil.DlcOfferAcceptMsg, peer *RemotePe return err } - //------------------------------------------ - outMsg := lnutil.NewDlcContractAckMsg(c, sigs, c.OurrefundTxSig64) c.Status = lnutil.ContractStatusAcknowledged @@ -582,7 +600,6 @@ func (nd *LitNode) BuildDlcFundingTransaction(c *lnutil.DlcContract) (wire.MsgTx } - //==================================================== // Here can be a situation when peers have different number of inputs. // Therefore we have to calculate fees for each peer separately. @@ -654,7 +671,7 @@ func (nd *LitNode) FundContract(c *lnutil.DlcContract) error { return nil } -func (nd *LitNode) SettleContract(cIdx uint64, oracleValue int64, oracleSig [32]byte) ([32]byte, [32]byte, error) { +func (nd *LitNode) SettleContract(cIdx uint64, oracleValue int64, oraclesSig[consts.MaxOraclesNumber][32]byte) ([32]byte, [32]byte, error) { c, err := nd.DlcManager.LoadContract(cIdx) if err != nil { @@ -772,16 +789,28 @@ func (nd *LitNode) SettleContract(cIdx uint64, oracleValue int64, oracleSig [32] kg.Step[2] = UseContractPayoutBase privSpend, _ := wal.GetPriv(kg) - pubSpend := wal.GetPub(kg) - privOracle, pubOracle := koblitz.PrivKeyFromBytes(koblitz.S256(), oracleSig[:]) - privContractOutput := lnutil.CombinePrivateKeys(privSpend, privOracle) - var pubOracleBytes [33]byte - copy(pubOracleBytes[:], pubOracle.SerializeCompressed()) - var pubSpendBytes [33]byte - copy(pubSpendBytes[:], pubSpend.SerializeCompressed()) + var pubOracleBytes [][33]byte + + privOracle0, pubOracle0 := koblitz.PrivKeyFromBytes(koblitz.S256(), oraclesSig[0][:]) + privContractOutput := lnutil.CombinePrivateKeys(privSpend, privOracle0) + + var pubOracleBytes0 [33]byte + copy(pubOracleBytes0[:], pubOracle0.SerializeCompressed()) + pubOracleBytes = append(pubOracleBytes, pubOracleBytes0) + + for i:=uint32(1); i < c.OraclesNumber; i++ { + + privOracle, pubOracle := koblitz.PrivKeyFromBytes(koblitz.S256(), oraclesSig[i][:]) + privContractOutput = lnutil.CombinePrivateKeys(privContractOutput, privOracle) + + var pubOracleBytes1 [33]byte + copy(pubOracleBytes1[:], pubOracle.SerializeCompressed()) + pubOracleBytes = append(pubOracleBytes, pubOracleBytes1) + + } - settleScript := lnutil.DlcCommitScript(c.OurPayoutBase, pubOracleBytes, c.TheirPayoutBase, 5) + settleScript := lnutil.DlcCommitScript(c.OurPayoutBase, c.TheirPayoutBase, pubOracleBytes , 5) err = nd.SignClaimTx(txClaim, settleTx.TxOut[0].Value, settleScript, privContractOutput, false) if err != nil { logging.Errorf("SettleContract SignClaimTx err %s", err.Error()) diff --git a/test/itest_dlc.py b/test/itest_dlc.py index ea00f4a22..69b68da26 100644 --- a/test/itest_dlc.py +++ b/test/itest_dlc.py @@ -17,21 +17,22 @@ def run_t(env, params): lit_funding_amt = params[0] contract_funding_amt = params[1] - oracle_value = params[2] - node_to_settle = params[3] - valueFullyOurs=params[4] - valueFullyTheirs=params[5] + oracles_number = params[2] + oracle_value = params[3] + node_to_settle = params[4] + valueFullyOurs=params[5] + valueFullyTheirs=params[6] - FundingTxVsize = params[6][0] - SettlementTxVsize = params[6][1] + FundingTxVsize = params[7][0] + SettlementTxVsize = params[7][1] - feeperbyte = params[7] + feeperbyte = params[8] - SetTxFeeOurs = params[8] - SetTxFeeTheirs = params[9] + SetTxFeeOurs = params[9] + SetTxFeeTheirs = params[10] - ClaimTxFeeOurs = params[10] - ClaimTxFeeTheirs = params[11] + ClaimTxFeeOurs = params[11] + ClaimTxFeeTheirs = params[12] bc = env.bitcoind @@ -39,9 +40,11 @@ def run_t(env, params): # Create oracles #------------ - env.new_oracle(1, oracle_value) # publishing interval is 1 second. + oracles = [] - oracle1 = env.oracles[0] + for i in range(oracles_number): + env.new_oracle(1, oracle_value) # publishing interval is 1 second. + oracles.append(env.oracles[i]) time.sleep(2) @@ -93,7 +96,6 @@ def run_t(env, params): env.generate_block() time.sleep(5) - print("Funding") bals1 = lit1.get_balance_info() print('new lit1 balance:', bals1['TxoTotal'], 'in txos,', bals1['ChanTotal'], 'in chans') bal1sum = bals1['TxoTotal'] + bals1['ChanTotal'] @@ -103,9 +105,6 @@ def run_t(env, params): lit_funding_amt *= 100000000 # to satoshi - - - bals2 = lit2.get_balance_info() print('new lit2 balance:', bals2['TxoTotal'], 'in txos,', bals2['ChanTotal'], 'in chans') bal2sum = bals2['TxoTotal'] + bals2['ChanTotal'] @@ -130,29 +129,32 @@ def run_t(env, params): #------------------------------------------ - # #------------ - # # Add oracles - # #------------ + #------------ + # Add oracles + #------------ res = lit1.rpc.ListOracles() assert len(res) != 0, "Initial lis of oracles must be empty" - - oracle1_pubkey = json.loads(oracle1.get_pubkey()) - assert len(oracle1_pubkey["A"]) == 66, "Wrong oracle1 pub key" - - oracle_res1 = lit1.rpc.AddOracle(Key=oracle1_pubkey["A"], Name="oracle1") - assert oracle_res1["Oracle"]["Idx"] == 1, "AddOracle does not works" - res = lit1.rpc.ListOracles(ListOraclesArgs={}) - assert len(res["Oracles"]) == 1, "ListOracles 1 does not works" + oracles_pubkey = [] + oidxs = [] + datasources = [] + + for oracle in oracles: + opk = json.loads(oracle.get_pubkey()) + oracles_pubkey.append(opk) - lit2.rpc.AddOracle(Key=oracle1_pubkey["A"], Name="oracle1") + oidx = lit1.rpc.AddOracle(Key=opk["A"], Name=opk["A"])["Oracle"]["Idx"] + oidxs.append(oidx) + lit2.rpc.AddOracle(Key=opk["A"], Name=opk["A"])["Oracle"]["Idx"] + datasources.append(json.loads(oracle.get_datasources())) - # #------------ - # # Now we have to create a contract in the lit1 node. - # #------------ + + #------------ + # Now we have to create a contract in the lit1 node. + #------------ contract = lit1.rpc.NewContract() @@ -162,12 +164,14 @@ def run_t(env, params): res = lit1.rpc.GetContract(Idx=1) assert res["Contract"]["Idx"] == 1, "GetContract does not works" - - res = lit1.rpc.SetContractOracle(CIdx=contract["Contract"]["Idx"], OIdx=oracle_res1["Oracle"]["Idx"]) + + res = lit1.rpc.SetContractOraclesNumber(CIdx=contract["Contract"]["Idx"], OraclesNumber=oracles_number) + assert res["Success"], "SetContractOraclesNumber does not works" + + res = lit1.rpc.SetContractOracle(CIdx=contract["Contract"]["Idx"], OIdx=oidxs) assert res["Success"], "SetContractOracle does not works" - datasources = json.loads(oracle1.get_datasources()) # Since the oracle publishes data every 1 second (we set this time above), # we increase the time for a point by 3 seconds. @@ -182,18 +186,29 @@ def run_t(env, params): res = lit1.rpc.SetContractRefundTime(CIdx=contract["Contract"]["Idx"], Time=settlement_time) assert res["Success"], "SetContractRefundTime does not works" + # we set settlement_time equal to refundtime, actually the refund transaction will be valid. + lit1.rpc.SetContractRefundTime(CIdx=contract["Contract"]["Idx"], Time=settlement_time) + res = lit1.rpc.ListContracts() assert res["Contracts"][contract["Contract"]["Idx"] - 1]["OracleTimestamp"] == settlement_time, "SetContractSettlementTime does not match settlement_time" - rpoint1 = oracle1.get_rpoint(datasources[0]["id"], settlement_time) decode_hex = codecs.getdecoder("hex_codec") - b_RPoint = decode_hex(json.loads(rpoint1)['R'])[0] - RPoint = [elem for elem in b_RPoint] + brpoints = [] + rpoints = [] + for oracle, datasource in zip(oracles, datasources): + res = oracle.get_rpoint(datasource[0]["id"], settlement_time) + print(res) + b_RPoint = decode_hex(json.loads(res)['R'])[0] + RPoint = [elem for elem in b_RPoint] + brpoints.append(RPoint) + rpoints.append(res) - res = lit1.rpc.SetContractRPoint(CIdx=contract["Contract"]["Idx"], RPoint=RPoint) + + res = lit1.rpc.SetContractRPoint(CIdx=contract["Contract"]["Idx"], RPoint=brpoints) assert res["Success"], "SetContractRpoint does not works" + lit1.rpc.SetContractCoinType(CIdx=contract["Contract"]["Idx"], CoinType = 257) res = lit1.rpc.GetContract(Idx=contract["Contract"]["Idx"]) assert res["Contract"]["CoinType"] == 257, "SetContractCoinType does not works" @@ -211,40 +226,25 @@ def run_t(env, params): assert res["Contract"]["OurFundingAmount"] == ourFundingAmount, "SetContractFunding does not works" assert res["Contract"]["TheirFundingAmount"] == theirFundingAmount, "SetContractFunding does not works" - print("Before SetContractDivision") - res = lit1.rpc.SetContractDivision(CIdx=contract["Contract"]["Idx"], ValueFullyOurs=valueFullyOurs, ValueFullyTheirs=valueFullyTheirs) assert res["Success"], "SetContractDivision does not works" - print("After SetContractDivision") - time.sleep(5) res = lit1.rpc.ListConnections() - print(res) - - print("Before OfferContract") res = lit1.rpc.OfferContract(CIdx=contract["Contract"]["Idx"], PeerIdx=lit1.get_peer_id(lit2)) assert res["Success"], "OfferContract does not works" - print("After OfferContract") - time.sleep(5) - - - print("Before ContractRespond") res = lit2.rpc.ContractRespond(AcceptOrDecline=True, CIdx=1) assert res["Success"], "ContractRespond on lit2 does not works" - print("After ContractRespond") - time.sleep(5) - #------------------------------------------ - + if deb_mod: print("ADDRESSES AFTER CONTRACT RESPOND") print("LIT1 Addresses") @@ -257,16 +257,9 @@ def run_t(env, params): print(bc.rpc.listaddressgroupings()) - # #------------------------------------------ - - - print("Before Generate Block") - env.generate_block() time.sleep(2) - print("After Generate Block") - print("Accept") bals1 = lit1.get_balance_info() print('new lit1 balance:', bals1['TxoTotal'], 'in txos,', bals1['ChanTotal'], 'in chans') @@ -287,47 +280,58 @@ def run_t(env, params): assert bal1sum == lit1_bal_after_accept, "lit1 Balance after contract accept does not match" assert bal2sum == lit2_bal_after_accept, "lit2 Balance after contract accept does not match" - oracle1_val = "" - oracle1_sig = "" + + OraclesSig = [] + OraclesVal = [] i = 0 while True: - res = oracle1.get_publication(json.loads(rpoint1)['R']) + + publications_result = [] + + for o, r in zip(oracles, rpoints): + publications_result.append(o.get_publication(json.loads(r)['R'])) + + time.sleep(5) i += 1 if i>4: assert False, "Error: Oracle does not publish data" try: - oracle1_val = json.loads(res)["value"] - oracle1_sig = json.loads(res)["signature"] + + for pr in publications_result: + oracle_val = json.loads(pr)["value"] + OraclesVal.append(oracle_val) + oracle_sig = json.loads(pr)["signature"] + b_OracleSig = decode_hex(oracle_sig)[0] + OracleSig = [elem for elem in b_OracleSig] + OraclesSig.append(OracleSig) + break except BaseException as e: print(e) next + # Oracles have to publish the same value + vEqual = True + nTemp = OraclesVal[0] + for v in OraclesVal: + if nTemp != v: + vEqual = False + break; + assert vEqual, "Oracles publish different values" - b_OracleSig = decode_hex(oracle1_sig)[0] - OracleSig = [elem for elem in b_OracleSig] - - - print("Before SettleContract") - time.sleep(5) - - - res = env.lits[node_to_settle].rpc.SettleContract(CIdx=contract["Contract"]["Idx"], OracleValue=oracle1_val, OracleSig=OracleSig) + res = env.lits[node_to_settle].rpc.SettleContract(CIdx=contract["Contract"]["Idx"], OracleValue=OraclesVal[0], OracleSig=OraclesSig) assert res["Success"], "SettleContract does not works." - time.sleep(5) - - print('After SettleContract:') - print(res) + time.sleep(5) try: env.generate_block(1) time.sleep(1) - env.generate_block(10) + env.generate_block(1) time.sleep(1) env.generate_block(1) time.sleep(1) @@ -335,6 +339,7 @@ def run_t(env, params): print("Exception After SettleContract: ") print(be) + time.sleep(2) #------------------------------------------ if deb_mod: @@ -374,16 +379,40 @@ def run_t(env, params): print('--------') - #------------------------------------------ - #------------------------------------------ - print("AFter Settle") - print("ORACLE VALUE:", oracle1_val, "; oracle signature:", oracle1_sig) + if deb_mod: + print("ADDRESSES AFTER SETTLE") + print("LIT1 Addresses") + print(pp.pprint(lit1.rpc.GetAddresses())) + + print("LIT2 Addresses") + print(pp.pprint(lit2.rpc.GetAddresses())) + + print("bitcoind Addresses") + print(pp.pprint(bc.rpc.listaddressgroupings())) + #------------------------------------------ + + + + print("=====START CONTRACT N1=====") + res = lit1.rpc.ListContracts() + #print(pp.pprint(res)) + print(res) + print("=====END CONTRACT N1=====") + + print("=====START CONTRACT N2=====") + res = lit2.rpc.ListContracts() + #print(pp.pprint(res)) + print(res) + print("=====END CONTRACT N2=====") + + + print("ORACLE VALUE:", OraclesVal[0], "; oracle signature:", OraclesVal[0]) valueOurs = 0 - valueOurs = env.lits[node_to_settle].rpc.GetContractDivision(CIdx=contract["Contract"]["Idx"],OracleValue=oracle1_val)['ValueOurs'] + valueOurs = env.lits[node_to_settle].rpc.GetContractDivision(CIdx=contract["Contract"]["Idx"],OracleValue=OraclesVal[0])['ValueOurs'] valueTheirs = contract_funding_amt * 2 - valueOurs print("valueOurs:", valueOurs, "; valueTheirs:", valueTheirs) @@ -434,36 +463,6 @@ def run_t(env, params): - - #------------------------------------------ - if deb_mod: - print("ADDRESSES AFTER SETTLE") - print("LIT1 Addresses") - print(pp.pprint(lit1.rpc.GetAddresses())) - - print("LIT2 Addresses") - print(pp.pprint(lit2.rpc.GetAddresses())) - - print("bitcoind Addresses") - print(pp.pprint(bc.rpc.listaddressgroupings())) - #------------------------------------------ - - - - print("=====START CONTRACT N1=====") - res = lit1.rpc.ListContracts() - #print(pp.pprint(res)) - print(res) - print("=====END CONTRACT N1=====") - - print("=====START CONTRACT N2=====") - res = lit2.rpc.ListContracts() - #print(pp.pprint(res)) - print(res) - print("=====END CONTRACT N2=====") - - - except BaseException as be: raise be @@ -484,14 +483,13 @@ def t_11_0(env): #----------------------------- # 2) SettlementTx vsize will be printed - + # Vsize from Blockchain: 181 # There fore we expect here # valueOurs: 17992800 = 18000000 - 7200 !!! # valueTheirs: 1992800 = 2000000 - 7200 !!! - #----------------------------- # 3) Claim TX in SettleContract @@ -526,6 +524,7 @@ def t_11_0(env): #----------------------------- + oracles_number = 1 oracle_value = 11 node_to_settle = 0 @@ -550,14 +549,13 @@ def t_11_0(env): vsizes = [FundingTxVsize, SettlementTxVsize] - params = [lit_funding_amt, contract_funding_amt, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] + params = [lit_funding_amt, contract_funding_amt, oracles_number, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] run_t(env, params) -# ==================================================================================== -# ==================================================================================== +# ----------------------------------------------------------------------------- def t_1300_1(env): @@ -568,7 +566,7 @@ def t_1300_1(env): # Vsize from Blockchain: 252 - # So we expect lit1, and lit2 balances equal to 89989920 !!! + # So we expect lit1, and lit2 balances equal to 89989920 # 90000000 - 89989920 = 10080 # But this is only when peers have one input each. What we expect. @@ -582,13 +580,11 @@ def t_1300_1(env): # valueOurs: 5992800 = 6000000 - 7200 !!! # valueTheirs: 13992800 = 14000000 - 7200 !!! - #----------------------------- # 3) Claim TX in SettleContract lit1 # Here the transaction vsize is always the same: 121 - # Vsize from Blockchain: 121 #----------------------------- @@ -618,6 +614,7 @@ def t_1300_1(env): #----------------------------- + oracles_number = 3 oracle_value = 1300 node_to_settle = 1 @@ -643,20 +640,18 @@ def t_1300_1(env): vsizes = [FundingTxVsize, SettlementTxVsize] - params = [lit_funding_amt, contract_funding_amt, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] + params = [lit_funding_amt, contract_funding_amt, oracles_number, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] run_t(env, params) -# ==================================================================================== -# ==================================================================================== - +# ----------------------------------------------------------------------------- def t_10_0(env): - + oracles_number = 3 oracle_value = 10 node_to_settle = 0 @@ -681,20 +676,19 @@ def t_10_0(env): vsizes = [FundingTxVsize, SettlementTxVsize] - params = [lit_funding_amt, contract_funding_amt, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] + params = [lit_funding_amt, contract_funding_amt, oracles_number, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] run_t(env, params) -# ==================================================================================== -# ==================================================================================== +# ----------------------------------------------------------------------------- def t_10_1(env): - + oracles_number = 3 oracle_value = 10 node_to_settle = 1 @@ -721,20 +715,17 @@ def t_10_1(env): vsizes = [FundingTxVsize, SettlementTxVsize] - params = [lit_funding_amt, contract_funding_amt, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] + params = [lit_funding_amt, contract_funding_amt, oracles_number, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] run_t(env, params) - -# ==================================================================================== -# ==================================================================================== - - +# ----------------------------------------------------------------------------- def t_20_0(env): + oracles_number = 3 oracle_value = 20 node_to_settle = 0 @@ -759,21 +750,17 @@ def t_20_0(env): vsizes = [FundingTxVsize, SettlementTxVsize] - params = [lit_funding_amt, contract_funding_amt, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] + params = [lit_funding_amt, contract_funding_amt, oracles_number, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] run_t(env, params) - - -# ==================================================================================== -# ==================================================================================== - - +# ----------------------------------------------------------------------------- def t_20_1(env): + oracles_number = 3 oracle_value = 20 node_to_settle = 1 @@ -794,12 +781,10 @@ def t_20_1(env): ClaimTxFeeOurs = 121 * 80 ClaimTxFeeTheirs = 0 - feeperbyte = 80 - vsizes = [FundingTxVsize, SettlementTxVsize] - params = [lit_funding_amt, contract_funding_amt, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] + params = [lit_funding_amt, contract_funding_amt, oracles_number, oracle_value, node_to_settle, valueFullyOurs, valueFullyTheirs, vsizes, feeperbyte, SetTxFeeOurs, SetTxFeeTheirs, ClaimTxFeeOurs, ClaimTxFeeTheirs] run_t(env, params) diff --git a/test/itest_dlcrefund.py b/test/itest_dlcrefund.py index c24664e76..89d419853 100644 --- a/test/itest_dlcrefund.py +++ b/test/itest_dlcrefund.py @@ -18,13 +18,14 @@ def run_t(env, params): lit_funding_amt = params[0] contract_funding_amt = params[1] - oracle_value = params[2] - valueFullyOurs=params[3] - valueFullyTheirs=params[4] + oracles_number = params[2] + oracle_value = params[3] + valueFullyOurs=params[4] + valueFullyTheirs=params[5] - feeperbyte = params[5] + feeperbyte = params[6] - node_to_refund = params[6] + node_to_refund = params[7] bc = env.bitcoind @@ -32,9 +33,11 @@ def run_t(env, params): # Create oracles #------------ - env.new_oracle(1, oracle_value) # publishing interval is 1 second. + oracles = [] - oracle1 = env.oracles[0] + for i in range(oracles_number): + env.new_oracle(1, oracle_value) # publishing interval is 1 second. + oracles.append(env.oracles[i]) time.sleep(2) @@ -128,17 +131,19 @@ def run_t(env, params): res = lit1.rpc.ListOracles() assert len(res) != 0, "Initial lis of oracles must be empty" - oracle1_pubkey = json.loads(oracle1.get_pubkey()) - assert len(oracle1_pubkey["A"]) == 66, "Wrong oracle1 pub key" - - oracle_res1 = lit1.rpc.AddOracle(Key=oracle1_pubkey["A"], Name="oracle1") - assert oracle_res1["Oracle"]["Idx"] == 1, "AddOracle does not works" + oracles_pubkey = [] + oidxs = [] + datasources = [] - res = lit1.rpc.ListOracles(ListOraclesArgs={}) - assert len(res["Oracles"]) == 1, "ListOracles 1 does not works" + for oracle in oracles: + opk = json.loads(oracle.get_pubkey()) + oracles_pubkey.append(opk) + oidx = lit1.rpc.AddOracle(Key=opk["A"], Name=opk["A"])["Oracle"]["Idx"] + oidxs.append(oidx) + lit2.rpc.AddOracle(Key=opk["A"], Name=opk["A"])["Oracle"]["Idx"] - lit2.rpc.AddOracle(Key=oracle1_pubkey["A"], Name="oracle1") + datasources.append(json.loads(oracle.get_datasources())) # #------------ @@ -155,10 +160,12 @@ def run_t(env, params): assert res["Contract"]["Idx"] == 1, "GetContract does not works" - res = lit1.rpc.SetContractOracle(CIdx=contract["Contract"]["Idx"], OIdx=oracle_res1["Oracle"]["Idx"]) + res = lit1.rpc.SetContractOraclesNumber(CIdx=contract["Contract"]["Idx"], OraclesNumber=oracles_number) + assert res["Success"], "SetContractOraclesNumber does not works" + + res = lit1.rpc.SetContractOracle(CIdx=contract["Contract"]["Idx"], OIdx=oidxs) assert res["Success"], "SetContractOracle does not works" - datasources = json.loads(oracle1.get_datasources()) # Since the oracle publishes data every 1 second (we set this time above), # we increase the time for a point by 3 seconds. @@ -176,13 +183,18 @@ def run_t(env, params): res = lit1.rpc.ListContracts() assert res["Contracts"][contract["Contract"]["Idx"] - 1]["OracleTimestamp"] == settlement_time, "SetContractSettlementTime does not match settlement_time" - rpoint1 = oracle1.get_rpoint(datasources[0]["id"], settlement_time) - decode_hex = codecs.getdecoder("hex_codec") - b_RPoint = decode_hex(json.loads(rpoint1)['R'])[0] - RPoint = [elem for elem in b_RPoint] - - res = lit1.rpc.SetContractRPoint(CIdx=contract["Contract"]["Idx"], RPoint=RPoint) + brpoints = [] + rpoints = [] + for oracle, datasource in zip(oracles, datasources): + res = oracle.get_rpoint(datasource[0]["id"], settlement_time) + print(res) + b_RPoint = decode_hex(json.loads(res)['R'])[0] + RPoint = [elem for elem in b_RPoint] + brpoints.append(RPoint) + rpoints.append(res) + + res = lit1.rpc.SetContractRPoint(CIdx=contract["Contract"]["Idx"], RPoint=brpoints) assert res["Success"], "SetContractRpoint does not works" lit1.rpc.SetContractCoinType(CIdx=contract["Contract"]["Idx"], CoinType = 257) @@ -202,34 +214,22 @@ def run_t(env, params): assert res["Contract"]["OurFundingAmount"] == ourFundingAmount, "SetContractFunding does not works" assert res["Contract"]["TheirFundingAmount"] == theirFundingAmount, "SetContractFunding does not works" - print("Before SetContractDivision") - res = lit1.rpc.SetContractDivision(CIdx=contract["Contract"]["Idx"], ValueFullyOurs=valueFullyOurs, ValueFullyTheirs=valueFullyTheirs) assert res["Success"], "SetContractDivision does not works" - print("After SetContractDivision") - time.sleep(3) res = lit1.rpc.ListConnections() print(res) - print("Before OfferContract") - res = lit1.rpc.OfferContract(CIdx=contract["Contract"]["Idx"], PeerIdx=lit1.get_peer_id(lit2)) assert res["Success"], "OfferContract does not works" - print("After OfferContract") - time.sleep(3) - print("Before ContractRespond") - res = lit2.rpc.ContractRespond(AcceptOrDecline=True, CIdx=1) assert res["Success"], "ContractRespond on lit2 does not works" - print("After ContractRespond") - time.sleep(3) #------------------------------------------ @@ -248,13 +248,9 @@ def run_t(env, params): # #------------------------------------------ - - print("Before Generate Block") - env.generate_block() time.sleep(2) - print("After Generate Block") print("Accept") bals1 = lit1.get_balance_info() @@ -276,27 +272,6 @@ def run_t(env, params): assert bal1sum == lit1_bal_after_accept, "lit1 Balance after contract accept does not match" assert bal2sum == lit2_bal_after_accept, "lit2 Balance after contract accept does not match" - oracle1_val = "" - oracle1_sig = "" - - i = 0 - while True: - res = oracle1.get_publication(json.loads(rpoint1)['R']) - time.sleep(5) - i += 1 - if i>4: - assert False, "Error: Oracle does not publish data" - - try: - oracle1_val = json.loads(res)["value"] - oracle1_sig = json.loads(res)["signature"] - break - except BaseException as e: - print(e) - next - - - print("Before Refund Contract") time.sleep(2) res = env.lits[node_to_refund].rpc.RefundContract(CIdx=1) @@ -364,10 +339,6 @@ def run_t(env, params): assert bal1sum == 0, "lit1 After Spend balance does not match." assert bal2sum == 199936640, "lit2 After Spend balance does not match." - - - - #------------------------------------------------ if deb_mod: @@ -420,6 +391,7 @@ def run_t(env, params): def forward(env): + oracles_number = 3 oracle_value = 10 node_to_settle = 0 @@ -431,7 +403,7 @@ def forward(env): feeperbyte = 80 - params = [lit_funding_amt, contract_funding_amt, oracle_value, valueFullyOurs, valueFullyTheirs, feeperbyte, 0] + params = [lit_funding_amt, contract_funding_amt, oracles_number, oracle_value, valueFullyOurs, valueFullyTheirs, feeperbyte, 0] run_t(env, params) @@ -439,6 +411,7 @@ def forward(env): def reverse(env): + oracles_number = 3 oracle_value = 10 node_to_settle = 0 @@ -450,6 +423,6 @@ def reverse(env): feeperbyte = 80 - params = [lit_funding_amt, contract_funding_amt, oracle_value, valueFullyOurs, valueFullyTheirs, feeperbyte, 1] + params = [lit_funding_amt, contract_funding_amt, oracles_number, oracle_value, valueFullyOurs, valueFullyTheirs, feeperbyte, 1] run_t(env, params) \ No newline at end of file diff --git a/test/testlib.py b/test/testlib.py index 6b82e6099..7d96d948d 100644 --- a/test/testlib.py +++ b/test/testlib.py @@ -78,7 +78,7 @@ def __init__(self, bcnode): s = '' for _ in range(64): s += hexchars[random.randint(0, len(hexchars) - 1)] - print('Using key:', s) + print('Using key (lit):', s) f.write(s + "\n") # Go and do the initial startup and sync. @@ -262,7 +262,7 @@ def __init__(self, interval, valueToPublish): s = '' for _ in range(192): s += hexchars[random.randint(0, len(hexchars) - 1)] - print('Using key:', s) + print('Using key (oracle):', s) f.write(s + "\n") self.start() diff --git a/test/tests.txt b/test/tests.txt index 4bf317dfc..1d47daa54 100644 --- a/test/tests.txt +++ b/test/tests.txt @@ -29,9 +29,11 @@ pushclose 2 reverse # regular contract dlc 2 t_11_0 -# large contract to test updeted messaging subsystem +# large contract to test updated messaging subsystem # and also to test settlement from counterparty -dlc 2 t_1300_1 +# TODO. Fix Error: [Errno 32] Broken pipe +# when delay betwen requests becomes large +#dlc 2 t_1300_1 # test at left edge dlc 2 t_10_0 @@ -46,4 +48,5 @@ dlc 2 t_20_0 dlc 2 t_20_1 dlcrefund 2 forward -dlcrefund 2 reverse \ No newline at end of file +dlcrefund 2 reverse + From 3fa0e4788b0bdf832ca28beeff67c4ecc40fbebe Mon Sep 17 00:00:00 2001 From: infografx Date: Wed, 6 Nov 2019 18:30:12 +0200 Subject: [PATCH 20/24] Oracle Fraud Detection. --- litrpc/dlccmds.go | 256 ++++++++++++++++++++++++++++++++- lnutil/dlclib.go | 2 - qln/lndb.go | 4 +- qln/msghandler.go | 2 + test/itest_checkoraclefraud.py | 18 +++ test/itest_dlc.py | 33 ++++- test/tests.txt | 10 +- 7 files changed, 315 insertions(+), 10 deletions(-) create mode 100644 test/itest_checkoraclefraud.py diff --git a/litrpc/dlccmds.go b/litrpc/dlccmds.go index 33b21f461..e24474594 100644 --- a/litrpc/dlccmds.go +++ b/litrpc/dlccmds.go @@ -2,11 +2,19 @@ package litrpc import ( "encoding/hex" + "encoding/binary" "fmt" "github.com/mit-dci/lit/dlc" "github.com/mit-dci/lit/lnutil" "github.com/mit-dci/lit/consts" + + "github.com/mit-dci/lit/wire" + + "github.com/adiabat/btcd/btcec" + "math/big" + "bytes" + "bufio" ) type ListOraclesArgs struct { @@ -501,10 +509,8 @@ func (r *LitRPC) SettleContract(args SettleContractArgs, return nil } - //====================================================================== - type RefundContractArgs struct { CIdx uint64 } @@ -526,3 +532,249 @@ func (r *LitRPC) RefundContract(args RefundContractArgs,reply *RefundContractRep reply.Success = true return nil } + +//====================================================================== + +type DifferentResultsFraudArgs struct { + Sfirst string + Hfirst string + Ssecond string + Hsecond string + Rpoint string + Apoint string +} + +type DifferentResultsFraudReply struct { + Fraud bool +} + +func (r *LitRPC) DifferentResultsFraud(args DifferentResultsFraudArgs, reply *DifferentResultsFraudReply) error { + + reply.Fraud = false + + curve := btcec.S256() + + argsRpoint := new(big.Int) + argsApoint := new(big.Int) + argsRpoint.SetString(args.Rpoint, 16) + argsApoint.SetString(args.Apoint, 16) + + + s1 := new(big.Int) + h1 := new(big.Int) + + s1.SetString(args.Sfirst, 16) + h1.SetString(args.Hfirst, 16) + + s2 := new(big.Int) + h2 := new(big.Int) + + s2.SetString(args.Ssecond, 16) + h2.SetString(args.Hsecond, 16) + + s2s1 := new(big.Int) + h1h2 := new(big.Int) + + s2s1.Sub(s2, s1) + h1h2.Sub(h1, h2) + + h1h2.ModInverse(h1h2,curve.N) + + v := new(big.Int) + v.Mul(s2s1, h1h2) + v.Mod(v, curve.N) + + //-------------------------------- + + k := new(big.Int) + h1vres := new(big.Int) + h1vres.Mul(h1, v) + + k.Add(s1,h1vres) + k.Mod(k, curve.N) + + //--------------------------------- + + bigS := new(big.Int) + bigS.Mul(h1, v) + bigS.Sub(k, bigS) + bigS.Mod(bigS, curve.N) + + var Rpoint [33]byte + var Apoint [33]byte + + _, pk := btcec.PrivKeyFromBytes(btcec.S256(), k.Bytes()) + copy(Rpoint[:], pk.SerializeCompressed()) + + _, pk = btcec.PrivKeyFromBytes(btcec.S256(), v.Bytes()) + copy(Apoint[:], pk.SerializeCompressed()) + + Rcompare := bytes.Compare(Rpoint[:], argsRpoint.Bytes()) + Acompare := bytes.Compare(Apoint[:], argsApoint.Bytes()) + + if (Rcompare == 0) && (Acompare == 0){ + + reply.Fraud = true + + } + + return nil + +} + +//====================================================================== +// For testing only +// This should be replaced by a fraudulent transaction that was published. + + +type GetLatestTxArgs struct { + CIdx uint64 +} + +type GetLatestTxArgsReply struct { + Tx string +} + +func (r *LitRPC) GetLatestTx(args GetLatestTxArgs, reply *GetLatestTxArgsReply) error { + + var buf bytes.Buffer + w := bufio.NewWriter(&buf) + r.Node.OpEventTx.Serialize(w) + w.Flush() + + encodedStr := hex.EncodeToString(buf.Bytes()) + reply.Tx = encodedStr + + return nil + +} + +//====================================================================== + +type GetMessageFromTxArgs struct { + CIdx uint64 + Tx string +} + +type GetMessageFromTxReply struct { + OracleValue int64 + ValueOurs int64 + ValueTheirs int64 + OracleA string + OracleR string + TheirPayoutBase string + OurPayoutBase string + +} + +func (r *LitRPC) GetMessageFromTx(args GetMessageFromTxArgs, reply *GetMessageFromTxReply) error { + + + parsedTx, _ := hex.DecodeString(args.Tx) + reader := bytes.NewReader(parsedTx) + + var msgTx wire.MsgTx + err := msgTx.Deserialize(reader) + if err != nil { + return nil + } + + inputPkScript := msgTx.TxOut[0].PkScript + + c, _ := r.Node.DlcManager.LoadContract(args.CIdx) + + for _, d := range c.Division { + tx, _ := lnutil.SettlementTx(c, d, true) + pkScriptsCompare := bytes.Compare(inputPkScript, tx.TxOut[0].PkScript) + + if pkScriptsCompare == 0 { + reply.OracleValue = d.OracleValue + reply.ValueOurs = d.ValueOurs + totalContractValue := c.TheirFundingAmount + c.OurFundingAmount + reply.ValueTheirs = totalContractValue - d.ValueOurs + reply.OracleA = hex.EncodeToString(c.OracleA[0][:]) + reply.OracleR = hex.EncodeToString(c.OracleR[0][:]) + reply.TheirPayoutBase = hex.EncodeToString(c.TheirPayoutBase[:]) + reply.OurPayoutBase = hex.EncodeToString(c.OurPayoutBase[:]) + + } + + } + + return nil + +} + +//====================================================================== + +type CompactProofOfMsgArgs struct { + OracleValue int64 + ValueOurs int64 + ValueTheirs int64 + OracleA string + OracleR string + TheirPayoutBase string + OurPayoutBase string + Tx string +} + +type CompactProofOfMsgReply struct { + Success bool +} + + +func (r *LitRPC) CompactProofOfMsg(args CompactProofOfMsgArgs, reply *CompactProofOfMsgReply) error { + + reply.Success = false + + parsedTx, _ := hex.DecodeString(args.Tx) + reader := bytes.NewReader(parsedTx) + var msgTx wire.MsgTx + err := msgTx.Deserialize(reader) + if err != nil { + return nil + } + + var buf bytes.Buffer + w := bufio.NewWriter(&buf) + msgTx.Serialize(w) + w.Flush() + + var oraclea []byte + var oracler []byte + var theirPayoutbase []byte + var ourPayoutbase []byte + oraclea, _ = hex.DecodeString(args.OracleA) + oracler, _ = hex.DecodeString(args.OracleR) + + theirPayoutbase, _ = hex.DecodeString(args.TheirPayoutBase) + ourPayoutbase, _ = hex.DecodeString(args.OurPayoutBase) + + var oraclea33 [33]byte + var oracler33 [33]byte + var theirPayoutbase33 [33]byte + var ourPayoutbase33 [33]byte + copy(oraclea33[:], oraclea) + copy(oracler33[:], oracler) + copy(theirPayoutbase33[:], theirPayoutbase) + copy(ourPayoutbase33[:], ourPayoutbase) + + var buft bytes.Buffer + binary.Write(&buft, binary.BigEndian, uint64(0)) + binary.Write(&buft, binary.BigEndian, uint64(0)) + binary.Write(&buft, binary.BigEndian, uint64(0)) + binary.Write(&buft, binary.BigEndian, args.OracleValue) + + oraclesSigPub, _ := lnutil.DlcCalcOracleSignaturePubKey(buft.Bytes(), oraclea33, oracler33) + var oraclesSigPubs [][33]byte + oraclesSigPubs = append(oraclesSigPubs, oraclesSigPub) + txoutput := lnutil.DlcOutput(theirPayoutbase33, ourPayoutbase33, oraclesSigPubs, args.ValueTheirs) + PkScriptCompare := bytes.Compare(txoutput.PkScript, msgTx.TxOut[0].PkScript) + + if PkScriptCompare == 0 { + reply.Success = true + } + + return nil + +} \ No newline at end of file diff --git a/lnutil/dlclib.go b/lnutil/dlclib.go index 843388fbd..cb7ed5ab7 100644 --- a/lnutil/dlclib.go +++ b/lnutil/dlclib.go @@ -405,10 +405,8 @@ func PrintTx(tx *wire.MsgTx) { // DlcOutput returns a Txo for a particular value that pays to // (PubKeyPeer+PubKeyOracleSig or (OurPubKey and TimeDelay)) func DlcOutput(pkPeer, pkOurs [33]byte, oraclesSigPub [][33]byte, value int64) *wire.TxOut { - scriptBytes := DlcCommitScript(pkPeer, pkOurs, oraclesSigPub, 5) scriptBytes = P2WSHify(scriptBytes) - return wire.NewTxOut(value, scriptBytes) } diff --git a/qln/lndb.go b/qln/lndb.go index 43d1a06b8..1a210fc46 100644 --- a/qln/lndb.go +++ b/qln/lndb.go @@ -144,9 +144,11 @@ type LitNode struct { ExchangeRates map[uint32][]lnutil.RateDesc - // REFACTORING FIELDS + // TODO REFACTORING FIELDS PeerMap map[*lnp2p.Peer]*RemotePeer // we never remove things from here, so this is a memory leak PeerMapMtx *sync.Mutex + + OpEventTx *wire.MsgTx // TODO for testing only } type LinkDesc struct { diff --git a/qln/msghandler.go b/qln/msghandler.go index 88b1aa35f..2a6b5f027 100644 --- a/qln/msghandler.go +++ b/qln/msghandler.go @@ -543,6 +543,8 @@ func (nd *LitNode) HandleContractOPEvent(c *lnutil.DlcContract, " for type %d", c.CoinType) } + nd.OpEventTx = opEvent.Tx + pkhIsMine := false pkhIdx := uint32(0) value := int64(0) diff --git a/test/itest_checkoraclefraud.py b/test/itest_checkoraclefraud.py new file mode 100644 index 000000000..792319409 --- /dev/null +++ b/test/itest_checkoraclefraud.py @@ -0,0 +1,18 @@ + +def run_test(env): + + lit = env.lits[0] + + s1string = "424b62134ec5ff7f8ff25de43917a03582e253283575b8f815d26bbdc27d17f8" + h1string = "9bd6e409476804596c2793eb722fd23479f2a1a8a439e8cb47faed68dc660535" + s2string = "4a61ef074997f0f0039c71e9dd91d15263c6f98bc54034336491d5e8a5445f4c" + h2string = "dc2b6ce71bb4099ca53c70eadcd1d9d4be46b65c1e0b540528e619fd236ae09a" + + rpoint = "02f8460e855b091cec11ccf4a85064d4a8a7d3a2970b957a2165564b537d510bb4" + apoint = "029bc17aed9a0a5821b5b0425d8260d66f0529eb357a0b036765d68904152f618a" + + res = lit.rpc.DifferentResultsFraud(Sfirst=s1string, Hfirst=h1string, Ssecond=s2string, Hsecond=h2string, Rpoint=rpoint, Apoint=apoint) + + print('Whether the oracle publish two different results or not.') + print(res) + diff --git a/test/itest_dlc.py b/test/itest_dlc.py index 69b68da26..0626bc7d8 100644 --- a/test/itest_dlc.py +++ b/test/itest_dlc.py @@ -198,7 +198,6 @@ def run_t(env, params): rpoints = [] for oracle, datasource in zip(oracles, datasources): res = oracle.get_rpoint(datasource[0]["id"], settlement_time) - print(res) b_RPoint = decode_hex(json.loads(res)['R'])[0] RPoint = [elem for elem in b_RPoint] brpoints.append(RPoint) @@ -406,9 +405,6 @@ def run_t(env, params): print(res) print("=====END CONTRACT N2=====") - - print("ORACLE VALUE:", OraclesVal[0], "; oracle signature:", OraclesVal[0]) - valueOurs = 0 @@ -463,6 +459,35 @@ def run_t(env, params): + # Check for possible ofracle fraud. + # If Olivia herself is a counterparty to a contract (e.g. Alice is Olivia), she can + # cause it to execute in an arbitrary fashion withouth revealing her private key. + + # This is detectable, and the defrauded party + # Bob can provide a compact proof of the fraud so that all other users can + # stop using Olivia’s commitments and signatures. + + # For testing only + publishedTX = lit2.rpc.GetLatestTx(CIdx=1) + print("GetLatestTx res: ", publishedTX["Tx"]) + + # At the moment we get lagest published tx. + msg = lit2.rpc.GetMessageFromTx(CIdx=1, Tx=str(publishedTX["Tx"])) + print("GetMessageFromTx res: ", msg) + + assert msg["OracleValue"] == oracle_value, "lit.rpc.GetMessageFromTx does not works." + + proofOfMsg = lit2.rpc.CompactProofOfMsg(\ + OracleValue=msg["OracleValue"], \ + ValueOurs=msg["ValueOurs"],\ + ValueTheirs=msg["ValueTheirs"],\ + OracleA=msg["OracleA"],\ + OracleR=msg["OracleR"],\ + TheirPayoutBase=msg["TheirPayoutBase"],\ + OurPayoutBase=msg["OurPayoutBase"], Tx=publishedTX["Tx"]) + + print("proofOfMsg: ", proofOfMsg) + except BaseException as be: raise be diff --git a/test/tests.txt b/test/tests.txt index 1d47daa54..728d73b0a 100644 --- a/test/tests.txt +++ b/test/tests.txt @@ -33,7 +33,7 @@ dlc 2 t_11_0 # and also to test settlement from counterparty # TODO. Fix Error: [Errno 32] Broken pipe # when delay betwen requests becomes large -#dlc 2 t_1300_1 +# dlc 2 t_1300_1 # test at left edge dlc 2 t_10_0 @@ -50,3 +50,11 @@ dlc 2 t_20_1 dlcrefund 2 forward dlcrefund 2 reverse +# If Olivia attempts to publicly report two different prices (in order to +# assume the role of a counterparty in a contract and “win” the bet regardless +# of the true outcome), she will reveal her permanent private key, as well as +# the k value for the particular contract she attempted to double-report. +checkoraclefraud 1 + + + From 566c4c6ba9b53d254ea902cbd04c13fc4d3e1b3c Mon Sep 17 00:00:00 2001 From: infografx Date: Mon, 11 Nov 2019 12:48:35 +0200 Subject: [PATCH 21/24] Fix. --- litrpc/dlccmds.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/litrpc/dlccmds.go b/litrpc/dlccmds.go index e24474594..a8c087b4b 100644 --- a/litrpc/dlccmds.go +++ b/litrpc/dlccmds.go @@ -551,7 +551,6 @@ type DifferentResultsFraudReply struct { func (r *LitRPC) DifferentResultsFraud(args DifferentResultsFraudArgs, reply *DifferentResultsFraudReply) error { reply.Fraud = false - curve := btcec.S256() argsRpoint := new(big.Int) @@ -559,7 +558,6 @@ func (r *LitRPC) DifferentResultsFraud(args DifferentResultsFraudArgs, reply *Di argsRpoint.SetString(args.Rpoint, 16) argsApoint.SetString(args.Apoint, 16) - s1 := new(big.Int) h1 := new(big.Int) @@ -595,11 +593,6 @@ func (r *LitRPC) DifferentResultsFraud(args DifferentResultsFraudArgs, reply *Di //--------------------------------- - bigS := new(big.Int) - bigS.Mul(h1, v) - bigS.Sub(k, bigS) - bigS.Mod(bigS, curve.N) - var Rpoint [33]byte var Apoint [33]byte @@ -613,9 +606,7 @@ func (r *LitRPC) DifferentResultsFraud(args DifferentResultsFraudArgs, reply *Di Acompare := bytes.Compare(Apoint[:], argsApoint.Bytes()) if (Rcompare == 0) && (Acompare == 0){ - reply.Fraud = true - } return nil From a8e43c249c1ba495271f25d18594be926f325cdb Mon Sep 17 00:00:00 2001 From: infografx Date: Tue, 12 Nov 2019 14:02:07 +0200 Subject: [PATCH 22/24] Fix. --- litrpc/dlccmds.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/litrpc/dlccmds.go b/litrpc/dlccmds.go index a8c087b4b..1863148fa 100644 --- a/litrpc/dlccmds.go +++ b/litrpc/dlccmds.go @@ -726,11 +726,6 @@ func (r *LitRPC) CompactProofOfMsg(args CompactProofOfMsgArgs, reply *CompactPro return nil } - var buf bytes.Buffer - w := bufio.NewWriter(&buf) - msgTx.Serialize(w) - w.Flush() - var oraclea []byte var oracler []byte var theirPayoutbase []byte From 8c9c5d55a07295abb2259311f82494c55548f82a Mon Sep 17 00:00:00 2001 From: infografx Date: Tue, 12 Nov 2019 16:00:35 +0200 Subject: [PATCH 23/24] Cleanup. --- lnutil/dlclib.go | 2 +- lnutil/msglib.go | 1 - qln/dlc.go | 3 --- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/lnutil/dlclib.go b/lnutil/dlclib.go index cb7ed5ab7..1e9f7f578 100644 --- a/lnutil/dlclib.go +++ b/lnutil/dlclib.go @@ -72,7 +72,7 @@ type DlcContract struct { OurChangePKH, TheirChangePKH [20]byte // Pubkey used in the funding multisig output OurFundMultisigPub, TheirFundMultisigPub [33]byte - //OurRevokePub, TheirRevokePub [33]byte + OurRefundPKH, TheirRefundPKH [20]byte OurrefundTxSig64, TheirrefundTxSig64 [64]byte // Pubkey to be used in the commit script (combined with oracle pubkey diff --git a/lnutil/msglib.go b/lnutil/msglib.go index 987cade6f..7309a6733 100644 --- a/lnutil/msglib.go +++ b/lnutil/msglib.go @@ -1744,7 +1744,6 @@ type DlcOfferAcceptMsg struct { OurFundMultisigPub [33]byte // The Pubkey to be used to in the contract settlement OurPayoutBase [33]byte - //OurRevokePub [33]byte OurRefundPKH [20]byte OurrefundTxSig64 [64]byte // The PKH to be paid to in the contract settlement diff --git a/qln/dlc.go b/qln/dlc.go index caf8c944a..d0233b30c 100644 --- a/qln/dlc.go +++ b/qln/dlc.go @@ -287,9 +287,6 @@ func (nd *LitNode) DlcOfferHandler(msg lnutil.DlcOfferMsg, peer *RemotePeer) { c.TheirChangePKH = msg.Contract.OurChangePKH c.TheirIdx = msg.Contract.Idx c.TheirPayoutPKH = msg.Contract.OurPayoutPKH - - //c.TheirRevokePub = msg.Contract.OurRevokePub - c.TheirRefundPKH = msg.Contract.OurRefundPKH c.Division = make([]lnutil.DlcContractDivision, len(msg.Contract.Division)) From fa864a540942d921969830561065abd7543d3525 Mon Sep 17 00:00:00 2001 From: infografx Date: Mon, 2 Dec 2019 03:25:47 +0200 Subject: [PATCH 24/24] Added 'netogiate contract' functionality. Merge branch 'dlc_negotiated_tx' into master-mit-lit_negotiate_contract --- cmd/lit-af/dlccmds.go | 10 + litrpc/dlccmds.go | 64 +++++- lnutil/dlclib.go | 143 +++++++++++-- lnutil/msglib.go | 251 +++++++++++++++++++++- qln/dlc.go | 336 ++++++++++++++++++++++++++++- qln/msghandler.go | 22 +- test/itest_negotiatecontract.py | 368 ++++++++++++++++++++++++++++++++ test/tests.txt | 59 ++--- 8 files changed, 1192 insertions(+), 61 deletions(-) create mode 100644 test/itest_negotiatecontract.py diff --git a/cmd/lit-af/dlccmds.go b/cmd/lit-af/dlccmds.go index c055575f1..ccd759861 100644 --- a/cmd/lit-af/dlccmds.go +++ b/cmd/lit-af/dlccmds.go @@ -1178,6 +1178,16 @@ func PrintContract(c *lnutil.DlcContract) { status = "Error" case lnutil.ContractStatusDeclined: status = "Declined" + case lnutil.ContractStatusNegotiatingByMe: + status = "ContractStatusNegotiatingByMe" + case lnutil.ContractStatusNegotiatingToMe: + status = "ContractStatusNegotiatingToMe" + case lnutil.ContractStatusNegotiatedByMe: + status = "ContractStatusNegotiatedByMe" + case lnutil.ContractStatusNegotiatedToMe: + status = "ContractStatusNegotiatedToMe" + case lnutil.ContractStatusNegotiateDeclined: + status = "ContractStatusNegotiateDeclined" } fmt.Fprintf(color.Output, "%-30s : %s\n\n", lnutil.White("Status"), status) diff --git a/litrpc/dlccmds.go b/litrpc/dlccmds.go index 1863148fa..4d76c9b6c 100644 --- a/litrpc/dlccmds.go +++ b/litrpc/dlccmds.go @@ -763,4 +763,66 @@ func (r *LitRPC) CompactProofOfMsg(args CompactProofOfMsgArgs, reply *CompactPro return nil -} \ No newline at end of file +} + + + + +//====================================================================== + + +type NegotiateContractArgs struct { + CIdx uint64 + DesiredOracleValue int64 +} + +type NegotiateContractReply struct { + Success bool +} + + +func (r *LitRPC) DlcNegotiateContract(args NegotiateContractArgs, reply *NegotiateContractReply) error { + + var err error + + err = r.Node.DlcNegotiateContract(args.CIdx, args.DesiredOracleValue) + if err != nil { + return err + } + + reply.Success = true + return nil + +} + + + +type NegotiateContractRespondArgs struct { + // True for accept, false for decline. + AcceptOrDecline bool + CIdx uint64 +} + +type NegotiateContractRespondReply struct { + Success bool +} + +// DeclineContract declines an offered contract +func (r *LitRPC) NegotiateContractRespond(args NegotiateContractRespondArgs, reply *NegotiateContractRespondReply) error { + var err error + + + if args.AcceptOrDecline { + err = r.Node.DlcAcceptNegotiate(args.CIdx) + }else{ + err = r.Node.DlcDeclineNegotiate(args.CIdx) + } + + if err != nil { + return err + } + + reply.Success = true + return nil +} + diff --git a/lnutil/dlclib.go b/lnutil/dlclib.go index 1e9f7f578..b41476edb 100644 --- a/lnutil/dlclib.go +++ b/lnutil/dlclib.go @@ -25,17 +25,23 @@ import ( type DlcContractStatus int const ( - ContractStatusDraft DlcContractStatus = 0 - ContractStatusOfferedByMe DlcContractStatus = 1 - ContractStatusOfferedToMe DlcContractStatus = 2 - ContractStatusDeclined DlcContractStatus = 3 - ContractStatusAccepted DlcContractStatus = 4 - ContractStatusAcknowledged DlcContractStatus = 5 - ContractStatusActive DlcContractStatus = 6 - ContractStatusSettling DlcContractStatus = 7 - ContractStatusClosed DlcContractStatus = 8 - ContractStatusError DlcContractStatus = 9 - ContractStatusAccepting DlcContractStatus = 10 + ContractStatusDraft DlcContractStatus = 0 + ContractStatusOfferedByMe DlcContractStatus = 1 + ContractStatusOfferedToMe DlcContractStatus = 2 + ContractStatusDeclined DlcContractStatus = 3 + ContractStatusAccepted DlcContractStatus = 4 + ContractStatusAcknowledged DlcContractStatus = 5 + ContractStatusActive DlcContractStatus = 6 // TODO active status have to as large as possible. 11? + ContractStatusSettling DlcContractStatus = 7 + ContractStatusClosed DlcContractStatus = 8 + ContractStatusError DlcContractStatus = 9 + ContractStatusAccepting DlcContractStatus = 10 + ContractStatusNegotiatingByMe DlcContractStatus = 11 + ContractStatusNegotiatingToMe DlcContractStatus = 12 + ContractStatusNegotiatedByMe DlcContractStatus = 13 + ContractStatusNegotiatedToMe DlcContractStatus = 14 + ContractStatusNegotiateDeclinedByMe DlcContractStatus = 15 + ContractStatusNegotiateDeclinedByHim DlcContractStatus = 16 ) // scalarSize is the size of an encoded big endian scalar. @@ -75,6 +81,8 @@ type DlcContract struct { OurRefundPKH, TheirRefundPKH [20]byte OurrefundTxSig64, TheirrefundTxSig64 [64]byte + DesiredOracleValue int64 + OurnegotiateTxSig64, TheirnegotiateTxSig64 [64]byte // Pubkey to be used in the commit script (combined with oracle pubkey // or CSV timeout) OurPayoutBase, TheirPayoutBase [33]byte @@ -190,7 +198,16 @@ func DlcContractFromBytes(b []byte) (*DlcContract, error) { copy(c.OurrefundTxSig64[:], buf.Next(64)) copy(c.TheirrefundTxSig64[:], buf.Next(64)) + + desiredOracleValue, err := wire.ReadVarInt(buf, 0) + if err != nil { + return nil, err + } + c.DesiredOracleValue = int64(desiredOracleValue) + copy(c.OurnegotiateTxSig64[:], buf.Next(64)) + copy(c.TheirnegotiateTxSig64[:], buf.Next(64)) + copy(c.OurPayoutBase[:], buf.Next(33)) copy(c.TheirPayoutBase[:], buf.Next(33)) @@ -318,6 +335,11 @@ func (self *DlcContract) Bytes() []byte { buf.Write(self.OurrefundTxSig64[:]) buf.Write(self.TheirrefundTxSig64[:]) + + wire.WriteVarInt(&buf, 0, uint64(self.DesiredOracleValue)) + + buf.Write(self.OurnegotiateTxSig64[:]) + buf.Write(self.TheirnegotiateTxSig64[:]) buf.Write(self.OurPayoutBase[:]) buf.Write(self.TheirPayoutBase[:]) @@ -425,6 +447,16 @@ func DlcCommitScript(pubKeyPeer, ourPubKey [33]byte, oraclesSigPub [][33]byte, d combinedPubKey = CombinePubs(combinedPubKey, oraclesSigPub[i]) } + + // neworaclepubkeystring := "02b24f7efcfb91f340c222638c58fd644a493a52312dd01966e07d715d4a0462de" + + // decoded, _ := hex.DecodeString(neworaclepubkeystring) + + // var neworaclepubkey [33]byte + // copy(neworaclepubkey[:], decoded[:]) + + // combinedPubKey = CombinePubs(combinedPubKey, neworaclepubkey) + return CommitScript(combinedPubKey, ourPubKey, delay) } @@ -643,6 +675,7 @@ func SettlementTx(c *DlcContract, d DlcContractDivision, if valueOurs > 0 { tx.AddTxOut(wire.NewTxOut(valueOurs, DirectWPKHScriptFromPKH(c.OurPayoutPKH))) } + } else { if valueOurs > 0 { tx.AddTxOut(DlcOutput(c.OurPayoutBase, c.TheirPayoutBase, oraclesSigPub, valueOurs)) @@ -711,4 +744,90 @@ func SignRefundTx(c *DlcContract, tx *wire.MsgTx, priv *koblitz.PrivateKey) (er return nil -} \ No newline at end of file +} + + +//================================================================== + + +func NegotiateTx(c *DlcContract) (*wire.MsgTx, error) { + + tx := wire.NewMsgTx() + tx.Version = 2 + + + DesiredOracleValue := c.DesiredOracleValue + + totalContractValue := c.TheirFundingAmount + c.OurFundingAmount + + var valueours int64 + + for _, d := range c.Division{ + + if d.OracleValue == DesiredOracleValue { + valueours = d.ValueOurs + } + + } + + // TODO. If valueours is undefinded -> ERROR + valueTheirs := totalContractValue - valueours + + var vsize uint32 + if (valueours == 0) || (valueTheirs == 0){ + vsize = 138 + }else{ + vsize = 169 + } + + fee := int64(vsize * c.FeePerByte) + + txin := wire.NewTxIn(&c.FundingOutpoint, nil, nil) + txin.Sequence = 0 + tx.AddTxIn(txin) + + + // TODO: Handle like in SettlementTx + if valueours > fee { + ourRefScript := DirectWPKHScriptFromPKH(c.OurRefundPKH) + ourOutput := wire.NewTxOut(valueours - fee, ourRefScript) + tx.AddTxOut(ourOutput) + } + + // TODO: Handle like in SettlementTx + if valueTheirs > fee { + theirRefScript := DirectWPKHScriptFromPKH(c.TheirRefundPKH) + theirOutput := wire.NewTxOut(valueTheirs - fee, theirRefScript) + tx.AddTxOut(theirOutput) + } + + txsort.InPlaceSort(tx) + + return tx, nil + +} + + +// SignRefundTx +func SignNegotiateTx(c *DlcContract, tx *wire.MsgTx, priv *koblitz.PrivateKey) (error) { + + pre, _, err := FundTxScript(c.OurFundMultisigPub, c.TheirFundMultisigPub) + if err != nil { + return err + } + + hCache := txscript.NewTxSigHashes(tx) + + sig, err := txscript.RawTxInWitnessSignature(tx, hCache, 0, c.OurFundingAmount+c.TheirFundingAmount, pre, txscript.SigHashAll, priv) + if err != nil { + return err + } + + sig = sig[:len(sig)-1] + sig64 , _ := sig64.SigCompress(sig) + c.OurnegotiateTxSig64 = sig64 + + return nil + +} + diff --git a/lnutil/msglib.go b/lnutil/msglib.go index 7309a6733..eef9bac64 100644 --- a/lnutil/msglib.go +++ b/lnutil/msglib.go @@ -56,12 +56,15 @@ const ( MSGID_PAY_SETUP = 0x77 // Setup a payment route //Discreet log contracts messages - MSGID_DLC_OFFER = 0x90 // Offer a contract - MSGID_DLC_ACCEPTOFFER = 0x91 // Accept the contract - MSGID_DLC_DECLINEOFFER = 0x92 // Decline the contract - MSGID_DLC_CONTRACTACK = 0x93 // Acknowledge an acceptance - MSGID_DLC_CONTRACTFUNDINGSIGS = 0x94 // Funding signatures - MSGID_DLC_SIGPROOF = 0x95 // Sigproof + MSGID_DLC_OFFER = 0x90 // Offer a contract + MSGID_DLC_ACCEPTOFFER = 0x91 // Accept the contract + MSGID_DLC_DECLINEOFFER = 0x92 // Decline the contract + MSGID_DLC_CONTRACTACK = 0x93 // Acknowledge an acceptance + MSGID_DLC_CONTRACTFUNDINGSIGS = 0x94 // Funding signatures + MSGID_DLC_SIGPROOF = 0x95 // Sigproof + MSGID_DLC_NEGOTIATE = 0x96 // Negotiate contract + MSGID_DLC_ACCEPTNEGOTIATE = 0x97 // Accept negotiate contract + MSGID_DLC_DECLINENEGOTIATE = 0x98 // Accept negotiate contract //Dual funding messages MSGID_DUALFUNDINGREQ = 0xA0 // Requests funding details (UTXOs, Change address, Pubkey), including our own details and amount needed. @@ -182,6 +185,13 @@ func LitMsgFromBytes(b []byte, peerid uint32) (LitMsg, error) { return NewDlcContractFundingSigsMsgFromBytes(b, peerid) case MSGID_DLC_SIGPROOF: return NewDlcContractSigProofMsgFromBytes(b, peerid) + case MSGID_DLC_NEGOTIATE: + return NewDlcContractNegotiateMsgFromBytes(b, peerid) + case MSGID_DLC_ACCEPTNEGOTIATE: + return NewDlcContractAcceptNegotiateMsgFromBytes(b, peerid) + case MSGID_DLC_DECLINENEGOTIATE: + return NewDlcContractDeclineNegotiateMsgFromBytes(b, peerid) + case MSGID_REMOTE_RPCREQUEST: return NewRemoteControlRpcRequestMsgFromBytes(b, peerid) @@ -1744,7 +1754,10 @@ type DlcOfferAcceptMsg struct { OurFundMultisigPub [33]byte // The Pubkey to be used to in the contract settlement OurPayoutBase [33]byte + // OurRefundPKH OurRefundPKH [20]byte + // We can freely exchange refund signatures because + // there is a locktime variable in the transaction. OurrefundTxSig64 [64]byte // The PKH to be paid to in the contract settlement OurPayoutPKH [20]byte @@ -2550,4 +2563,228 @@ func (msg EndChunksMsg) Peer() uint32 { func (msg EndChunksMsg) MsgType() uint8 { return MSGID_CHUNKS_END -} \ No newline at end of file +} + + +//--------------------------------------------------------------- + + +type DlcContractNegotiateMsg struct { + // Index of the peer we are negotiating the contract with + PeerIdx uint32 + // The index of the contract on the peer + Idx uint64 + + DesiredOracleValue uint64 + OurnegotiateTxSig64 [64]byte +} + +func NewDlcContractNegotiateMsg(contract *DlcContract) DlcContractNegotiateMsg { + + // TODO: wire.ReadVarInt(buf, 0) returns only uint64, why? + DesiredOracleValue := uint64(contract.DesiredOracleValue) + + msg := new(DlcContractNegotiateMsg) + msg.PeerIdx = contract.PeerIdx + msg.Idx = contract.TheirIdx + msg.DesiredOracleValue = DesiredOracleValue + msg.OurnegotiateTxSig64 = contract.OurnegotiateTxSig64 + + return *msg + +} + + +func NewDlcContractNegotiateMsgFromBytes(b []byte, peerIdx uint32) (DlcContractNegotiateMsg, error) { + + msg := new(DlcContractNegotiateMsg) + msg.PeerIdx = peerIdx + + buf := bytes.NewBuffer(b[1:]) // get rid of messageType + + msg.Idx, _ = wire.ReadVarInt(buf, 0) + + msg.DesiredOracleValue, _ = wire.ReadVarInt(buf, 0) + + copy(msg.OurnegotiateTxSig64[:], buf.Next(64)) + + return *msg, nil + +} + + + +func (msg DlcContractNegotiateMsg) Bytes() []byte { + var buf bytes.Buffer + + buf.WriteByte(msg.MsgType()) + + wire.WriteVarInt(&buf, 0, msg.Idx) + wire.WriteVarInt(&buf, 0, msg.DesiredOracleValue) + buf.Write(msg.OurnegotiateTxSig64[:]) + + return buf.Bytes() + +} + + + +func (msg DlcContractNegotiateMsg) Peer() uint32 { + return msg.PeerIdx +} + + +func (msg DlcContractNegotiateMsg) MsgType() uint8 { + return MSGID_DLC_NEGOTIATE +} + + + +//--------------------------------------------------------------- + + +type DlcContractAcceptNegotiateMsg struct { + // Index of the peer we are negotiating the contract with + PeerIdx uint32 + // The index of the contract on the peer + Idx uint64 + + SignedNegotiateTx *wire.MsgTx +} + +func NewDlcAcceptNegotiateMsg(contract *DlcContract, + signedTx *wire.MsgTx) DlcContractAcceptNegotiateMsg { + + msg := new(DlcContractAcceptNegotiateMsg) + msg.PeerIdx = contract.PeerIdx + msg.Idx = contract.TheirIdx + msg.SignedNegotiateTx = signedTx + + return *msg + +} + + +func NewDlcContractAcceptNegotiateMsgFromBytes(b []byte, + peerIdx uint32) (DlcContractAcceptNegotiateMsg, error) { + + + msg := new(DlcContractAcceptNegotiateMsg) + msg.PeerIdx = peerIdx + + + // TODO + if len(b) < 34 { + return *msg, fmt.Errorf("DlcContractAcceptNegotiateMsg %d bytes, expect"+ + "at least 34", len(b)) + } + + buf := bytes.NewBuffer(b[1:]) // get rid of messageType + msg.Idx, _ = wire.ReadVarInt(buf, 0) + + msg.SignedNegotiateTx = wire.NewMsgTx() + msg.SignedNegotiateTx.Deserialize(buf) + + return *msg, nil + +} + + +func (msg DlcContractAcceptNegotiateMsg) Bytes() []byte { + var buf bytes.Buffer + + buf.WriteByte(msg.MsgType()) + wire.WriteVarInt(&buf, 0, msg.Idx) + + writer := bufio.NewWriter(&buf) + msg.SignedNegotiateTx.Serialize(writer) + writer.Flush() + return buf.Bytes() +} + + +func (msg DlcContractAcceptNegotiateMsg) Peer() uint32 { + return msg.PeerIdx +} + + +func (msg DlcContractAcceptNegotiateMsg) MsgType() uint8 { + return MSGID_DLC_ACCEPTNEGOTIATE +} + + + +//--------------------------------------------------------------- + + +type DlcContractDeclineNegotiateMsg struct { + // Index of the peer we are negotiating the contract with + PeerIdx uint32 + // The index of the contract on the peer + Idx uint64 + +} + +func NewDlcContractDeclineNegotiateMsg(contract *DlcContract) DlcContractDeclineNegotiateMsg { + + msg := new(DlcContractDeclineNegotiateMsg) + msg.PeerIdx = contract.PeerIdx + msg.Idx = contract.TheirIdx + + return *msg + +} + + +func NewDlcContractDeclineNegotiateMsgFromBytes(b []byte, + peerIdx uint32) (DlcContractDeclineNegotiateMsg, error) { + + + msg := new(DlcContractDeclineNegotiateMsg) + msg.PeerIdx = peerIdx + + + // TODO + if len(b) < 2 { + return *msg, fmt.Errorf("DlcContractDeclineNegotiateMsg %d bytes, expect"+ + "at least 2", len(b)) + } + + buf := bytes.NewBuffer(b[1:]) // get rid of messageType + msg.Idx, _ = wire.ReadVarInt(buf, 0) + + // msg.SignedNegotiateTx = wire.NewMsgTx() + // msg.SignedNegotiateTx.Deserialize(buf) + + return *msg, nil + +} + + +func (msg DlcContractDeclineNegotiateMsg) Bytes() []byte { + var buf bytes.Buffer + + buf.WriteByte(msg.MsgType()) + wire.WriteVarInt(&buf, 0, msg.Idx) + + // writer := bufio.NewWriter(&buf) + // msg.SignedNegotiateTx.Serialize(writer) + // writer.Flush() + return buf.Bytes() +} + + +func (msg DlcContractDeclineNegotiateMsg) Peer() uint32 { + return msg.PeerIdx +} + + +func (msg DlcContractDeclineNegotiateMsg) MsgType() uint8 { + return MSGID_DLC_DECLINENEGOTIATE +} + + + + + + diff --git a/qln/dlc.go b/qln/dlc.go index d0233b30c..db6620270 100644 --- a/qln/dlc.go +++ b/qln/dlc.go @@ -2,6 +2,10 @@ package qln import ( "fmt" + "errors" + + "encoding/hex" + "github.com/mit-dci/lit/btcutil" "github.com/mit-dci/lit/btcutil/txscript" "github.com/mit-dci/lit/btcutil/txsort" @@ -15,6 +19,8 @@ import ( "github.com/mit-dci/lit/consts" ) +var _,_ = hex.DecodeString("") + func (nd *LitNode) AddContract() (*lnutil.DlcContract, error) { c, err := nd.DlcManager.AddContract() @@ -116,8 +122,8 @@ func (nd *LitNode) OfferDlc(peerIdx uint32, cIdx uint64) error { return err } - copy(c.OurPayoutPKH[:], btcutil.Hash160(ourPayoutPKHKey[:])) - + copy(c.OurPayoutPKH[:], btcutil.Hash160(ourPayoutPKHKey[:])) + // Fund the contract err = nd.FundContract(c) if err != nil { @@ -173,7 +179,7 @@ func (nd *LitNode) AcceptDlc(cIdx uint64) error { } if c.Status != lnutil.ContractStatusOfferedToMe { - return fmt.Errorf("You cannot decline a contract unless it is in the 'Offered/Awaiting reply' state") + return fmt.Errorf("You cannot accept a contract unless it is in the 'Offered/Awaiting reply' state") } if !nd.ConnectedToPeer(c.PeerIdx) { @@ -225,6 +231,7 @@ func (nd *LitNode) AcceptDlc(cIdx uint64) error { nd.DlcManager.SaveContract(c) return } + copy(c.OurPayoutPKH[:], btcutil.Hash160(ourPayoutPKHKey[:])) wal, _ := nd.SubWallet[c.CoinType] @@ -249,9 +256,7 @@ func (nd *LitNode) AcceptDlc(cIdx uint64) error { kg.Step[2] = UseContractFundMultisig mypriv, err := wal.GetPriv(kg) - //wal, _ := nd.SubWallet[c.CoinType] - - + err = lnutil.SignRefundTx(c, refundTx, mypriv) if err != nil { logging.Errorf("Error of SignRefundTx: %s", err.Error()) @@ -554,12 +559,14 @@ func (nd *LitNode) SignSettlementDivisions(c *lnutil.DlcContract) ([]lnutil.DlcC if err != nil { return nil, err } + sig, err := nd.SignSettlementTx(c, tx, priv) if err != nil { return nil, err } returnValue[i].Outcome = d.OracleValue returnValue[i].Signature = sig + } return returnValue, nil @@ -713,14 +720,16 @@ func (nd *LitNode) SettleContract(cIdx uint64, oracleValue int64, oraclesSig[con return [32]byte{}, [32]byte{}, err } + mySig, err := nd.SignSettlementTx(c, settleTx, priv) if err != nil { logging.Errorf("SettleContract SignSettlementTx err %s", err.Error()) return [32]byte{}, [32]byte{}, err } + myBigSig := sig64.SigDecompress(mySig) - + theirSig, err := c.GetTheirSettlementSignature(oracleValue) theirBigSig := sig64.SigDecompress(theirSig) @@ -749,7 +758,6 @@ func (nd *LitNode) SettleContract(cIdx uint64, oracleValue int64, oraclesSig[con return [32]byte{}, [32]byte{}, err } - //=========================================== // Claim TX //=========================================== @@ -788,7 +796,7 @@ func (nd *LitNode) SettleContract(cIdx uint64, oracleValue int64, oraclesSig[con var pubOracleBytes [][33]byte - + privOracle0, pubOracle0 := koblitz.PrivKeyFromBytes(koblitz.S256(), oraclesSig[0][:]) privContractOutput := lnutil.CombinePrivateKeys(privSpend, privOracle0) @@ -799,6 +807,7 @@ func (nd *LitNode) SettleContract(cIdx uint64, oracleValue int64, oraclesSig[con for i:=uint32(1); i < c.OraclesNumber; i++ { privOracle, pubOracle := koblitz.PrivKeyFromBytes(koblitz.S256(), oraclesSig[i][:]) + privContractOutput = lnutil.CombinePrivateKeys(privContractOutput, privOracle) var pubOracleBytes1 [33]byte @@ -846,6 +855,11 @@ func (nd *LitNode) RefundContract(cIdx uint64) (bool, error) { return false, err } + if (c.Status != lnutil.ContractStatusActive) && (c.Status != lnutil.ContractStatusNegotiateDeclinedByHim) { + return false, errors.New("You cannot refund a contract that is not in active stage") + } + + wal, _ := nd.SubWallet[c.CoinType] refundTx, err := lnutil.RefundTx(c) @@ -867,3 +881,307 @@ func (nd *LitNode) RefundContract(cIdx uint64) (bool, error) { return true, nil } + + + + +func (nd *LitNode) DlcNegotiateContract(cIdx uint64, DesiredOracleValue int64) error { + + c, err := nd.DlcManager.LoadContract(cIdx) + if err != nil { + return err + } + + if !nd.ConnectedToPeer(c.PeerIdx) { + return fmt.Errorf("You are not connected to peer %d, do that first", c.PeerIdx) + } + + + if (c.Status != lnutil.ContractStatusActive) && (c.Status != lnutil.ContractStatusNegotiateDeclinedByHim) { + return fmt.Errorf("You cannot negotiate a contract that is not in active stage") + } + + c.DesiredOracleValue = DesiredOracleValue + + + //---------------------------------------------------------------------------- + // Create Tx + + var negotiateTx *wire.MsgTx + negotiateTx, err = lnutil.NegotiateTx(c) + + //---------------------------------------------------------------------------- + // Sign + + var kg portxo.KeyGen + kg.Depth = 5 + kg.Step[0] = 44 | 1<<31 + kg.Step[1] = c.CoinType | 1<<31 + kg.Step[2] = UseContractFundMultisig + kg.Step[3] = c.PeerIdx | 1<<31 + kg.Step[4] = uint32(c.Idx) | 1<<31 + + wal, _ := nd.SubWallet[c.CoinType] + priv, err := wal.GetPriv(kg) + + err = lnutil.SignNegotiateTx(c, negotiateTx, priv) + if err != nil { + logging.Errorf("Error of SignRefundTx: %s", err.Error()) + c.Status = lnutil.ContractStatusError + nd.DlcManager.SaveContract(c) + return err + } + + + + outMsg := lnutil.NewDlcContractNegotiateMsg(c) + + nd.tmpSendLitMsg(outMsg) + + + c.Status = lnutil.ContractStatusNegotiatingByMe + + + err = nd.DlcManager.SaveContract(c) + if err != nil { + logging.Errorf("DlcNegotiateContractHandler SaveContract err %s\n", err.Error()) + return err + } + + return nil + +} + + +// DlcNegotiateContractHandler +func (nd *LitNode) DlcNegotiateContractHandler(msg lnutil.DlcContractNegotiateMsg, peer *RemotePeer) error { + //Тут он принимает запрос. Т.е просто видит что он есть. + //Он получает DlcNegotiateMsg. Там подпись для DesiredOracleValue + + + c, err := nd.DlcManager.LoadContract(msg.Idx) + if err != nil { + logging.Errorf("DlcFundingSigsHandler FindContract err %s\n", err.Error()) + return err + } + + c.TheirnegotiateTxSig64 = msg.OurnegotiateTxSig64 + + // TODO: wire.ReadVarInt(buf, 0) returns only uint64, why? + c.DesiredOracleValue = int64(msg.DesiredOracleValue) + + //------------------------- + //Verify + + theirBigSig := sig64.SigDecompress(c.TheirnegotiateTxSig64) + + var negotiateTx *wire.MsgTx + negotiateTx, err = lnutil.NegotiateTx(c) + + hCache := txscript.NewTxSigHashes(negotiateTx) + + pre, _, err := lnutil.FundTxScript(c.OurFundMultisigPub, c.TheirFundMultisigPub) + if err != nil { + return err + } + + parsed, err := txscript.ParseScript(pre) + if err != nil { + logging.Errorf("DlcNegotiateContractHandler Sig err %s", err.Error()) + return err + } + + // always sighash all + hash := txscript.CalcWitnessSignatureHash(parsed, hCache, txscript.SigHashAll, negotiateTx, 0, c.OurFundingAmount+c.TheirFundingAmount) + + theirparsedPubKey, parsepuberr := koblitz.ParsePubKey(c.TheirFundMultisigPub[:], koblitz.S256()) + if parsepuberr != nil { + logging.Errorf("DlcFundingSigsHandler err %s\n", parsepuberr.Error()) + } + + theirparsedSig, parsesigerr := koblitz.ParseDERSignature(theirBigSig, koblitz.S256()) + if parsesigerr != nil { + logging.Errorf("DlcFundingSigsHandler err %s\n", parsesigerr.Error()) + } + + theirSigvalid := theirparsedSig.Verify(hash, theirparsedPubKey) + + fmt.Printf("DlcNegotiateContractHandler(): c.TheirnegotiateTxSig64 valid?: %t \n", theirSigvalid) + + + if theirSigvalid { + c.Status = lnutil.ContractStatusNegotiatingToMe + }else{ + return errors.New("DlcNegotiateContractHandler: c.TheirnegotiateTxSig64 is invalid.") + } + + //------------------------- + + err = nd.DlcManager.SaveContract(c) + if err != nil { + logging.Errorf("DlcNegotiateContractHandler SaveContract err %s\n", err.Error()) + return err + } + + + return nil +} + + +func (nd *LitNode) DlcAcceptNegotiate(cIdx uint64) error { + //Тут он может согласится + //Если он соглашается, то публикует транзакцию и шлет Ack + + c, err := nd.DlcManager.LoadContract(cIdx) + if err != nil { + return err + } + + if c.Status != lnutil.ContractStatusNegotiatingToMe { + return fmt.Errorf("You cannot accept negotiate a contract unless it is in the 'Negotiating to me' state") + } + + + if !nd.ConnectedToPeer(c.PeerIdx) { + return fmt.Errorf("You are not connected to peer %d, do that first", c.PeerIdx) + } + + var negotiateTx *wire.MsgTx + negotiateTx, err = lnutil.NegotiateTx(c) + + + var kg portxo.KeyGen + kg.Depth = 5 + kg.Step[0] = 44 | 1<<31 + kg.Step[1] = c.CoinType | 1<<31 + kg.Step[2] = UseContractFundMultisig + kg.Step[3] = c.PeerIdx | 1<<31 + kg.Step[4] = uint32(c.Idx) | 1<<31 + + wal, _ := nd.SubWallet[c.CoinType] + priv, err := wal.GetPriv(kg) + + err = lnutil.SignNegotiateTx(c, negotiateTx, priv) + if err != nil { + logging.Errorf("Error of SignRefundTx: %s", err.Error()) + c.Status = lnutil.ContractStatusError + nd.DlcManager.SaveContract(c) + return err + } + + pre, swap, err := lnutil.FundTxScript(c.OurFundMultisigPub, c.TheirFundMultisigPub) + + + myBigSig := sig64.SigDecompress(c.OurnegotiateTxSig64) + myBigSig = append(myBigSig, byte(txscript.SigHashAll)) + theirBigSig := sig64.SigDecompress(c.TheirnegotiateTxSig64) + theirBigSig = append(theirBigSig, byte(txscript.SigHashAll)) + + // swap if needed + if swap { + negotiateTx.TxIn[0].Witness = SpendMultiSigWitStack(pre, theirBigSig, myBigSig) + } else { + negotiateTx.TxIn[0].Witness = SpendMultiSigWitStack(pre, myBigSig, theirBigSig) + } + + err = wal.DirectSendTx(negotiateTx) + + c.Status = lnutil.ContractStatusNegotiatedToMe + + err = nd.DlcManager.SaveContract(c) + if err != nil { + logging.Errorf("DlcNegotiateContractHandler SaveContract err %s\n", err.Error()) + return err + } + + // Send ACK + outMsg := lnutil.NewDlcAcceptNegotiateMsg(c, negotiateTx) + nd.tmpSendLitMsg(outMsg) + + + return nil +} + + +func (nd *LitNode) DlcAcceptNegotiateAck(msg lnutil.DlcContractAcceptNegotiateMsg, peer *RemotePeer) error { + //Я вижу что он принял и все + + c, err := nd.DlcManager.LoadContract(msg.Idx) + if err != nil { + return err + } + + c.Status = lnutil.ContractStatusNegotiatedByMe + + err = nd.DlcManager.SaveContract(c) + if err != nil { + logging.Errorf("DlcAcceptNegotiateAck SaveContract err %s\n", err.Error()) + return err + } + + return nil + +} + + + +func (nd *LitNode) DlcDeclineNegotiate(cIdx uint64) error { + //Или отказатся + //Шлет Ack + + c, err := nd.DlcManager.LoadContract(cIdx) + if err != nil { + return err + } + + if !nd.ConnectedToPeer(c.PeerIdx) { + return fmt.Errorf("You are not connected to peer %d, do that first", c.PeerIdx) + } + + + if c.Status != lnutil.ContractStatusNegotiatingToMe { + return fmt.Errorf("You cannot decline negotiate a contract unless it is in the 'Negotiating to me' state") + } + + + + c.Status = lnutil.ContractStatusNegotiateDeclinedByMe + + err = nd.DlcManager.SaveContract(c) + if err != nil { + logging.Errorf("DlcAcceptNegotiateAck SaveContract err %s\n", err.Error()) + return err + } + + // Send ACK + outMsg := lnutil.NewDlcContractDeclineNegotiateMsg(c) + nd.tmpSendLitMsg(outMsg) + + return nil + +} + + +func (nd *LitNode) DlcDeclineNegotiateAck(msg lnutil.DlcContractDeclineNegotiateMsg, peer *RemotePeer) error { + //Я вижу что он отказался + + c, err := nd.DlcManager.LoadContract(msg.Idx) + if err != nil { + return err + } + + c.Status = lnutil.ContractStatusNegotiateDeclinedByHim + + err = nd.DlcManager.SaveContract(c) + if err != nil { + logging.Errorf("DlcDeclineNegotiateAck SaveContract err %s\n", err.Error()) + return err + } + + return nil +} + + + + + diff --git a/qln/msghandler.go b/qln/msghandler.go index 2a6b5f027..d0cbae728 100644 --- a/qln/msghandler.go +++ b/qln/msghandler.go @@ -47,6 +47,9 @@ func (nd *LitNode) registerHandlers() { mp.DefineMessage(lnutil.MSGID_DLC_CONTRACTACK, makeNeoOmniParser(lnutil.MSGID_DLC_CONTRACTACK), hf) mp.DefineMessage(lnutil.MSGID_DLC_CONTRACTFUNDINGSIGS, makeNeoOmniParser(lnutil.MSGID_DLC_CONTRACTFUNDINGSIGS), hf) mp.DefineMessage(lnutil.MSGID_DLC_SIGPROOF, makeNeoOmniParser(lnutil.MSGID_DLC_SIGPROOF), hf) + mp.DefineMessage(lnutil.MSGID_DLC_NEGOTIATE, makeNeoOmniParser(lnutil.MSGID_DLC_NEGOTIATE), hf) + mp.DefineMessage(lnutil.MSGID_DLC_ACCEPTNEGOTIATE, makeNeoOmniParser(lnutil.MSGID_DLC_ACCEPTNEGOTIATE), hf) + mp.DefineMessage(lnutil.MSGID_DLC_DECLINENEGOTIATE, makeNeoOmniParser(lnutil.MSGID_DLC_DECLINENEGOTIATE), hf) mp.DefineMessage(lnutil.MSGID_DUALFUNDINGREQ, makeNeoOmniParser(lnutil.MSGID_DUALFUNDINGREQ), hf) mp.DefineMessage(lnutil.MSGID_DUALFUNDINGACCEPT, makeNeoOmniParser(lnutil.MSGID_DUALFUNDINGACCEPT), hf) mp.DefineMessage(lnutil.MSGID_DUALFUNDINGDECL, makeNeoOmniParser(lnutil.MSGID_DUALFUNDINGDECL), hf) @@ -141,6 +144,17 @@ func (nd *LitNode) PeerHandler(msg lnutil.LitMsg, q *Qchan, peer *RemotePeer) er if msg.MsgType() == lnutil.MSGID_DLC_SIGPROOF { nd.DlcSigProofHandler(msg.(lnutil.DlcContractSigProofMsg), peer) } + if msg.MsgType() == lnutil.MSGID_DLC_NEGOTIATE { + nd.DlcNegotiateContractHandler(msg.(lnutil.DlcContractNegotiateMsg), peer) + } + + if msg.MsgType() == lnutil.MSGID_DLC_ACCEPTNEGOTIATE { + nd.DlcAcceptNegotiateAck(msg.(lnutil.DlcContractAcceptNegotiateMsg), peer) + } + + if msg.MsgType() == lnutil.MSGID_DLC_DECLINENEGOTIATE { + nd.DlcDeclineNegotiateAck(msg.(lnutil.DlcContractDeclineNegotiateMsg), peer) + } case 0xB0: // remote control if msg.MsgType() == lnutil.MSGID_REMOTE_RPCREQUEST { @@ -550,10 +564,12 @@ func (nd *LitNode) HandleContractOPEvent(c *lnutil.DlcContract, value := int64(0) myPKHPkSript := lnutil.DirectWPKHScriptFromPKH(c.OurPayoutPKH) for i, out := range opEvent.Tx.TxOut { + if bytes.Equal(myPKHPkSript, out.PkScript) { pkhIdx = uint32(i) pkhIsMine = true value = out.Value + } } @@ -592,8 +608,7 @@ func (nd *LitNode) HandleContractOPEvent(c *lnutil.DlcContract, vsize := uint32(110) fee := vsize * c.FeePerByte - txClaim.AddTxOut(wire.NewTxOut(value-int64(fee), - lnutil.DirectWPKHScriptFromPKH(addr))) + txClaim.AddTxOut(wire.NewTxOut(value-int64(fee), lnutil.DirectWPKHScriptFromPKH(addr))) var kg portxo.KeyGen kg.Depth = 5 @@ -608,8 +623,7 @@ func (nd *LitNode) HandleContractOPEvent(c *lnutil.DlcContract, hCache := txscript.NewTxSigHashes(txClaim) // generate sig - txClaim.TxIn[0].Witness, err = txscript.WitnessScript(txClaim, - hCache, 0, value, myPKHPkSript, txscript.SigHashAll, priv, true) + txClaim.TxIn[0].Witness, err = txscript.WitnessScript(txClaim, hCache, 0, value, myPKHPkSript, txscript.SigHashAll, priv, true) if err != nil { return err diff --git a/test/itest_negotiatecontract.py b/test/itest_negotiatecontract.py new file mode 100644 index 000000000..bfd66b0bd --- /dev/null +++ b/test/itest_negotiatecontract.py @@ -0,0 +1,368 @@ +import testlib + +import time, datetime +import json + +import pprint + +import requests # pip3 install requests + +import codecs + +deb_mod = False + + +def run_t(env, params): + global deb_mod + try: + + lit_funding_amt = params[0] + contract_funding_amt = params[1] + oracles_number = params[2] + oracle_value = params[3] + valueFullyOurs=params[4] + valueFullyTheirs=params[5] + + feeperbyte = params[6] + + node_to_refund = params[7] + + accept = params[8] + desiredOracleValue = params[9] + + bc = env.bitcoind + + #------------ + # Create oracles + #------------ + + oracles = [] + + for i in range(oracles_number): + env.new_oracle(1, oracle_value) # publishing interval is 1 second. + oracles.append(env.oracles[i]) + + time.sleep(2) + + #------------ + # Create lits + #------------ + + lit1 = env.lits[0] + lit2 = env.lits[1] + + + pp = pprint.PrettyPrinter(indent=4) + + + #------------------------------------------ + if deb_mod: + print("ADDRESSES BEFORE SEND TO ADDRESS") + print("LIT1 Addresses") + print(pp.pprint(lit1.rpc.GetAddresses())) + + print("LIT2 Addresses") + print(pp.pprint(lit2.rpc.GetAddresses())) + + print("bitcoind Addresses") + print(pp.pprint(bc.rpc.listaddressgroupings())) + #------------------------------------------ + + + lit1.connect_to_peer(lit2) + print("---------------") + print('Connecting lit1:', lit1.lnid, 'to lit2:', lit2.lnid) + + addr1 = lit1.make_new_addr() + txid1 = bc.rpc.sendtoaddress(addr1, lit_funding_amt) + + if deb_mod: + print("Funding TxId lit1: " + str(txid1)) + + time.sleep(2) + + addr2 = lit2.make_new_addr() + txid2 = bc.rpc.sendtoaddress(addr2, lit_funding_amt) + + if deb_mod: + print("Funding TxId lit2: " + str(txid2)) + + time.sleep(2) + + env.generate_block() + time.sleep(2) + + + #------------------------------------------ + if deb_mod: + print("ADDRESSES AFTER SEND TO ADDRESS") + print("LIT1 Addresses") + print(pp.pprint(lit1.rpc.GetAddresses())) + + print("LIT2 Addresses") + print(pp.pprint(lit2.rpc.GetAddresses())) + + print("bitcoind Addresses") + print(pp.pprint(bc.rpc.listaddressgroupings())) + #------------------------------------------ + + + print("Funding") + bals1 = lit1.get_balance_info() + print('new lit1 balance:', bals1['TxoTotal'], 'in txos,', bals1['ChanTotal'], 'in chans') + bal1sum = bals1['TxoTotal'] + bals1['ChanTotal'] + print(' = sum ', bal1sum) + + print(lit_funding_amt) + + lit_funding_amt *= 100000000 # to satoshi + + + bals2 = lit2.get_balance_info() + print('new lit2 balance:', bals2['TxoTotal'], 'in txos,', bals2['ChanTotal'], 'in chans') + bal2sum = bals2['TxoTotal'] + bals2['ChanTotal'] + print(' = sum ', bal2sum) + + + assert bal1sum == lit_funding_amt, "Funding lit1 does not works" + assert bal2sum == lit_funding_amt, "Funding lit2 does not works" + + # #------------ + # # Add oracles + # #------------ + + res = lit1.rpc.ListOracles() + assert len(res) != 0, "Initial lis of oracles must be empty" + + oracles_pubkey = [] + oidxs = [] + datasources = [] + + for oracle in oracles: + opk = json.loads(oracle.get_pubkey()) + oracles_pubkey.append(opk) + + oidx = lit1.rpc.AddOracle(Key=opk["A"], Name=opk["A"])["Oracle"]["Idx"] + oidxs.append(oidx) + lit2.rpc.AddOracle(Key=opk["A"], Name=opk["A"])["Oracle"]["Idx"] + + datasources.append(json.loads(oracle.get_datasources())) + + + # #------------ + # # Now we have to create a contract in the lit1 node. + # #------------ + + contract = lit1.rpc.NewContract() + + res = lit1.rpc.ListContracts() + assert len(res["Contracts"]) == 1, "ListContracts does not works" + + + res = lit1.rpc.GetContract(Idx=1) + assert res["Contract"]["Idx"] == 1, "GetContract does not works" + + + res = lit1.rpc.SetContractOraclesNumber(CIdx=contract["Contract"]["Idx"], OraclesNumber=oracles_number) + assert res["Success"], "SetContractOraclesNumber does not works" + + res = lit1.rpc.SetContractOracle(CIdx=contract["Contract"]["Idx"], OIdx=oidxs) + assert res["Success"], "SetContractOracle does not works" + + + # Since the oracle publishes data every 1 second (we set this time above), + # we increase the time for a point by 3 seconds. + + settlement_time = int(time.time()) + 3 + + # dlc contract settime + res = lit1.rpc.SetContractSettlementTime(CIdx=contract["Contract"]["Idx"], Time=settlement_time) + assert res["Success"], "SetContractSettlementTime does not works" + + # we set settlement_time equal to refundtime, actually the refund transaction will be valid. + res = lit1.rpc.SetContractRefundTime(CIdx=contract["Contract"]["Idx"], Time=settlement_time) + assert res["Success"], "SetContractRefundTime does not works" + + res = lit1.rpc.ListContracts() + assert res["Contracts"][contract["Contract"]["Idx"] - 1]["OracleTimestamp"] == settlement_time, "SetContractSettlementTime does not match settlement_time" + + decode_hex = codecs.getdecoder("hex_codec") + brpoints = [] + rpoints = [] + for oracle, datasource in zip(oracles, datasources): + res = oracle.get_rpoint(datasource[0]["id"], settlement_time) + print(res) + b_RPoint = decode_hex(json.loads(res)['R'])[0] + RPoint = [elem for elem in b_RPoint] + brpoints.append(RPoint) + rpoints.append(res) + + res = lit1.rpc.SetContractRPoint(CIdx=contract["Contract"]["Idx"], RPoint=brpoints) + assert res["Success"], "SetContractRpoint does not works" + + lit1.rpc.SetContractCoinType(CIdx=contract["Contract"]["Idx"], CoinType = 257) + res = lit1.rpc.GetContract(Idx=contract["Contract"]["Idx"]) + assert res["Contract"]["CoinType"] == 257, "SetContractCoinType does not works" + + + lit1.rpc.SetContractFeePerByte(CIdx=contract["Contract"]["Idx"], FeePerByte = feeperbyte) + res = lit1.rpc.GetContract(Idx=contract["Contract"]["Idx"]) + assert res["Contract"]["FeePerByte"] == feeperbyte, "SetContractFeePerByte does not works" + + ourFundingAmount = contract_funding_amt + theirFundingAmount = contract_funding_amt + + lit1.rpc.SetContractFunding(CIdx=contract["Contract"]["Idx"], OurAmount=ourFundingAmount, TheirAmount=theirFundingAmount) + res = lit1.rpc.GetContract(Idx=contract["Contract"]["Idx"]) + assert res["Contract"]["OurFundingAmount"] == ourFundingAmount, "SetContractFunding does not works" + assert res["Contract"]["TheirFundingAmount"] == theirFundingAmount, "SetContractFunding does not works" + + res = lit1.rpc.SetContractDivision(CIdx=contract["Contract"]["Idx"], ValueFullyOurs=valueFullyOurs, ValueFullyTheirs=valueFullyTheirs) + assert res["Success"], "SetContractDivision does not works" + + time.sleep(3) + + res = lit1.rpc.ListConnections() + print(res) + + res = lit1.rpc.OfferContract(CIdx=contract["Contract"]["Idx"], PeerIdx=lit1.get_peer_id(lit2)) + assert res["Success"], "OfferContract does not works" + + time.sleep(3) + + res = lit2.rpc.ContractRespond(AcceptOrDecline=True, CIdx=1) + assert res["Success"], "ContractRespond on lit2 does not works" + + time.sleep(3) + + #------------------------------------------ + + if deb_mod: + print("ADDRESSES AFTER CONTRACT RESPOND") + print("LIT1 Addresses") + print(pp.pprint(lit1.rpc.GetAddresses())) + + print("LIT2 Addresses") + print(pp.pprint(lit2.rpc.GetAddresses())) + + print("bitcoind Addresses") + print(pp.pprint(bc.rpc.listaddressgroupings())) + + + # #------------------------------------------ + + env.generate_block() + time.sleep(2) + + bals1 = lit1.get_balance_info() + print('lit1 balance after accept:', bals1['TxoTotal'], 'in txos,', bals1['ChanTotal'], 'in chans') + + bals2 = lit2.get_balance_info() + print('lit2 balance after accept:', bals2['TxoTotal'], 'in txos,', bals2['ChanTotal'], 'in chans') + + + NegotiateContractResult = lit1.rpc.DlcNegotiateContract (CIdx=contract["Contract"]["Idx"], DesiredOracleValue=desiredOracleValue) + + time.sleep(3) + + NegotiateContractRespondResult = lit2.rpc.NegotiateContractRespond(AcceptOrDecline=accept, CIdx=contract["Contract"]["Idx"]) + assert res["Success"], "NegotiateContractRespond on lit2 does not works" + + time.sleep(3) + + env.generate_block() + time.sleep(2) + + if accept: + + # desiredOracleValue = 10 + # ::lit1:: NegotiateTx(): valueours - fee: -11040 + # ::lit1:: NegotiateTx(): valueTheirs - fee: 19988960 + # lit1 balance after negotiate: 109978880 in txos, 0 in chans + # lit2 balance after negotiate: 89989920 in txos, 0 in chans + + # desiredOracleValue = 11 + # ::lit1:: NegotiateTx(): valueours - fee: 1986480 + # ::lit1:: NegotiateTx(): valueTheirs - fee: 17986480 + # lit1 balance after negotiate: 107976400 in txos, 0 in chans + # lit2 balance after negotiate: 91976400 in txos, 0 in chans + + # desiredOracleValue = 20 + # ::lit1:: NegotiateTx(): valueours - fee: 19988960 + # ::lit1:: NegotiateTx(): valueTheirs - fee: -11040 + # lit1 balance after negotiate: 89989920 in txos, 0 in chans + # lit2 balance after negotiate: 109978880 in txos, 0 in chans + + + + bals1 = lit1.get_balance_info() + print('lit1 balance after negotiate:', bals1['TxoTotal'], 'in txos,', bals1['ChanTotal'], 'in chans') + assert bals1['TxoTotal'] == 107976400, "The resulting lit1 node balance does not match." + + bals2 = lit2.get_balance_info() + print('lit2 balance after negotiate:', bals2['TxoTotal'], 'in txos,', bals2['ChanTotal'], 'in chans') + assert bals2['TxoTotal'] == 91976400, "The resulting lit1 node balance does not match." + + else: + + bals1 = lit1.get_balance_info() + print('lit1 balance after negotiate:', bals1['TxoTotal'], 'in txos,', bals1['ChanTotal'], 'in chans') + assert bals1['TxoTotal'] == 89989920, "The resulting lit1 node balance does not match." + + bals2 = lit2.get_balance_info() + print('lit2 balance after negotiate:', bals2['TxoTotal'], 'in txos,', bals2['ChanTotal'], 'in chans') + assert bals2['TxoTotal'] == 89989920, "The resulting lit1 node balance does not match." + + + except BaseException as be: + raise be + + +# ==================================================================================== +# ==================================================================================== + + + +def accept(env): + + oracles_number = 3 + oracle_value = 20 + node_to_settle = 0 + + valueFullyOurs=10 + valueFullyTheirs=20 + + lit_funding_amt = 1 # 1 BTC + contract_funding_amt = 10000000 # satoshi + + feeperbyte = 80 + + accept = True + desiredOracleValue = 11 + + params = [lit_funding_amt, contract_funding_amt, oracles_number, oracle_value, valueFullyOurs, valueFullyTheirs, feeperbyte, 0, accept, desiredOracleValue] + + run_t(env, params) + + + +def decline(env): + + oracles_number = 3 + oracle_value = 11 + node_to_settle = 0 + + valueFullyOurs=10 + valueFullyTheirs=20 + + lit_funding_amt = 1 # 1 BTC + contract_funding_amt = 10000000 # satoshi + + feeperbyte = 80 + + accept = False + desiredOracleValue = 11 + + params = [lit_funding_amt, contract_funding_amt, oracles_number, oracle_value, valueFullyOurs, valueFullyTheirs, feeperbyte, 0, accept, desiredOracleValue] + + run_t(env, params) diff --git a/test/tests.txt b/test/tests.txt index 728d73b0a..1bd3383fd 100644 --- a/test/tests.txt +++ b/test/tests.txt @@ -1,33 +1,33 @@ # Basic tests -testlib 1 -connect 2 -reconnect 0 simple -reconnect 0 reordered -reconnect 0 inbound -reconnect 0 reordered_inbound -setgetfee 1 +#testlib 1 +#connect 2 +#reconnect 0 simple +#reconnect 0 reordered +#reconnect 0 inbound +#reconnect 0 reordered_inbound +#setgetfee 1 # Tx operations -receive 1 -send 1 -send2 2 +#receive 1 +#send 1 +#send2 2 # Simple channel operations -fund 2 -close 2 forward -close 2 reverse -break 2 forward -break 2 reverse -push 2 -pushbreak 2 forward -pushbreak 2 reverse -pushclose 2 forward -pushclose 2 reverse +#fund 2 +#close 2 forward +#close 2 reverse +#break 2 forward +#break 2 reverse +#push 2 +#pushbreak 2 forward +#pushbreak 2 reverse +#pushclose 2 forward +#pushclose 2 reverse # DLC subsystem tx sizes calculation, etc... # regular contract -dlc 2 t_11_0 +#dlc 2 t_11_0 # large contract to test updated messaging subsystem # and also to test settlement from counterparty @@ -36,25 +36,28 @@ dlc 2 t_11_0 # dlc 2 t_1300_1 # test at left edge -dlc 2 t_10_0 +#dlc 2 t_10_0 # test at left edge from the counterparty -dlc 2 t_10_1 +#dlc 2 t_10_1 # test at right edge -dlc 2 t_20_0 +#dlc 2 t_20_0 # test at right edge from the counterparty -dlc 2 t_20_1 +#dlc 2 t_20_1 -dlcrefund 2 forward -dlcrefund 2 reverse +#dlcrefund 2 forward +#dlcrefund 2 reverse # If Olivia attempts to publicly report two different prices (in order to # assume the role of a counterparty in a contract and “win” the bet regardless # of the true outcome), she will reveal her permanent private key, as well as # the k value for the particular contract she attempted to double-report. -checkoraclefraud 1 +#checkoraclefraud 1 + +negotiatecontract 2 accept +negotiatecontract 2 decline