package main import ( "crypto/sha256" "encoding/json" "fmt" "io/ioutil" "math" "os" "time" ) type Offer struct { ID string `json:"id"` Date int `json:"date"` OwnerNodeAddress NodeAddress `json:"ownerNodeAddress"` Direction string `json:"direction"` Price int `json:"price"` MarketPriceMargin float64 `json:"marketPriceMargin"` UseMarketBasedPrice bool `json:"useMarketBasedPrice"` Amount int64 `json:"amount"` MinAmount int64 `json:"minAmount"` BaseCurrencyCode string `json:"baseCurrencyCode"` CounterCurrencyCode string `json:"counterCurrencyCode"` ArbitratorNodeAddresses []NodeAddress `json:"arbitratorNodeAddresses"` MediatorNodeAddress []NodeAddress `json:"mediatorNodeAddresses"` PaymentMethodId string `json:"paymentMethodId"` MakerPaymentAccountId string `json:"makerPaymentAccountId"` OfferFeePaymentTxId string `json:"offerFeePaymentTxId"` VersionNR string `json:"versionNr"` BlockHeightAtOfferCreation int `json:"blockHeightAtOfferCreation"` TxFee int `json:"txFee"` MakerFee int `json:"makerFee"` IsCurrencyForMakerFeeBtc bool `json:"isCurrencyForMakerFeeBtc"` BuyerSecurityDeposit int `json:"buyerSecurityDeposit"` SellerSecurityDeposit int `json:"sellerSecurityDeposit"` MaxTradeLimit int `json:"maxTradeLimit"` MaxTradePeriod int `json:"maxTradePeriod"` UseAutoClose bool `json:"useAutoClose"` UseReOpenAfterAutoClose bool `json:"useReOpenAfterAutoClose"` LowerClosePrice int `json:"lowerClosePrice"` UpperClosePrice int `json:"upperClosePrice"` IsPrivateOffer bool `json:"isPrivateOffer"` ExtraDataMap map[string]string `json:"extraDataMap"` ProtocolVersion int `json:"protocolVersion"` } type NodeAddress struct { HostName string `json:"hostName"` Port int `json:"port"` } type AccountPayload struct { Address string `json:"address"` PaymentMethodId string `json:"paymentMethodId"` ID string `json:"id"` MaxTradePeriod int `json:"maxTradePeriod"` } type BisqContract struct { OfferPayload Offer `json:"offerPayload"` TradeAmount int `json:"tradeAmount"` TradePrice int `json:"tradePrice"` TakerFeeTXID string `json:"takerFeeTxID"` BuyerNodeAddress NodeAddress `json:"buyerNodeAddress"` SellerNodeAddress NodeAddress `json:"sellerNodeAddress"` MediatorNodeAddress NodeAddress `json:"mediatorNodeAddress"` IsBuyerMakerAndSellerTaker bool `json:"isBuyerMakerAndSellerTaker"` MakerAccountId string `json:"makerAccountId"` TakerAccountId string `json:"takerAccountId"` MakerPaymentAccountPayload AccountPayload `json:"makerPaymentAccountPayload"` TakerPaymentAccountPayload AccountPayload `json:"takerPaymentAccountPayload"` MakerPayoutAddressString string `json:"makerPayoutAddressString"` TakerPayoutAddressString string `json:"takerPayoutAddressString"` LockTime int `json:"lockTime"` RefundAgentNodeAddress NodeAddress `json:"refundAgentNodeAddress"` } // Ideally this should be 10 minutes, but realistically... const AverageBlockTime = 9.465 const BisqLockTime = 6 * 24 * 10 // 6 blocks an hour * 24 hours a day * 10 days (for alt coin trades) func EstimateBlockHeightTime(blockHeight int) time.Time { loc, _ := time.LoadLocation("America/Vancouver") return time.Date(2009, 01, 03, 18, 15, 00, 00, loc).Add(time.Minute * time.Duration(int64(math.Round(float64(blockHeight)*AverageBlockTime)))) } func main() { contractBytes, _ := ioutil.ReadFile(os.Args[1]) contract := new(BisqContract) err := json.Unmarshal(contractBytes, contract) if err == nil { // Deal with general formatting weirdness. The canonical Bisq hash output is over the contract // when it has been formatted to separated fields by newlines, and with an indent of 2 spaces contractOut, _ := json.MarshalIndent(contract, "", " ") fmt.Printf("Successfully Validated Contract\n") fmt.Printf("Contract Offer Date: %s\n", time.Unix(int64(contract.OfferPayload.Date/1000), 0)) fmt.Printf("Contract Block Height %d (estimated) / %s (estimated)\n", contract.LockTime-BisqLockTime, EstimateBlockHeightTime(contract.LockTime-BisqLockTime)) fmt.Printf("Contract Hash: %x\n", sha256.Sum256(contractOut)) } else { fmt.Printf("Error %v", err) } }